Skip to content

Commit be7757f

Browse files
committed
♻️ Reorg Certificate
1 parent c1ed1dd commit be7757f

File tree

2 files changed

+61
-48
lines changed

2 files changed

+61
-48
lines changed

packages/agent_dart_base/lib/agent/certificate.dart

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ final AgentBLS _bls = AgentBLS();
1818
/// A certificate needs to be verified (using Certificate.prototype.verify)
1919
/// before it can be used.
2020
class UnverifiedCertificateError extends AgentFetchError {
21-
UnverifiedCertificateError();
21+
UnverifiedCertificateError([this.reason = 'Certificate is not verified.']);
22+
23+
final String reason;
2224

2325
@override
24-
String toString() => 'Cannot lookup unverified certificate. '
25-
"Try to call 'verify()' again.";
26+
String toString() => reason;
2627
}
2728

2829
/// type HashTree =
@@ -48,24 +49,26 @@ enum NodeId {
4849
}
4950

5051
class Cert {
51-
const Cert({this.tree, this.signature, this.delegation});
52+
const Cert({
53+
required this.tree,
54+
required this.signature,
55+
required this.delegation,
56+
});
5257

5358
factory Cert.fromJson(Map json) {
5459
return Cert(
60+
tree: json['tree'],
61+
signature: (json['signature'] as Uint8Buffer).buffer.asUint8List(),
5562
delegation: json['delegation'] != null
5663
? CertDelegation.fromJson(
5764
Map<String, dynamic>.from(json['delegation']),
5865
)
5966
: null,
60-
signature: json['signature'] != null
61-
? (json['signature'] as Uint8Buffer).buffer.asUint8List()
62-
: null,
63-
tree: json['tree'],
6467
);
6568
}
6669

67-
final List? tree;
68-
final Uint8List? signature;
70+
final List tree;
71+
final Uint8List signature;
6972
final CertDelegation? delegation;
7073

7174
Map<String, dynamic> toJson() {
@@ -128,33 +131,36 @@ class CertDelegation extends ReadStateResponse {
128131
}
129132

130133
class Certificate {
131-
Certificate(
132-
BinaryBlob certificate,
133-
this._agent,
134-
) : cert = Cert.fromJson(cborDecode(certificate));
134+
Certificate({
135+
required BinaryBlob cert,
136+
required this.canisterId,
137+
this.rootKey,
138+
this.maxAgeInMinutes = 5,
139+
}) : assert(maxAgeInMinutes <= 5),
140+
cert = Cert.fromJson(cborDecode(cert));
135141

136-
final Agent _agent;
137142
final Cert cert;
143+
final Principal canisterId;
144+
final BinaryBlob? rootKey;
145+
final int maxAgeInMinutes;
146+
138147
bool verified = false;
139-
BinaryBlob? _rootKey;
140148

141-
Uint8List? lookupEx(List path) {
142-
checkState();
143-
return lookupPathEx(path, cert.tree!);
149+
Uint8List? lookup(List path) {
150+
return lookupPath(path, cert.tree);
144151
}
145152

146-
Uint8List? lookup(List path) {
147-
checkState();
148-
return lookupPath(path, cert.tree!);
153+
Uint8List? lookupEx(List path) {
154+
return lookupPathEx(path, cert.tree);
149155
}
150156

151-
Future<bool> verify(Principal canisterId) async {
152-
final rootHash = await reconstruct(cert.tree!);
153-
final derKey = await _checkDelegation(cert.delegation, canisterId);
154-
final sig = cert.signature;
157+
Future<bool> verify() async {
158+
final rootHash = await reconstruct(cert.tree);
159+
final derKey = await _checkDelegation(cert.delegation);
155160
final key = extractDER(derKey);
161+
final sig = cert.signature;
156162
final msg = u8aConcat([domainSep('ic-state-root'), rootHash]);
157-
final res = await _bls.blsVerify(key, sig!, msg);
163+
final res = await _bls.blsVerify(key, sig, msg);
158164
verified = res;
159165
return res;
160166
}
@@ -165,32 +171,29 @@ class Certificate {
165171
}
166172
}
167173

168-
Future<Uint8List> _checkDelegation(
169-
CertDelegation? d,
170-
Principal canisterId,
171-
) async {
174+
Future<Uint8List> _checkDelegation(CertDelegation? d) async {
172175
if (d == null) {
173-
if (_rootKey == null) {
174-
if (_agent.rootKey != null) {
175-
_rootKey = _agent.rootKey;
176-
return Future.value(_rootKey);
177-
}
178-
throw StateError(
176+
if (rootKey == null) {
177+
throw UnverifiedCertificateError(
179178
'The rootKey is not exist. Try to call `fetchRootKey` again.',
180179
);
181180
}
182-
return Future.value(_rootKey);
181+
return Future.value(rootKey);
183182
}
184-
final Certificate cert = Certificate(d.certificate, _agent);
185-
if (!(await cert.verify(canisterId))) {
186-
throw StateError('Fail to verify certificate.');
183+
final cert = Certificate(
184+
cert: d.certificate,
185+
canisterId: canisterId,
186+
rootKey: rootKey,
187+
);
188+
if (!(await cert.verify())) {
189+
throw UnverifiedCertificateError('Fail to verify certificate.');
187190
}
188191

189192
final canisterRangesLookup = cert.lookupEx(
190193
['subnet', d.subnetId, 'canister_ranges'],
191194
);
192195
if (canisterRangesLookup == null) {
193-
throw StateError(
196+
throw UnverifiedCertificateError(
194197
'Cannot find canister ranges for subnet 0x${d.subnetId.toHex()}.',
195198
);
196199
}
@@ -200,14 +203,16 @@ class Certificate {
200203
}).toList();
201204
if (!canisterRanges
202205
.any((range) => range.$1 <= canisterId && canisterId <= range.$2)) {
203-
throw StateError('Certificate is not authorized.');
206+
throw UnverifiedCertificateError('Certificate is not authorized.');
204207
}
205208

206209
final publicKeyLookup = cert.lookupEx(
207210
['subnet', d.subnetId, 'public_key'],
208211
);
209212
if (publicKeyLookup == null) {
210-
throw StateError('Cannot find subnet key for 0x${d.subnetId.toHex()}.');
213+
throw UnverifiedCertificateError(
214+
'Cannot find subnet key for 0x${d.subnetId.toHex()}.',
215+
);
211216
}
212217
return publicKeyLookup;
213218
}

packages/agent_dart_base/lib/agent/polling/polling.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,26 @@ Future<BinaryBlob> pollForResponse(
2727
final path = [blobFromText('request_status'), requestId];
2828
final Certificate cert;
2929
if (overrideCertificate != null) {
30-
cert = Certificate(overrideCertificate, agent);
30+
cert = Certificate(
31+
cert: overrideCertificate,
32+
canisterId: canisterId,
33+
rootKey: agent.rootKey,
34+
);
3135
} else {
3236
final state = await agent.readState(
3337
canisterId,
3438
ReadStateOptions(paths: [path]),
3539
null,
3640
);
37-
cert = Certificate(state.certificate, agent);
41+
cert = Certificate(
42+
cert: state.certificate,
43+
canisterId: canisterId,
44+
rootKey: agent.rootKey,
45+
);
3846
}
39-
final verified = await cert.verify(canisterId);
47+
final verified = await cert.verify();
4048
if (!verified) {
41-
throw StateError('Fail to verify certificate.');
49+
throw UnverifiedCertificateError();
4250
}
4351

4452
final maybeBuf = cert.lookup([...path, blobFromText('status').buffer]);

0 commit comments

Comments
 (0)