Skip to content

Commit c6668fc

Browse files
committed
🔒️ Verify certificate time
1 parent 3a9b38c commit c6668fc

File tree

1 file changed

+35
-2
lines changed

1 file changed

+35
-2
lines changed

packages/agent_dart_base/lib/agent/certificate.dart

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import 'cbor.dart';
1212
import 'errors.dart';
1313
import 'request_id.dart';
1414
import 'types.dart';
15+
import 'utils/buffer_pipe.dart';
16+
import 'utils/leb128.dart';
1517

1618
final AgentBLS _bls = AgentBLS();
1719

@@ -136,13 +138,13 @@ class Certificate {
136138
required this.canisterId,
137139
this.rootKey,
138140
this.maxAgeInMinutes = 5,
139-
}) : assert(maxAgeInMinutes <= 5),
141+
}) : assert(maxAgeInMinutes == null || maxAgeInMinutes <= 5),
140142
cert = Cert.fromJson(cborDecode(cert));
141143

142144
final Cert cert;
143145
final Principal canisterId;
144146
final BinaryBlob? rootKey;
145-
final int maxAgeInMinutes;
147+
final int? maxAgeInMinutes;
146148

147149
bool verified = false;
148150

@@ -155,6 +157,7 @@ class Certificate {
155157
}
156158

157159
Future<bool> verify() async {
160+
_verifyCertTime();
158161
final rootHash = await reconstruct(cert.tree);
159162
final derKey = await _checkDelegation(cert.delegation);
160163
final key = extractDER(derKey);
@@ -171,6 +174,35 @@ class Certificate {
171174
}
172175
}
173176

177+
void _verifyCertTime() {
178+
final timeLookup = lookupEx(['time']);
179+
if (timeLookup == null) {
180+
throw UnverifiedCertificateError('Certificate does not contain a time.');
181+
}
182+
final now = DateTime.now();
183+
final lebDecodedTime = lebDecode(BufferPipe(timeLookup));
184+
final time = DateTime.fromMicrosecondsSinceEpoch(
185+
(lebDecodedTime / BigInt.from(1000)).toInt(),
186+
);
187+
// Signed time is after 5 minutes from now.
188+
if (time.isAfter(now.add(const Duration(minutes: 5)))) {
189+
throw UnverifiedCertificateError(
190+
'Certificate is signed more than 5 minutes in the future.\n'
191+
'|-- Certificate time: ${time.toIso8601String()}\n'
192+
'|-- Current time: ${now.toIso8601String()}',
193+
);
194+
}
195+
// Signed time is before [maxAgeInMinutes] minutes.
196+
if (maxAgeInMinutes != null &&
197+
time.isBefore(now.subtract(Duration(minutes: maxAgeInMinutes!)))) {
198+
throw UnverifiedCertificateError(
199+
'Certificate is signed more than $maxAgeInMinutes minutes in the past.\n'
200+
'|-- Certificate time: ${time.toIso8601String()}\n'
201+
'|-- Current time: ${now.toIso8601String()}',
202+
);
203+
}
204+
}
205+
174206
Future<Uint8List> _checkDelegation(CertDelegation? d) async {
175207
if (d == null) {
176208
if (rootKey == null) {
@@ -184,6 +216,7 @@ class Certificate {
184216
cert: d.certificate,
185217
canisterId: canisterId,
186218
rootKey: rootKey,
219+
maxAgeInMinutes: null, // Do not check max age for delegation certificates
187220
);
188221
if (!(await cert.verify())) {
189222
throw UnverifiedCertificateError('Fail to verify certificate.');

0 commit comments

Comments
 (0)