@icp-sdk/vetkeys 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/encrypted_maps.es.js +60 -89
- package/dist/lib/index.es.js +7 -7
- package/dist/lib/key_manager.es.js +7 -6
- package/dist/lib/utils-CiynM__c.mjs +901 -0
- package/package.json +21 -18
- package/dist/lib/actor-CrGCNm9W.mjs +0 -5719
- package/dist/lib/index-BUXhtQhx.mjs +0 -3932
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
import { bls12_381 as i } from "@noble/curves/bls12-381";
|
|
2
|
+
import { shake256 as Z } from "@noble/hashes/sha3";
|
|
3
|
+
import { hkdf as $ } from "@noble/hashes/hkdf";
|
|
4
|
+
import { sha256 as tt } from "@noble/hashes/sha2";
|
|
5
|
+
const l = 48, a = 96;
|
|
6
|
+
class x {
|
|
7
|
+
#t;
|
|
8
|
+
#e;
|
|
9
|
+
/**
|
|
10
|
+
* Create a random transport secret key
|
|
11
|
+
*/
|
|
12
|
+
static random() {
|
|
13
|
+
return new x(i.utils.randomSecretKey());
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Deserialize TransportSecretKey from a bytestring
|
|
17
|
+
*
|
|
18
|
+
* The passed value would typically be a string previously returned
|
|
19
|
+
* by calling serialize on a randomly-created TransportSecretKey.
|
|
20
|
+
*/
|
|
21
|
+
static deserialize(t) {
|
|
22
|
+
if (t.length !== 32)
|
|
23
|
+
throw new Error("Invalid size for transport secret key");
|
|
24
|
+
return new x(t);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Return the encoding of the transport public key; this value is
|
|
28
|
+
* sent to the IC
|
|
29
|
+
*/
|
|
30
|
+
publicKeyBytes() {
|
|
31
|
+
return this.#e.toBytes(!0);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Return the transport secret key value
|
|
35
|
+
*
|
|
36
|
+
* Applications would not normally need to call this
|
|
37
|
+
*/
|
|
38
|
+
serialize() {
|
|
39
|
+
return this.#t;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @internal constructor
|
|
43
|
+
*/
|
|
44
|
+
constructor(t) {
|
|
45
|
+
this.#t = t;
|
|
46
|
+
const e = i.G1.Point.BASE.multiply(
|
|
47
|
+
i.G1.Point.Fn.fromBytes(this.#t)
|
|
48
|
+
);
|
|
49
|
+
this.#e = e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function lt(n) {
|
|
53
|
+
if (n.length != 48)
|
|
54
|
+
return !1;
|
|
55
|
+
try {
|
|
56
|
+
return i.G1.Point.fromHex(n), !0;
|
|
57
|
+
} catch {
|
|
58
|
+
return !1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function y(n) {
|
|
62
|
+
let t = n.length;
|
|
63
|
+
const e = new Uint8Array(8 + t);
|
|
64
|
+
for (let r = 7; r >= 0; r--)
|
|
65
|
+
e[r] = t & 255, t >>>= 8;
|
|
66
|
+
return e.set(n, 8), e;
|
|
67
|
+
}
|
|
68
|
+
var et = /* @__PURE__ */ ((n) => (n.KEY_1 = "key_1", n.TEST_KEY_1 = "test_key_1", n))(et || {}), rt = /* @__PURE__ */ ((n) => (n.KEY_1 = "key_1", n.TEST_KEY_1 = "test_key_1", n.DFX_TEST_KEY = "dfx_test_key", n))(rt || {});
|
|
69
|
+
function b(n) {
|
|
70
|
+
const t = new Uint8Array(n.length / 2);
|
|
71
|
+
for (let e = 0; e < n.length; e += 2)
|
|
72
|
+
t[e / 2] = parseInt(n.substring(e, e + 2), 16);
|
|
73
|
+
return t;
|
|
74
|
+
}
|
|
75
|
+
class d {
|
|
76
|
+
#t;
|
|
77
|
+
/**
|
|
78
|
+
* Read a MasterPublicKey from the bytestring encoding
|
|
79
|
+
*
|
|
80
|
+
* Normally the bytes provided here will have been returned by
|
|
81
|
+
* the `vetkd_public_key` management canister interface.
|
|
82
|
+
*/
|
|
83
|
+
static deserialize(t) {
|
|
84
|
+
return new d(i.G2.Point.fromHex(t));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Derive a canister master key from the subnet master key
|
|
88
|
+
*
|
|
89
|
+
* To create the derived public key in VetKD, a two step derivation is performed. The first step
|
|
90
|
+
* creates a key that is specific to the canister that is making VetKD requests to the
|
|
91
|
+
* management canister, sometimes called canister master key.
|
|
92
|
+
*
|
|
93
|
+
* This function can be used to compute canister master keys knowing just the subnet master key
|
|
94
|
+
* plus the canister identity. This avoids having to interact with the IC for performing this
|
|
95
|
+
* computation.
|
|
96
|
+
*/
|
|
97
|
+
deriveCanisterKey(t) {
|
|
98
|
+
const e = "ic-vetkd-bls12-381-g2-canister-id", r = this.publicKeyBytes(), s = new Uint8Array([
|
|
99
|
+
...y(r),
|
|
100
|
+
...y(t)
|
|
101
|
+
]), o = P(s, e), c = i.G2.Point.BASE.multiply(o);
|
|
102
|
+
return new m(this.#t.add(c));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Return the bytestring encoding of the master public key
|
|
106
|
+
*/
|
|
107
|
+
publicKeyBytes() {
|
|
108
|
+
return this.#t.toBytes(!0);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Return the hardcoded master public key used on IC
|
|
112
|
+
*
|
|
113
|
+
* This allows performing public key derivation offline
|
|
114
|
+
*/
|
|
115
|
+
static productionKey(t = "key_1") {
|
|
116
|
+
if (t == "key_1")
|
|
117
|
+
return d.deserialize(
|
|
118
|
+
b(
|
|
119
|
+
"a9caf9ae8af0c7c7272f8a122133e2e0c7c0899b75e502bda9e109ca8193ded3ef042ed96db1125e1bdaad77d8cc60d917e122fe2501c45b96274f43705edf0cfd455bc66c3c060faa2fcd15486e76351edf91fecb993797273bbc8beaa47404"
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
if (t == "test_key_1")
|
|
123
|
+
return d.deserialize(
|
|
124
|
+
b(
|
|
125
|
+
"ad86e8ff845912f022a0838a502d763fdea547c9948f8cb20ea7738dd52c1c38dcb4c6ca9ac29f9ac690fc5ad7681cb41922b8dffbd65d94bff141f5fb5b6624eccc03bf850f222052df888cf9b1e47203556d7522271cbb879b2ef4b8c2bfb1"
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
throw new Error(
|
|
129
|
+
"Unknown MasterPublicKeyId value for productionKey"
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Return the hardcoded master public key used in PocketIC
|
|
134
|
+
*
|
|
135
|
+
* This allows performing public key derivation offline
|
|
136
|
+
*/
|
|
137
|
+
static pocketicKey(t = "key_1") {
|
|
138
|
+
if (t == "key_1")
|
|
139
|
+
return d.deserialize(
|
|
140
|
+
b(
|
|
141
|
+
"8c800b5cff00463d26e8167369168827f1e48f4d8d60f71dd6a295580f65275b5f5f8e6a792c876b2c72492136530d0710a27522ee63977a76216c3cef9e70bfcb45b88736fc62142e7e0737848ce06cbb1f45a4a6a349b142ae5cf7853561e0"
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
if (t == "test_key_1")
|
|
145
|
+
return d.deserialize(
|
|
146
|
+
b(
|
|
147
|
+
"9069b82c7aae418cef27678291e7f2cb1a008a500eceba7199bffca12421b07c158987c6a22618af3d1958738b2835691028801f7663d311799733286c557c8979184bb62cb559a4d582fca7d2e48b860f08ed6641aef66a059ec891889a6218"
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
if (t == "dfx_test_key")
|
|
151
|
+
return d.deserialize(
|
|
152
|
+
b(
|
|
153
|
+
"b181c14cf9d04ba45d782c0067a44b0aaa9fc2acf94f1a875f0dae801af4f80339a7e6bf8b09fcf993824c8df3080b3f1409b688ca08cbd44d2cb28db9899f4aa3b5f06b9174240448e10be2f01f9f80079ea5431ce2d11d1c8d1c775333315f"
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Unknown PocketIcMasterPublicKeyId value for pocketicKey"
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* @internal constructor
|
|
162
|
+
*/
|
|
163
|
+
constructor(t) {
|
|
164
|
+
this.#t = t;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
class m {
|
|
168
|
+
#t;
|
|
169
|
+
/**
|
|
170
|
+
* Read a DerivedPublicKey from the bytestring encoding
|
|
171
|
+
*
|
|
172
|
+
* Normally the bytes provided here will have been returned by
|
|
173
|
+
* the `vetkd_public_key` management canister interface.
|
|
174
|
+
*/
|
|
175
|
+
static deserialize(t) {
|
|
176
|
+
return new m(i.G2.Point.fromHex(t));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Perform second-stage derivation of a public key
|
|
180
|
+
*
|
|
181
|
+
* To create the derived public key in VetKD, a two step derivation is performed. The first step
|
|
182
|
+
* creates a key that is specific to the canister that is making VetKD requests to the
|
|
183
|
+
* management canister, sometimes called canister master key. The second step incorporates the
|
|
184
|
+
* "derivation context" value provided to the `vetkd_public_key` management canister interface.
|
|
185
|
+
*
|
|
186
|
+
* If `vetkd_public_key` is invoked with an empty derivation context, it simply returns the
|
|
187
|
+
* canister master key. Then the second derivation step can be done offline, using this
|
|
188
|
+
* function. This is useful if you wish to derive multiple keys without having to interact with
|
|
189
|
+
* the IC each time.
|
|
190
|
+
*
|
|
191
|
+
* If `context` is empty, then this simply returns the underlying key. This matches the behavior
|
|
192
|
+
* of `vetkd_public_key`
|
|
193
|
+
*/
|
|
194
|
+
deriveSubKey(t) {
|
|
195
|
+
if (t.length === 0)
|
|
196
|
+
return this;
|
|
197
|
+
{
|
|
198
|
+
const e = "ic-vetkd-bls12-381-g2-context", r = this.publicKeyBytes(), s = new Uint8Array([
|
|
199
|
+
...y(r),
|
|
200
|
+
...y(t)
|
|
201
|
+
]), o = P(s, e), c = i.G2.Point.BASE.multiply(o);
|
|
202
|
+
return new m(this.getPoint().add(c));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Return the bytestring encoding of the derived public key
|
|
207
|
+
*
|
|
208
|
+
* Applications would not normally need to call this, unless they
|
|
209
|
+
* are using VetKD for creating a random beacon, in which case
|
|
210
|
+
* these bytes are used by anyone verifying the beacon.
|
|
211
|
+
*/
|
|
212
|
+
publicKeyBytes() {
|
|
213
|
+
return this.#t.toBytes(!0);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* @internal getter returning the point element of the derived public key
|
|
217
|
+
*
|
|
218
|
+
* Applications would not normally need to call this
|
|
219
|
+
*/
|
|
220
|
+
getPoint() {
|
|
221
|
+
return this.#t;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* @internal constructor
|
|
225
|
+
*
|
|
226
|
+
* This is public for typing reasons but there should be no need
|
|
227
|
+
* for an application to call this.
|
|
228
|
+
*/
|
|
229
|
+
constructor(t) {
|
|
230
|
+
this.#t = t;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function P(n, t) {
|
|
234
|
+
return i.G2.hashToScalar(n, { DST: t });
|
|
235
|
+
}
|
|
236
|
+
function U(n) {
|
|
237
|
+
if (typeof n == "string")
|
|
238
|
+
return new TextEncoder().encode(n);
|
|
239
|
+
const t = new Uint8Array(n.length);
|
|
240
|
+
return t.set(n), t;
|
|
241
|
+
}
|
|
242
|
+
function T(n, t) {
|
|
243
|
+
const e = new TextEncoder().encode(n), r = U(t), s = new Uint8Array(e.length + r.length);
|
|
244
|
+
return s.set(e, 0), s.set(r, e.length), s;
|
|
245
|
+
}
|
|
246
|
+
function A(n, t, e) {
|
|
247
|
+
const r = new Uint8Array();
|
|
248
|
+
return $(tt, n, r, t, e);
|
|
249
|
+
}
|
|
250
|
+
function nt(n, t) {
|
|
251
|
+
const e = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_", r = n.publicKeyBytes(), s = new Uint8Array([...r, ...t]);
|
|
252
|
+
return i.G1.Point.fromAffine(
|
|
253
|
+
i.G1.hashToCurve(s, {
|
|
254
|
+
DST: e
|
|
255
|
+
}).toAffine()
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
function O(n, t, e) {
|
|
259
|
+
const r = n.publicKeyBytes(), s = new Uint8Array([...r, ...t]), c = i.shortSignatures.hash(
|
|
260
|
+
s,
|
|
261
|
+
"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_"
|
|
262
|
+
);
|
|
263
|
+
return i.shortSignatures.verify(
|
|
264
|
+
e,
|
|
265
|
+
c,
|
|
266
|
+
n.getPoint()
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
class _ {
|
|
270
|
+
#t;
|
|
271
|
+
#e;
|
|
272
|
+
/**
|
|
273
|
+
* Return the VetKey bytes, aka the BLS signature
|
|
274
|
+
*
|
|
275
|
+
* Use the raw bytes only if your design makes use of the fact that VetKeys
|
|
276
|
+
* are BLS signatures (eg for random beacon or threshold BLS signature
|
|
277
|
+
* generation). If you are using VetKD for key distribution, instead use
|
|
278
|
+
* deriveSymmetricKey or asHkdfCryptoKey
|
|
279
|
+
*/
|
|
280
|
+
signatureBytes() {
|
|
281
|
+
return this.#e;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Return the serialization of the VetKey
|
|
285
|
+
*
|
|
286
|
+
* This is the byte encoding of the unencrypted VetKey.
|
|
287
|
+
*/
|
|
288
|
+
serialize() {
|
|
289
|
+
return this.#e;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Derive a symmetric key of the requested length from the VetKey
|
|
293
|
+
*
|
|
294
|
+
* As an alternative to this function consider using asDerivedKeyMaterial,
|
|
295
|
+
* which uses the WebCrypto API and prevents export of the underlying key.
|
|
296
|
+
*
|
|
297
|
+
* The `domainSep` parameter should be a string unique to your application and
|
|
298
|
+
* also your usage of the resulting key. For example say your application
|
|
299
|
+
* "my-app" is deriving two keys, one for usage "foo" and the other for
|
|
300
|
+
* "bar". You might use as domain separators "my-app-foo" and "my-app-bar".
|
|
301
|
+
*
|
|
302
|
+
* The returned Uint8Array will be `outputLength` bytes long.
|
|
303
|
+
*/
|
|
304
|
+
deriveSymmetricKey(t, e) {
|
|
305
|
+
return A(this.#e, t, e);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Return a DerivedKeyMaterial type which is suitable for further key derivation
|
|
309
|
+
*/
|
|
310
|
+
async asDerivedKeyMaterial() {
|
|
311
|
+
return v.setup(this.#e);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Deserialize a VetKey from the 48 byte encoding of the BLS signature
|
|
315
|
+
*
|
|
316
|
+
* This deserializes the same value as returned by serialize (or signatureBytes)
|
|
317
|
+
*/
|
|
318
|
+
static deserialize(t) {
|
|
319
|
+
return new _(i.G1.Point.fromHex(t));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* @internal getter returning the point object of the VetKey
|
|
323
|
+
*
|
|
324
|
+
* Applications would not usually need to call this
|
|
325
|
+
*/
|
|
326
|
+
getPoint() {
|
|
327
|
+
return this.#t;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* @internal constructor
|
|
331
|
+
*
|
|
332
|
+
* This is public for typing reasons but there is no reason for an application
|
|
333
|
+
* to call this constructor.
|
|
334
|
+
*/
|
|
335
|
+
constructor(t) {
|
|
336
|
+
this.#t = t, this.#e = new Uint8Array(t.toBytes(!0));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const E = 12, R = 2, D = "IC GCMv2", C = new TextEncoder().encode(
|
|
340
|
+
D
|
|
341
|
+
), g = 8;
|
|
342
|
+
class v {
|
|
343
|
+
#t;
|
|
344
|
+
#e;
|
|
345
|
+
/**
|
|
346
|
+
* @internal constructor
|
|
347
|
+
*/
|
|
348
|
+
constructor(t, e) {
|
|
349
|
+
this.#t = t, this.#e = e;
|
|
350
|
+
}
|
|
351
|
+
static async fromCryptoKey(t) {
|
|
352
|
+
const e = {
|
|
353
|
+
name: "HKDF",
|
|
354
|
+
hash: "SHA-256",
|
|
355
|
+
info: new TextEncoder().encode(
|
|
356
|
+
"ic-vetkd-bls12-381-g2-derived-key-material"
|
|
357
|
+
),
|
|
358
|
+
salt: new Uint8Array()
|
|
359
|
+
}, r = {
|
|
360
|
+
name: "AES-GCM",
|
|
361
|
+
length: 256
|
|
362
|
+
}, s = await crypto.subtle.deriveKey(
|
|
363
|
+
e,
|
|
364
|
+
t,
|
|
365
|
+
r,
|
|
366
|
+
!0,
|
|
367
|
+
// exportable
|
|
368
|
+
["encrypt"]
|
|
369
|
+
), o = await crypto.subtle.exportKey(
|
|
370
|
+
"raw",
|
|
371
|
+
s
|
|
372
|
+
), c = await crypto.subtle.importKey(
|
|
373
|
+
"raw",
|
|
374
|
+
o,
|
|
375
|
+
{ name: "HKDF" },
|
|
376
|
+
!1,
|
|
377
|
+
["deriveKey", "deriveBits"]
|
|
378
|
+
);
|
|
379
|
+
return new v(c, t);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* @internal constructor
|
|
383
|
+
*/
|
|
384
|
+
static async setup(t) {
|
|
385
|
+
const r = await globalThis.crypto.subtle.importKey(
|
|
386
|
+
"raw",
|
|
387
|
+
t,
|
|
388
|
+
"HKDF",
|
|
389
|
+
!1,
|
|
390
|
+
["deriveKey", "deriveBits"]
|
|
391
|
+
);
|
|
392
|
+
return v.fromCryptoKey(r);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Return the CryptoKey
|
|
396
|
+
*/
|
|
397
|
+
getCryptoKey() {
|
|
398
|
+
return this.#e;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Return a WebCrypto CryptoKey handle suitable for AES-GCM encryption/decryption
|
|
402
|
+
*
|
|
403
|
+
* The key is derived using HKDF with the provided domain separator
|
|
404
|
+
*
|
|
405
|
+
* The CryptoKey is not exportable
|
|
406
|
+
*/
|
|
407
|
+
async deriveAesGcmCryptoKey(t, e) {
|
|
408
|
+
const r = {
|
|
409
|
+
name: "HKDF",
|
|
410
|
+
hash: "SHA-256",
|
|
411
|
+
length: 256,
|
|
412
|
+
info: T(
|
|
413
|
+
"ic-vetkd-bls12-381-g2-aes-gcm-v" + e.toString() + "-",
|
|
414
|
+
t
|
|
415
|
+
),
|
|
416
|
+
salt: new Uint8Array()
|
|
417
|
+
}, s = {
|
|
418
|
+
name: "AES-GCM",
|
|
419
|
+
length: 256
|
|
420
|
+
};
|
|
421
|
+
return globalThis.crypto.subtle.deriveKey(
|
|
422
|
+
r,
|
|
423
|
+
this.#t,
|
|
424
|
+
s,
|
|
425
|
+
!1,
|
|
426
|
+
["encrypt", "decrypt"]
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Encrypt the provided message using AES-GCM and a key derived using HKDF
|
|
431
|
+
*
|
|
432
|
+
* The GCM key is derived using HKDF with the provided domain separator
|
|
433
|
+
*/
|
|
434
|
+
async encryptMessage(t, e, r) {
|
|
435
|
+
const s = await this.deriveAesGcmCryptoKey(
|
|
436
|
+
e,
|
|
437
|
+
R
|
|
438
|
+
), o = globalThis.crypto.getRandomValues(
|
|
439
|
+
new Uint8Array(E)
|
|
440
|
+
), c = T(D, r), u = new Uint8Array(
|
|
441
|
+
await globalThis.crypto.subtle.encrypt(
|
|
442
|
+
{ name: "AES-GCM", iv: o, additionalData: c },
|
|
443
|
+
s,
|
|
444
|
+
U(t)
|
|
445
|
+
)
|
|
446
|
+
);
|
|
447
|
+
return new Uint8Array([
|
|
448
|
+
...C,
|
|
449
|
+
...o,
|
|
450
|
+
...u
|
|
451
|
+
]);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Decrypt the provided ciphertext using AES-GCM and a key derived using HKDF
|
|
455
|
+
*
|
|
456
|
+
* The GCM key is derived using HKDF with the provided domain separator
|
|
457
|
+
*/
|
|
458
|
+
async decryptMessage(t, e, r) {
|
|
459
|
+
const o = g + E + 16;
|
|
460
|
+
if (t.length < o)
|
|
461
|
+
throw new Error(
|
|
462
|
+
"Invalid ciphertext, too short to possibly be valid"
|
|
463
|
+
);
|
|
464
|
+
const c = t.slice(0, g);
|
|
465
|
+
if (!z(c, C))
|
|
466
|
+
if (r.length == 0) {
|
|
467
|
+
const w = t.slice(
|
|
468
|
+
0,
|
|
469
|
+
E
|
|
470
|
+
), X = t.slice(
|
|
471
|
+
E
|
|
472
|
+
), q = {
|
|
473
|
+
name: "HKDF",
|
|
474
|
+
hash: "SHA-256",
|
|
475
|
+
length: 256,
|
|
476
|
+
info: U(e),
|
|
477
|
+
salt: new Uint8Array()
|
|
478
|
+
}, j = {
|
|
479
|
+
name: "AES-GCM",
|
|
480
|
+
length: 256
|
|
481
|
+
}, J = await globalThis.crypto.subtle.deriveKey(
|
|
482
|
+
q,
|
|
483
|
+
this.#e,
|
|
484
|
+
j,
|
|
485
|
+
!1,
|
|
486
|
+
["decrypt"]
|
|
487
|
+
);
|
|
488
|
+
try {
|
|
489
|
+
const Q = await globalThis.crypto.subtle.decrypt(
|
|
490
|
+
{ name: "AES-GCM", iv: w },
|
|
491
|
+
J,
|
|
492
|
+
X
|
|
493
|
+
);
|
|
494
|
+
return new Uint8Array(Q);
|
|
495
|
+
} catch {
|
|
496
|
+
throw new Error("Decryption failed");
|
|
497
|
+
}
|
|
498
|
+
} else
|
|
499
|
+
throw new Error(
|
|
500
|
+
"Unknown header for AES-GCM encrypted ciphertext"
|
|
501
|
+
);
|
|
502
|
+
const u = T(D, r), p = t.slice(
|
|
503
|
+
g,
|
|
504
|
+
g + E
|
|
505
|
+
), G = t.slice(
|
|
506
|
+
g + E
|
|
507
|
+
), K = await this.deriveAesGcmCryptoKey(
|
|
508
|
+
e,
|
|
509
|
+
R
|
|
510
|
+
);
|
|
511
|
+
try {
|
|
512
|
+
const w = await globalThis.crypto.subtle.decrypt(
|
|
513
|
+
{ name: "AES-GCM", iv: p, additionalData: u },
|
|
514
|
+
K,
|
|
515
|
+
G
|
|
516
|
+
);
|
|
517
|
+
return new Uint8Array(w);
|
|
518
|
+
} catch {
|
|
519
|
+
throw new Error("Decryption failed");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
class N {
|
|
524
|
+
#t;
|
|
525
|
+
#e;
|
|
526
|
+
#r;
|
|
527
|
+
/**
|
|
528
|
+
* Parse an encrypted key returned by the `vetkd_derive_encrypted_key`
|
|
529
|
+
* managment canister interface
|
|
530
|
+
*/
|
|
531
|
+
static deserialize(t) {
|
|
532
|
+
if (t.length !== l + a + l)
|
|
533
|
+
throw new Error("Invalid EncryptedVetKey serialization");
|
|
534
|
+
const e = i.G1.Point.fromHex(t.subarray(0, l)), r = i.G2.Point.fromHex(
|
|
535
|
+
t.subarray(l, l + a)
|
|
536
|
+
), s = i.G1.Point.fromHex(
|
|
537
|
+
t.subarray(l + a)
|
|
538
|
+
);
|
|
539
|
+
return new N(e, r, s);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Decrypt the encrypted key returning a VetKey
|
|
543
|
+
*/
|
|
544
|
+
decryptAndVerify(t, e, r) {
|
|
545
|
+
const s = i.G1.Point.BASE, o = i.G2.Point.BASE.negate(), c = i.fields.Fp12.ONE, u = i.pairingBatch([
|
|
546
|
+
{ g1: this.#t, g2: o },
|
|
547
|
+
{ g1: s, g2: this.#e }
|
|
548
|
+
]);
|
|
549
|
+
if (!i.fields.Fp12.eql(u, c))
|
|
550
|
+
throw new Error("Invalid VetKey");
|
|
551
|
+
const p = this.#r.subtract(
|
|
552
|
+
this.#t.multiply(i.G1.Point.Fn.fromBytes(t.serialize()))
|
|
553
|
+
);
|
|
554
|
+
if (O(e, r, p))
|
|
555
|
+
return new _(p);
|
|
556
|
+
throw new Error("Invalid VetKey");
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* @internal constructor
|
|
560
|
+
*/
|
|
561
|
+
constructor(t, e, r) {
|
|
562
|
+
this.#t = t, this.#e = e, this.#r = r;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const V = new Uint8Array([
|
|
566
|
+
73,
|
|
567
|
+
67,
|
|
568
|
+
32,
|
|
569
|
+
73,
|
|
570
|
+
66,
|
|
571
|
+
69,
|
|
572
|
+
0,
|
|
573
|
+
1
|
|
574
|
+
]), f = 8;
|
|
575
|
+
function M(n, t, e) {
|
|
576
|
+
const r = new Uint8Array([...n, ...t, ...e]);
|
|
577
|
+
return P(
|
|
578
|
+
r,
|
|
579
|
+
"ic-vetkd-bls12-381-ibe-hash-to-mask"
|
|
580
|
+
/* HashToMask */
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
function W(n, t) {
|
|
584
|
+
if (n.length !== t.length)
|
|
585
|
+
throw new Error("xorBuf arguments should have the same length");
|
|
586
|
+
const e = new Uint8Array(n.length);
|
|
587
|
+
for (let r = 0; r < n.length; r++)
|
|
588
|
+
e[r] = n[r] ^ t[r];
|
|
589
|
+
return e;
|
|
590
|
+
}
|
|
591
|
+
function L(n, t) {
|
|
592
|
+
if (t.length !== 576)
|
|
593
|
+
throw new Error("Unexpected size for Gt element");
|
|
594
|
+
const e = A(
|
|
595
|
+
t,
|
|
596
|
+
"ic-vetkd-bls12-381-ibe-mask-seed",
|
|
597
|
+
n.length
|
|
598
|
+
);
|
|
599
|
+
return W(e, n);
|
|
600
|
+
}
|
|
601
|
+
function Y(n, t) {
|
|
602
|
+
const e = "ic-vetkd-bls12-381-ibe-mask-msg-".concat(
|
|
603
|
+
n.length.toString().padStart(20, "0")
|
|
604
|
+
), r = A(t, e, 32), s = Z(r, { dkLen: n.length });
|
|
605
|
+
return W(n, s);
|
|
606
|
+
}
|
|
607
|
+
function F(n) {
|
|
608
|
+
const t = i.fields.Fp12.toBytes(n), e = new Uint8Array(576), r = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
|
609
|
+
for (let s = 0; s < 12; ++s)
|
|
610
|
+
for (let o = 0; o < 48; ++o)
|
|
611
|
+
e[48 * s + o] = t[48 * r[s] + o];
|
|
612
|
+
return e;
|
|
613
|
+
}
|
|
614
|
+
function z(n, t) {
|
|
615
|
+
if (n.length !== t.length)
|
|
616
|
+
return !1;
|
|
617
|
+
let e = 0;
|
|
618
|
+
for (let r = 0; r < n.length; ++r)
|
|
619
|
+
e |= n[r] ^ t[r];
|
|
620
|
+
return e == 0;
|
|
621
|
+
}
|
|
622
|
+
class B {
|
|
623
|
+
#t;
|
|
624
|
+
constructor(t) {
|
|
625
|
+
this.#t = t;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Create an identity from a byte string
|
|
629
|
+
*/
|
|
630
|
+
static fromBytes(t) {
|
|
631
|
+
return new B(t);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Create an identity from a string
|
|
635
|
+
*/
|
|
636
|
+
static fromString(t) {
|
|
637
|
+
return B.fromBytes(new TextEncoder().encode(t));
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Create an identity from a Principal
|
|
641
|
+
*/
|
|
642
|
+
static fromPrincipal(t) {
|
|
643
|
+
return B.fromBytes(t.toUint8Array());
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* @internal getter returning the encoded
|
|
647
|
+
*/
|
|
648
|
+
getBytes() {
|
|
649
|
+
return this.#t;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const h = 32;
|
|
653
|
+
class S {
|
|
654
|
+
#t;
|
|
655
|
+
constructor(t) {
|
|
656
|
+
if (t.length !== h)
|
|
657
|
+
throw new Error("IBE seed must be exactly IBE_SEED_BYTES long");
|
|
658
|
+
this.#t = t;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Create a seed for IBE encryption from a byte string
|
|
662
|
+
*
|
|
663
|
+
* This input should be randomly chosen by a secure random number generator.
|
|
664
|
+
* If the seed is not securely generated the IBE scheme will be insecure.
|
|
665
|
+
*
|
|
666
|
+
* At least 128 bits (16 bytes) must be provided.
|
|
667
|
+
*
|
|
668
|
+
* If the input is exactly 256 bits it is used directly. Otherwise the input
|
|
669
|
+
* is hashed with HKDF to produce a 256 bit seed.
|
|
670
|
+
*/
|
|
671
|
+
static fromBytes(t) {
|
|
672
|
+
if (t.length < 16)
|
|
673
|
+
throw new Error(
|
|
674
|
+
"Insufficient input material for IbeSeed derivation"
|
|
675
|
+
);
|
|
676
|
+
return t.length == h ? new S(t) : new S(
|
|
677
|
+
A(
|
|
678
|
+
t,
|
|
679
|
+
"ic-vetkd-bls12-381-ibe-hash-seed",
|
|
680
|
+
h
|
|
681
|
+
)
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Create a random seed for IBE encryption
|
|
686
|
+
*/
|
|
687
|
+
static random() {
|
|
688
|
+
return new S(
|
|
689
|
+
globalThis.crypto.getRandomValues(new Uint8Array(h))
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* @internal getter returning the seed bytes
|
|
694
|
+
*/
|
|
695
|
+
getBytes() {
|
|
696
|
+
return this.#t;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
const k = f + h + a;
|
|
700
|
+
class H {
|
|
701
|
+
#t;
|
|
702
|
+
#e;
|
|
703
|
+
#r;
|
|
704
|
+
#n;
|
|
705
|
+
/**
|
|
706
|
+
* Helper function for determining the size of an IBE ciphertext in bytes.
|
|
707
|
+
*/
|
|
708
|
+
static ciphertextSize(t) {
|
|
709
|
+
if (t < 0)
|
|
710
|
+
throw new Error(
|
|
711
|
+
"IbeCiphertext.ciphertextSize argument cannot be negative"
|
|
712
|
+
);
|
|
713
|
+
return t + k;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Helper function for determining the size of an IBE plaintext in bytes.
|
|
717
|
+
*/
|
|
718
|
+
static plaintextSize(t) {
|
|
719
|
+
if (t < k)
|
|
720
|
+
throw new Error(
|
|
721
|
+
"IbeCiphertext.plaintextSize given ciphertext size is too small to be valid"
|
|
722
|
+
);
|
|
723
|
+
return t - k;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Serialize the IBE ciphertext to a bytestring
|
|
727
|
+
*/
|
|
728
|
+
serialize() {
|
|
729
|
+
const t = this.#e.toBytes(!0);
|
|
730
|
+
return new Uint8Array([
|
|
731
|
+
...this.#t,
|
|
732
|
+
...t,
|
|
733
|
+
...this.#r,
|
|
734
|
+
...this.#n
|
|
735
|
+
]);
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Deserialize an IBE ciphertext
|
|
739
|
+
*/
|
|
740
|
+
static deserialize(t) {
|
|
741
|
+
if (t.length < f + a + h)
|
|
742
|
+
throw new Error("Invalid IBE ciphertext");
|
|
743
|
+
const e = t.subarray(0, f), r = i.G2.Point.fromHex(
|
|
744
|
+
t.subarray(f, f + a)
|
|
745
|
+
), s = t.subarray(
|
|
746
|
+
f + a,
|
|
747
|
+
f + a + h
|
|
748
|
+
), o = t.subarray(f + a + h);
|
|
749
|
+
if (!z(e, V))
|
|
750
|
+
throw new Error("Unexpected header for IBE ciphertext");
|
|
751
|
+
return new H(e, r, s, o);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Encrypt a message using IBE, returning the ciphertext
|
|
755
|
+
*
|
|
756
|
+
* Any user who is able to retrieve the VetKey for the specified derived public key and
|
|
757
|
+
* identity will be able to decrypt this message.
|
|
758
|
+
*
|
|
759
|
+
* There is no fixed upper bound on the size of the message that can be encrypted using
|
|
760
|
+
* this scheme. However, internally during the encryption process several heap allocations
|
|
761
|
+
* are performed which are approximately the same length as the message itself, so
|
|
762
|
+
* encrypting or decrypting very large messages may result in memory allocation errors.
|
|
763
|
+
*
|
|
764
|
+
* If you anticipate using IBE to encrypt very large messages, consider using IBE just to
|
|
765
|
+
* encrypt a symmetric key, and then using a standard cipher such as AES-GCM to encrypt the
|
|
766
|
+
* data.
|
|
767
|
+
*
|
|
768
|
+
* The seed parameter must be a randomly generated value that was generated just for this
|
|
769
|
+
* one message. Using it for a second message, or for any other purpose, compromises the
|
|
770
|
+
* security of the IBE scheme.
|
|
771
|
+
*/
|
|
772
|
+
static encrypt(t, e, r, s) {
|
|
773
|
+
const o = V, c = M(o, s.getBytes(), r), u = nt(t, e.getBytes()), p = i.fields.Fp12.pow(
|
|
774
|
+
i.pairing(u, t.getPoint()),
|
|
775
|
+
c
|
|
776
|
+
), G = i.G2.Point.BASE.multiply(c), K = L(s.getBytes(), F(p)), w = Y(r, s.getBytes());
|
|
777
|
+
return new H(o, G, K, w);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Decrypt an IBE ciphertext, returning the message
|
|
781
|
+
*
|
|
782
|
+
* There is no fixed upper bound on the size of the message that can be encrypted using
|
|
783
|
+
* this scheme. However, internally during the encryption process several heap allocations
|
|
784
|
+
* are performed which are approximately the same length as the message itself, so
|
|
785
|
+
* encrypting or decrypting very large messages may result in memory allocation errors.
|
|
786
|
+
*/
|
|
787
|
+
decrypt(t) {
|
|
788
|
+
const e = L(
|
|
789
|
+
this.#r,
|
|
790
|
+
F(i.pairing(t.getPoint(), this.#e))
|
|
791
|
+
), r = Y(this.#n, e), s = M(this.#t, e, r);
|
|
792
|
+
if (z(
|
|
793
|
+
i.G2.Point.BASE.multiply(s).toBytes(!0),
|
|
794
|
+
this.#e.toBytes(!0)
|
|
795
|
+
))
|
|
796
|
+
return r;
|
|
797
|
+
throw new Error("Decryption failed");
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Private constructor
|
|
801
|
+
*/
|
|
802
|
+
constructor(t, e, r, s) {
|
|
803
|
+
this.#t = t, this.#e = e, this.#r = r, this.#n = s;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
const st = 32;
|
|
807
|
+
class I {
|
|
808
|
+
#t;
|
|
809
|
+
#e;
|
|
810
|
+
#r;
|
|
811
|
+
#n;
|
|
812
|
+
static computeVrfHash(t, e, r) {
|
|
813
|
+
const s = new Uint8Array([
|
|
814
|
+
...y(t.serialize()),
|
|
815
|
+
...y(e.publicKeyBytes()),
|
|
816
|
+
...y(r)
|
|
817
|
+
]);
|
|
818
|
+
return A(
|
|
819
|
+
s,
|
|
820
|
+
"ic-vetkd-bls12-381-g2-vrf",
|
|
821
|
+
st
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Serialize a VrfOutput to a byte string
|
|
826
|
+
*/
|
|
827
|
+
serialize() {
|
|
828
|
+
return new Uint8Array([
|
|
829
|
+
...this.#t.serialize(),
|
|
830
|
+
...this.#e.publicKeyBytes(),
|
|
831
|
+
...this.#r
|
|
832
|
+
]);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Deserialize and verify a VrfOutput
|
|
836
|
+
*
|
|
837
|
+
* Note this verifies the VrfOutput with respect to the derived public key
|
|
838
|
+
* and VRF input which are included in the struct. It is the responsibility
|
|
839
|
+
* of the application to examine the return value of `publicKey` and `input`
|
|
840
|
+
* and ensure these values make sense in the context where this VRF is being
|
|
841
|
+
* used.
|
|
842
|
+
*/
|
|
843
|
+
static deserialize(t) {
|
|
844
|
+
if (t.length < l + a)
|
|
845
|
+
throw new Error(
|
|
846
|
+
"VrfOutput.deserialize input too short to possibly be valid"
|
|
847
|
+
);
|
|
848
|
+
const e = _.deserialize(t.slice(0, l)), r = m.deserialize(
|
|
849
|
+
t.slice(l, l + a)
|
|
850
|
+
), s = t.slice(l + a);
|
|
851
|
+
if (!O(r, s, e.getPoint()))
|
|
852
|
+
throw new Error("VrfOutput.deserialize proof is invalid");
|
|
853
|
+
return new I(e, r, s);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Return the public key under which this VRF output was derived
|
|
857
|
+
*/
|
|
858
|
+
publicKey() {
|
|
859
|
+
return this.#e;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Return the input that was used to create this VRF output
|
|
863
|
+
*/
|
|
864
|
+
input() {
|
|
865
|
+
return this.#r;
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Return the VRF output
|
|
869
|
+
*
|
|
870
|
+
* This is a random-looking value which was provably generated by some party with
|
|
871
|
+
* access to the VRF secret key.
|
|
872
|
+
*/
|
|
873
|
+
output() {
|
|
874
|
+
return this.#n;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Private constructor
|
|
878
|
+
*/
|
|
879
|
+
constructor(t, e, r) {
|
|
880
|
+
this.#t = t, this.#e = e, this.#r = r, this.#n = I.computeVrfHash(t, e, r);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
export {
|
|
884
|
+
v as D,
|
|
885
|
+
N as E,
|
|
886
|
+
H as I,
|
|
887
|
+
d as M,
|
|
888
|
+
rt as P,
|
|
889
|
+
x as T,
|
|
890
|
+
_ as V,
|
|
891
|
+
m as a,
|
|
892
|
+
B as b,
|
|
893
|
+
S as c,
|
|
894
|
+
et as d,
|
|
895
|
+
I as e,
|
|
896
|
+
nt as f,
|
|
897
|
+
A as g,
|
|
898
|
+
P as h,
|
|
899
|
+
lt as i,
|
|
900
|
+
O as v
|
|
901
|
+
};
|