@@ -18,11 +18,12 @@ final AgentBLS _bls = AgentBLS();
18
18
/// A certificate needs to be verified (using Certificate.prototype.verify)
19
19
/// before it can be used.
20
20
class UnverifiedCertificateError extends AgentFetchError {
21
- UnverifiedCertificateError ();
21
+ UnverifiedCertificateError ([this .reason = 'Certificate is not verified.' ]);
22
+
23
+ final String reason;
22
24
23
25
@override
24
- String toString () => 'Cannot lookup unverified certificate. '
25
- "Try to call 'verify()' again." ;
26
+ String toString () => reason;
26
27
}
27
28
28
29
/// type HashTree =
@@ -48,24 +49,26 @@ enum NodeId {
48
49
}
49
50
50
51
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
+ });
52
57
53
58
factory Cert .fromJson (Map json) {
54
59
return Cert (
60
+ tree: json['tree' ],
61
+ signature: (json['signature' ] as Uint8Buffer ).buffer.asUint8List (),
55
62
delegation: json['delegation' ] != null
56
63
? CertDelegation .fromJson (
57
64
Map <String , dynamic >.from (json['delegation' ]),
58
65
)
59
66
: null ,
60
- signature: json['signature' ] != null
61
- ? (json['signature' ] as Uint8Buffer ).buffer.asUint8List ()
62
- : null ,
63
- tree: json['tree' ],
64
67
);
65
68
}
66
69
67
- final List ? tree;
68
- final Uint8List ? signature;
70
+ final List tree;
71
+ final Uint8List signature;
69
72
final CertDelegation ? delegation;
70
73
71
74
Map <String , dynamic > toJson () {
@@ -128,33 +131,36 @@ class CertDelegation extends ReadStateResponse {
128
131
}
129
132
130
133
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));
135
141
136
- final Agent _agent;
137
142
final Cert cert;
143
+ final Principal canisterId;
144
+ final BinaryBlob ? rootKey;
145
+ final int maxAgeInMinutes;
146
+
138
147
bool verified = false ;
139
- BinaryBlob ? _rootKey;
140
148
141
- Uint8List ? lookupEx (List path) {
142
- checkState ();
143
- return lookupPathEx (path, cert.tree! );
149
+ Uint8List ? lookup (List path) {
150
+ return lookupPath (path, cert.tree);
144
151
}
145
152
146
- Uint8List ? lookup (List path) {
147
- checkState ();
148
- return lookupPath (path, cert.tree! );
153
+ Uint8List ? lookupEx (List path) {
154
+ return lookupPathEx (path, cert.tree);
149
155
}
150
156
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);
155
160
final key = extractDER (derKey);
161
+ final sig = cert.signature;
156
162
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);
158
164
verified = res;
159
165
return res;
160
166
}
@@ -165,32 +171,29 @@ class Certificate {
165
171
}
166
172
}
167
173
168
- Future <Uint8List > _checkDelegation (
169
- CertDelegation ? d,
170
- Principal canisterId,
171
- ) async {
174
+ Future <Uint8List > _checkDelegation (CertDelegation ? d) async {
172
175
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 (
179
178
'The rootKey is not exist. Try to call `fetchRootKey` again.' ,
180
179
);
181
180
}
182
- return Future .value (_rootKey );
181
+ return Future .value (rootKey );
183
182
}
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.' );
187
190
}
188
191
189
192
final canisterRangesLookup = cert.lookupEx (
190
193
['subnet' , d.subnetId, 'canister_ranges' ],
191
194
);
192
195
if (canisterRangesLookup == null ) {
193
- throw StateError (
196
+ throw UnverifiedCertificateError (
194
197
'Cannot find canister ranges for subnet 0x${d .subnetId .toHex ()}.' ,
195
198
);
196
199
}
@@ -200,14 +203,16 @@ class Certificate {
200
203
}).toList ();
201
204
if (! canisterRanges
202
205
.any ((range) => range.$1 <= canisterId && canisterId <= range.$2)) {
203
- throw StateError ('Certificate is not authorized.' );
206
+ throw UnverifiedCertificateError ('Certificate is not authorized.' );
204
207
}
205
208
206
209
final publicKeyLookup = cert.lookupEx (
207
210
['subnet' , d.subnetId, 'public_key' ],
208
211
);
209
212
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
+ );
211
216
}
212
217
return publicKeyLookup;
213
218
}
0 commit comments