@mysten/seal 0.4.3 → 0.4.5
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/CHANGELOG.md +21 -0
- package/dist/cjs/client.d.ts +5 -5
- package/dist/cjs/client.js +74 -65
- package/dist/cjs/client.js.map +2 -2
- package/dist/cjs/decrypt.js +14 -21
- package/dist/cjs/decrypt.js.map +2 -2
- package/dist/cjs/dem.js +23 -21
- package/dist/cjs/dem.js.map +2 -2
- package/dist/cjs/elgamal.d.ts +1 -1
- package/dist/cjs/elgamal.js +5 -5
- package/dist/cjs/elgamal.js.map +2 -2
- package/dist/cjs/encrypt.js +20 -8
- package/dist/cjs/encrypt.js.map +2 -2
- package/dist/cjs/error.d.ts +6 -0
- package/dist/cjs/error.js +9 -0
- package/dist/cjs/error.js.map +2 -2
- package/dist/cjs/ibe.d.ts +4 -7
- package/dist/cjs/ibe.js +10 -5
- package/dist/cjs/ibe.js.map +2 -2
- package/dist/cjs/kdf.d.ts +19 -3
- package/dist/cjs/kdf.js +28 -12
- package/dist/cjs/kdf.js.map +2 -2
- package/dist/cjs/key-server.d.ts +1 -1
- package/dist/cjs/key-server.js +1 -1
- package/dist/cjs/key-server.js.map +2 -2
- package/dist/cjs/keys.js +1 -1
- package/dist/cjs/keys.js.map +2 -2
- package/dist/cjs/session-key.d.ts +5 -2
- package/dist/cjs/session-key.js +10 -8
- package/dist/cjs/session-key.js.map +2 -2
- package/dist/cjs/utils.d.ts +4 -3
- package/dist/cjs/utils.js +6 -2
- package/dist/cjs/utils.js.map +3 -3
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/esm/client.d.ts +5 -5
- package/dist/esm/client.js +79 -68
- package/dist/esm/client.js.map +2 -2
- package/dist/esm/decrypt.js +16 -23
- package/dist/esm/decrypt.js.map +2 -2
- package/dist/esm/dem.js +25 -23
- package/dist/esm/dem.js.map +2 -2
- package/dist/esm/elgamal.d.ts +1 -1
- package/dist/esm/elgamal.js +5 -5
- package/dist/esm/elgamal.js.map +2 -2
- package/dist/esm/encrypt.js +21 -9
- package/dist/esm/encrypt.js.map +2 -2
- package/dist/esm/error.d.ts +6 -0
- package/dist/esm/error.js +9 -0
- package/dist/esm/error.js.map +2 -2
- package/dist/esm/ibe.d.ts +4 -7
- package/dist/esm/ibe.js +12 -7
- package/dist/esm/ibe.js.map +2 -2
- package/dist/esm/kdf.d.ts +19 -3
- package/dist/esm/kdf.js +28 -12
- package/dist/esm/kdf.js.map +2 -2
- package/dist/esm/key-server.d.ts +1 -1
- package/dist/esm/key-server.js +2 -2
- package/dist/esm/key-server.js.map +2 -2
- package/dist/esm/keys.js +1 -1
- package/dist/esm/keys.js.map +2 -2
- package/dist/esm/session-key.d.ts +5 -2
- package/dist/esm/session-key.js +10 -8
- package/dist/esm/session-key.js.map +2 -2
- package/dist/esm/utils.d.ts +4 -3
- package/dist/esm/utils.js +6 -2
- package/dist/esm/utils.js.map +3 -3
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @mysten/seal
|
|
2
2
|
|
|
3
|
+
## 0.4.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [ec519fc]
|
|
8
|
+
- @mysten/sui@1.30.1
|
|
9
|
+
|
|
10
|
+
## 0.4.4
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- ee1bfd8: Better handling of weighted/duplicate key servers.
|
|
15
|
+
- 5264038: expose ZkLoginCompatibleClient, use ZkLoginCompatibleClient for SessionKey constructor
|
|
16
|
+
- Updated dependencies [2456052]
|
|
17
|
+
- Updated dependencies [5264038]
|
|
18
|
+
- Updated dependencies [2456052]
|
|
19
|
+
- Updated dependencies [2456052]
|
|
20
|
+
- Updated dependencies [2456052]
|
|
21
|
+
- Updated dependencies [2456052]
|
|
22
|
+
- @mysten/sui@1.30.0
|
|
23
|
+
|
|
3
24
|
## 0.4.3
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -4,14 +4,15 @@ import type { SessionKey } from './session-key.js';
|
|
|
4
4
|
import type { SealCompatibleClient } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Configuration options for initializing a SealClient
|
|
7
|
-
* @property serverObjectIds: Array of
|
|
7
|
+
* @property serverObjectIds: Array of the key servers to use.
|
|
8
|
+
* The first element is the object ID, and the second element is the weight of the key server.
|
|
8
9
|
* @property verifyKeyServers: Whether to verify the key servers' authenticity.
|
|
9
10
|
* Should be false if servers are pre-verified (e.g., getAllowlistedKeyServers).
|
|
10
11
|
* Defaults to true.
|
|
11
12
|
* @property timeout: Timeout in milliseconds for network requests. Defaults to 10 seconds.
|
|
12
13
|
*/
|
|
13
14
|
export interface SealClientExtensionOptions {
|
|
14
|
-
serverObjectIds: string[];
|
|
15
|
+
serverObjectIds: [string, number][];
|
|
15
16
|
verifyKeyServers?: boolean;
|
|
16
17
|
timeout?: number;
|
|
17
18
|
}
|
|
@@ -54,8 +55,7 @@ export declare class SealClient {
|
|
|
54
55
|
* Decrypt the given encrypted bytes using cached keys.
|
|
55
56
|
* Calls fetchKeys in case one or more of the required keys is not cached yet.
|
|
56
57
|
* The function throws an error if the client's key servers are not a subset of
|
|
57
|
-
* the encrypted object's key servers
|
|
58
|
-
* threshold cannot be met.
|
|
58
|
+
* the encrypted object's key servers or if the threshold cannot be met.
|
|
59
59
|
*
|
|
60
60
|
* @param data - The encrypted bytes to decrypt.
|
|
61
61
|
* @param sessionKey - The session key to use.
|
|
@@ -67,7 +67,7 @@ export declare class SealClient {
|
|
|
67
67
|
sessionKey: SessionKey;
|
|
68
68
|
txBytes: Uint8Array;
|
|
69
69
|
}): Promise<Uint8Array<ArrayBufferLike>>;
|
|
70
|
-
getKeyServers(): Promise<KeyServer
|
|
70
|
+
getKeyServers(): Promise<Map<string, KeyServer>>;
|
|
71
71
|
/**
|
|
72
72
|
* Fetch keys from the key servers and update the cache.
|
|
73
73
|
*
|
package/dist/cjs/client.js
CHANGED
|
@@ -39,19 +39,24 @@ var import_ibe = require("./ibe.js");
|
|
|
39
39
|
var import_key_server = require("./key-server.js");
|
|
40
40
|
var import_keys = require("./keys.js");
|
|
41
41
|
var import_utils = require("./utils.js");
|
|
42
|
-
var _suiClient,
|
|
42
|
+
var _suiClient, _weights, _keyServers, _verifyKeyServers, _cachedKeys, _timeout, _totalWeight, _SealClient_instances, createEncryptionInput_fn, weight_fn, validateEncryptionServices_fn, getWeightedKeyServers_fn, loadKeyServers_fn;
|
|
43
43
|
const _SealClient = class _SealClient {
|
|
44
44
|
constructor(options) {
|
|
45
45
|
__privateAdd(this, _SealClient_instances);
|
|
46
46
|
__privateAdd(this, _suiClient);
|
|
47
|
-
__privateAdd(this,
|
|
48
|
-
__privateAdd(this, _verifyKeyServers);
|
|
47
|
+
__privateAdd(this, _weights);
|
|
49
48
|
__privateAdd(this, _keyServers, null);
|
|
49
|
+
__privateAdd(this, _verifyKeyServers);
|
|
50
50
|
// A caching map for: fullId:object_id -> partial key.
|
|
51
51
|
__privateAdd(this, _cachedKeys, /* @__PURE__ */ new Map());
|
|
52
52
|
__privateAdd(this, _timeout);
|
|
53
|
+
__privateAdd(this, _totalWeight);
|
|
53
54
|
__privateSet(this, _suiClient, options.suiClient);
|
|
54
|
-
|
|
55
|
+
if (new Set(options.serverObjectIds.map(([objectId, _]) => objectId)).size !== options.serverObjectIds.length) {
|
|
56
|
+
throw new import_error.InvalidClientOptionsError("Duplicate object IDs");
|
|
57
|
+
}
|
|
58
|
+
__privateSet(this, _weights, new Map(options.serverObjectIds));
|
|
59
|
+
__privateSet(this, _totalWeight, options.serverObjectIds.map(([_, weight]) => weight).reduce((sum, term) => sum + term, 0));
|
|
55
60
|
__privateSet(this, _verifyKeyServers, options.verifyKeyServers ?? true);
|
|
56
61
|
__privateSet(this, _timeout, options.timeout ?? 1e4);
|
|
57
62
|
}
|
|
@@ -89,7 +94,7 @@ const _SealClient = class _SealClient {
|
|
|
89
94
|
aad = new Uint8Array()
|
|
90
95
|
}) {
|
|
91
96
|
return (0, import_encrypt.encrypt)({
|
|
92
|
-
keyServers: await this.
|
|
97
|
+
keyServers: await __privateMethod(this, _SealClient_instances, getWeightedKeyServers_fn).call(this),
|
|
93
98
|
kemType,
|
|
94
99
|
threshold,
|
|
95
100
|
packageId,
|
|
@@ -101,8 +106,7 @@ const _SealClient = class _SealClient {
|
|
|
101
106
|
* Decrypt the given encrypted bytes using cached keys.
|
|
102
107
|
* Calls fetchKeys in case one or more of the required keys is not cached yet.
|
|
103
108
|
* The function throws an error if the client's key servers are not a subset of
|
|
104
|
-
* the encrypted object's key servers
|
|
105
|
-
* threshold cannot be met.
|
|
109
|
+
* the encrypted object's key servers or if the threshold cannot be met.
|
|
106
110
|
*
|
|
107
111
|
* @param data - The encrypted bytes to decrypt.
|
|
108
112
|
* @param sessionKey - The session key to use.
|
|
@@ -151,32 +155,29 @@ const _SealClient = class _SealClient {
|
|
|
151
155
|
sessionKey,
|
|
152
156
|
threshold
|
|
153
157
|
}) {
|
|
154
|
-
|
|
155
|
-
if (threshold > keyServers.length || threshold < 1 || keyServers.length < 1) {
|
|
158
|
+
if (threshold > __privateGet(this, _totalWeight) || threshold < 1) {
|
|
156
159
|
throw new import_error.InvalidThresholdError(
|
|
157
|
-
`Invalid threshold ${threshold}
|
|
160
|
+
`Invalid threshold ${threshold} servers with weights ${__privateGet(this, _weights)}`
|
|
158
161
|
);
|
|
159
162
|
}
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
if (hasAllKeys) {
|
|
173
|
-
completedServerCount++;
|
|
163
|
+
const keyServers = await this.getKeyServers();
|
|
164
|
+
const fullIds = ids.map((id) => (0, import_utils.createFullId)(sessionKey.getPackageId(), id));
|
|
165
|
+
let completedWeight = 0;
|
|
166
|
+
const remainingKeyServers = [];
|
|
167
|
+
let remainingKeyServersWeight = 0;
|
|
168
|
+
for (const objectId of keyServers.keys()) {
|
|
169
|
+
if (fullIds.every((fullId) => __privateGet(this, _cachedKeys).has(`${fullId}:${objectId}`))) {
|
|
170
|
+
completedWeight += __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
171
|
+
} else {
|
|
172
|
+
remainingKeyServers.push(objectId);
|
|
173
|
+
remainingKeyServersWeight += __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
-
if (
|
|
176
|
+
if (completedWeight >= threshold) {
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
-
for (const
|
|
179
|
+
for (const objectId of remainingKeyServers) {
|
|
180
|
+
const server = keyServers.get(objectId);
|
|
180
181
|
if (server.keyType !== import_key_server.KeyServerType.BonehFranklinBLS12381) {
|
|
181
182
|
throw new import_error.InvalidKeyServerError(
|
|
182
183
|
`Server ${server.objectId} has invalid key type: ${server.keyType}`
|
|
@@ -187,7 +188,8 @@ const _SealClient = class _SealClient {
|
|
|
187
188
|
const signedRequest = await sessionKey.createRequestParams(txBytes);
|
|
188
189
|
const controller = new AbortController();
|
|
189
190
|
const errors = [];
|
|
190
|
-
const keyFetches =
|
|
191
|
+
const keyFetches = remainingKeyServers.map(async (objectId) => {
|
|
192
|
+
const server = keyServers.get(objectId);
|
|
191
193
|
try {
|
|
192
194
|
const allKeys = await (0, import_keys.fetchKeysForAllIds)(
|
|
193
195
|
server.url,
|
|
@@ -198,7 +200,6 @@ const _SealClient = class _SealClient {
|
|
|
198
200
|
__privateGet(this, _timeout),
|
|
199
201
|
controller.signal
|
|
200
202
|
);
|
|
201
|
-
const receivedIds = /* @__PURE__ */ new Set();
|
|
202
203
|
for (const { fullId, key } of allKeys) {
|
|
203
204
|
const keyElement = import_bls12381.G1Element.fromBytes(key);
|
|
204
205
|
if (!import_ibe.BonehFranklinBLS12381Services.verifyUserSecretKey(
|
|
@@ -210,13 +211,10 @@ const _SealClient = class _SealClient {
|
|
|
210
211
|
continue;
|
|
211
212
|
}
|
|
212
213
|
__privateGet(this, _cachedKeys).set(`${fullId}:${server.objectId}`, keyElement);
|
|
213
|
-
receivedIds.add(fullId);
|
|
214
214
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
completedServerCount++;
|
|
219
|
-
if (completedServerCount >= threshold) {
|
|
215
|
+
if (fullIds.every((fullId) => __privateGet(this, _cachedKeys).has(`${fullId}:${server.objectId}`))) {
|
|
216
|
+
completedWeight += __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
217
|
+
if (completedWeight >= threshold) {
|
|
220
218
|
controller.abort();
|
|
221
219
|
}
|
|
222
220
|
}
|
|
@@ -224,13 +222,15 @@ const _SealClient = class _SealClient {
|
|
|
224
222
|
if (!controller.signal.aborted) {
|
|
225
223
|
errors.push(error);
|
|
226
224
|
}
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
} finally {
|
|
226
|
+
remainingKeyServersWeight -= __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
227
|
+
if (remainingKeyServersWeight < threshold - completedWeight) {
|
|
228
|
+
controller.abort(new import_error.TooManyFailedFetchKeyRequestsError());
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
});
|
|
232
232
|
await Promise.allSettled(keyFetches);
|
|
233
|
-
if (
|
|
233
|
+
if (completedWeight < threshold) {
|
|
234
234
|
throw (0, import_error.toMajorityError)(errors);
|
|
235
235
|
}
|
|
236
236
|
}
|
|
@@ -253,9 +253,9 @@ const _SealClient = class _SealClient {
|
|
|
253
253
|
switch (kemType) {
|
|
254
254
|
case import_encrypt.KemType.BonehFranklinBLS12381DemCCA:
|
|
255
255
|
const keyServers = await this.getKeyServers();
|
|
256
|
-
if (threshold > __privateGet(this,
|
|
256
|
+
if (threshold > __privateGet(this, _totalWeight)) {
|
|
257
257
|
throw new import_error.InvalidThresholdError(
|
|
258
|
-
`Invalid threshold ${threshold} for ${__privateGet(this,
|
|
258
|
+
`Invalid threshold ${threshold} for ${__privateGet(this, _totalWeight)} servers`
|
|
259
259
|
);
|
|
260
260
|
}
|
|
261
261
|
await this.fetchKeys({
|
|
@@ -264,14 +264,15 @@ const _SealClient = class _SealClient {
|
|
|
264
264
|
sessionKey,
|
|
265
265
|
threshold
|
|
266
266
|
});
|
|
267
|
-
const fullId = (0, import_utils.createFullId)(
|
|
267
|
+
const fullId = (0, import_utils.createFullId)(sessionKey.getPackageId(), id);
|
|
268
268
|
const derivedKeys = /* @__PURE__ */ new Map();
|
|
269
|
-
let
|
|
270
|
-
for (const
|
|
271
|
-
const cachedKey = __privateGet(this, _cachedKeys).get(`${fullId}:${
|
|
269
|
+
let weight = 0;
|
|
270
|
+
for (const objectId of keyServers.keys()) {
|
|
271
|
+
const cachedKey = __privateGet(this, _cachedKeys).get(`${fullId}:${objectId}`);
|
|
272
272
|
if (cachedKey) {
|
|
273
|
-
derivedKeys.set(
|
|
274
|
-
|
|
273
|
+
derivedKeys.set(objectId, new import_key_server.BonehFranklinBLS12381DerivedKey(cachedKey));
|
|
274
|
+
weight += __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
275
|
+
if (weight >= threshold) {
|
|
275
276
|
break;
|
|
276
277
|
}
|
|
277
278
|
}
|
|
@@ -281,11 +282,12 @@ const _SealClient = class _SealClient {
|
|
|
281
282
|
}
|
|
282
283
|
};
|
|
283
284
|
_suiClient = new WeakMap();
|
|
284
|
-
|
|
285
|
-
_verifyKeyServers = new WeakMap();
|
|
285
|
+
_weights = new WeakMap();
|
|
286
286
|
_keyServers = new WeakMap();
|
|
287
|
+
_verifyKeyServers = new WeakMap();
|
|
287
288
|
_cachedKeys = new WeakMap();
|
|
288
289
|
_timeout = new WeakMap();
|
|
290
|
+
_totalWeight = new WeakMap();
|
|
289
291
|
_SealClient_instances = new WeakSet();
|
|
290
292
|
createEncryptionInput_fn = function(type, data, aad) {
|
|
291
293
|
switch (type) {
|
|
@@ -295,31 +297,38 @@ createEncryptionInput_fn = function(type, data, aad) {
|
|
|
295
297
|
return new import_dem.Hmac256Ctr(data, aad);
|
|
296
298
|
}
|
|
297
299
|
};
|
|
300
|
+
weight_fn = function(objectId) {
|
|
301
|
+
return __privateGet(this, _weights).get(objectId) ?? 0;
|
|
302
|
+
};
|
|
298
303
|
validateEncryptionServices_fn = function(services, threshold) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
for (const [objectId, count] of serverObjectIdsMap) {
|
|
308
|
-
if (servicesMap.get(objectId) !== count) {
|
|
309
|
-
throw new import_error.InconsistentKeyServersError(
|
|
310
|
-
`Client's key servers must be a subset of the encrypted object's key servers`
|
|
311
|
-
);
|
|
312
|
-
}
|
|
304
|
+
if (services.some((objectId) => {
|
|
305
|
+
const countInClient = __privateMethod(this, _SealClient_instances, weight_fn).call(this, objectId);
|
|
306
|
+
return countInClient > 0 && countInClient !== (0, import_utils.count)(services, objectId);
|
|
307
|
+
})) {
|
|
308
|
+
throw new import_error.InconsistentKeyServersError(
|
|
309
|
+
`Client's key servers must be a subset of the encrypted object's key servers`
|
|
310
|
+
);
|
|
313
311
|
}
|
|
314
|
-
if (threshold > __privateGet(this,
|
|
312
|
+
if (threshold > __privateGet(this, _totalWeight)) {
|
|
315
313
|
throw new import_error.InvalidThresholdError(
|
|
316
|
-
`Invalid threshold ${threshold} for ${__privateGet(this,
|
|
314
|
+
`Invalid threshold ${threshold} for ${__privateGet(this, _totalWeight)} servers`
|
|
317
315
|
);
|
|
318
316
|
}
|
|
319
317
|
};
|
|
318
|
+
getWeightedKeyServers_fn = async function() {
|
|
319
|
+
const keyServers = await this.getKeyServers();
|
|
320
|
+
const keyServersWithMultiplicity = [];
|
|
321
|
+
for (const [objectId, weight] of __privateGet(this, _weights)) {
|
|
322
|
+
const keyServer = keyServers.get(objectId);
|
|
323
|
+
for (let i = 0; i < weight; i++) {
|
|
324
|
+
keyServersWithMultiplicity.push(keyServer);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return keyServersWithMultiplicity;
|
|
328
|
+
};
|
|
320
329
|
loadKeyServers_fn = async function() {
|
|
321
330
|
const keyServers = await (0, import_key_server.retrieveKeyServers)({
|
|
322
|
-
objectIds: __privateGet(this,
|
|
331
|
+
objectIds: [...__privateGet(this, _weights)].map(([objectId]) => objectId),
|
|
323
332
|
client: __privateGet(this, _suiClient)
|
|
324
333
|
});
|
|
325
334
|
if (keyServers.length === 0) {
|
|
@@ -334,7 +343,7 @@ loadKeyServers_fn = async function() {
|
|
|
334
343
|
})
|
|
335
344
|
);
|
|
336
345
|
}
|
|
337
|
-
return keyServers;
|
|
346
|
+
return new Map(keyServers.map((server) => [server.objectId, server]));
|
|
338
347
|
};
|
|
339
348
|
let SealClient = _SealClient;
|
|
340
349
|
//# sourceMappingURL=client.js.map
|
package/dist/cjs/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/client.ts"],
|
|
4
|
-
"sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { EncryptedObject } from './bcs.js';\nimport { G1Element, G2Element } from './bls12381.js';\nimport { decrypt } from './decrypt.js';\nimport type { EncryptionInput } from './dem.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { DemType, encrypt, KemType } from './encrypt.js';\nimport {\n\tInconsistentKeyServersError,\n\tInvalidKeyServerError,\n\tInvalidThresholdError,\n\ttoMajorityError,\n} from './error.js';\nimport { BonehFranklinBLS12381Services, DST } from './ibe.js';\nimport {\n\tBonehFranklinBLS12381DerivedKey,\n\tKeyServerType,\n\tretrieveKeyServers,\n\tverifyKeyServer,\n} from './key-server.js';\nimport type { DerivedKey, KeyServer } from './key-server.js';\nimport { fetchKeysForAllIds } from './keys.js';\nimport type { SessionKey } from './session-key.js';\nimport type { KeyCacheKey, SealCompatibleClient } from './types.js';\nimport { createFullId } from './utils.js';\n\n/**\n * Configuration options for initializing a SealClient\n * @property serverObjectIds: Array of object IDs for the key servers to use.\n * @property verifyKeyServers: Whether to verify the key servers' authenticity.\n * \t Should be false if servers are pre-verified (e.g., getAllowlistedKeyServers).\n * \t Defaults to true.\n * @property timeout: Timeout in milliseconds for network requests. Defaults to 10 seconds.\n */\n\nexport interface SealClientExtensionOptions {\n\tserverObjectIds: string[];\n\tverifyKeyServers?: boolean;\n\ttimeout?: number;\n}\n\nexport interface SealClientOptions extends SealClientExtensionOptions {\n\tsuiClient: SealCompatibleClient;\n}\n\nexport class SealClient {\n\t#suiClient: SealCompatibleClient;\n\t#serverObjectIds: string[];\n\t#verifyKeyServers: boolean;\n\t#keyServers: Promise<KeyServer[]> | null = null;\n\t// A caching map for: fullId:object_id -> partial key.\n\t#cachedKeys = new Map<KeyCacheKey, G1Element>();\n\t#timeout: number;\n\n\tconstructor(options: SealClientOptions) {\n\t\tthis.#suiClient = options.suiClient;\n\t\tthis.#serverObjectIds = options.serverObjectIds;\n\t\tthis.#verifyKeyServers = options.verifyKeyServers ?? true;\n\t\tthis.#timeout = options.timeout ?? 10_000;\n\t}\n\n\tstatic experimental_asClientExtension(options: SealClientExtensionOptions) {\n\t\treturn {\n\t\t\tname: 'seal' as const,\n\t\t\tregister: (client: SealCompatibleClient) => {\n\t\t\t\treturn new SealClient({\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t\t...options,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Return an encrypted message under the identity.\n\t *\n\t * @param kemType - The type of KEM to use.\n\t * @param demType - The type of DEM to use.\n\t * @param threshold - The threshold for the TSS encryption.\n\t * @param packageId - the packageId namespace.\n\t * @param id - the identity to use.\n\t * @param data - the data to encrypt.\n\t * @param aad - optional additional authenticated data.\n\t * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n\t * \tSince the symmetric key can be used to decrypt, it should not be shared but can be used e.g. for backup.\n\t */\n\tasync encrypt({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tdemType = DemType.AesGcm256,\n\t\tthreshold,\n\t\tpackageId,\n\t\tid,\n\t\tdata,\n\t\taad = new Uint8Array(),\n\t}: {\n\t\tkemType?: KemType;\n\t\tdemType?: DemType;\n\t\tthreshold: number;\n\t\tpackageId: string;\n\t\tid: string;\n\t\tdata: Uint8Array;\n\t\taad?: Uint8Array;\n\t}) {\n\t\t// TODO: Verify that packageId is first version of its package (else throw error).\n\t\treturn encrypt({\n\t\t\tkeyServers: await this.getKeyServers(),\n\t\t\tkemType,\n\t\t\tthreshold,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tencryptionInput: this.#createEncryptionInput(demType, data, aad),\n\t\t});\n\t}\n\n\t#createEncryptionInput(type: DemType, data: Uint8Array, aad: Uint8Array): EncryptionInput {\n\t\tswitch (type) {\n\t\t\tcase DemType.AesGcm256:\n\t\t\t\treturn new AesGcm256(data, aad);\n\t\t\tcase DemType.Hmac256Ctr:\n\t\t\t\treturn new Hmac256Ctr(data, aad);\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt the given encrypted bytes using cached keys.\n\t * Calls fetchKeys in case one or more of the required keys is not cached yet.\n\t * The function throws an error if the client's key servers are not a subset of\n\t * the encrypted object's key servers (including the same weights) or if the\n\t * threshold cannot be met.\n\t *\n\t * @param data - The encrypted bytes to decrypt.\n\t * @param sessionKey - The session key to use.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @returns - The decrypted plaintext corresponding to ciphertext.\n\t */\n\tasync decrypt({\n\t\tdata,\n\t\tsessionKey,\n\t\ttxBytes,\n\t}: {\n\t\tdata: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\ttxBytes: Uint8Array;\n\t}) {\n\t\tconst encryptedObject = EncryptedObject.parse(data);\n\n\t\tthis.#validateEncryptionServices(\n\t\t\tencryptedObject.services.map((s) => s[0]),\n\t\t\tencryptedObject.threshold,\n\t\t);\n\n\t\tawait this.fetchKeys({\n\t\t\tids: [encryptedObject.id],\n\t\t\ttxBytes,\n\t\t\tsessionKey,\n\t\t\tthreshold: encryptedObject.threshold,\n\t\t});\n\n\t\treturn decrypt({ encryptedObject, keys: this.#cachedKeys });\n\t}\n\n\t#validateEncryptionServices(services: string[], threshold: number) {\n\t\t// Check that the client's key servers are a subset of the encrypted object's key servers.\n\t\tconst serverObjectIdsMap = new Map<string, number>();\n\t\tfor (const objectId of this.#serverObjectIds) {\n\t\t\tserverObjectIdsMap.set(objectId, (serverObjectIdsMap.get(objectId) ?? 0) + 1);\n\t\t}\n\t\tconst servicesMap = new Map<string, number>();\n\t\tfor (const service of services) {\n\t\t\tservicesMap.set(service, (servicesMap.get(service) ?? 0) + 1);\n\t\t}\n\t\tfor (const [objectId, count] of serverObjectIdsMap) {\n\t\t\tif (servicesMap.get(objectId) !== count) {\n\t\t\t\tthrow new InconsistentKeyServersError(\n\t\t\t\t\t`Client's key servers must be a subset of the encrypted object's key servers`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Check that the threshold can be met with the client's key servers.\n\t\tif (threshold > this.#serverObjectIds.length) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${this.#serverObjectIds.length} servers`,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync getKeyServers() {\n\t\tif (!this.#keyServers) {\n\t\t\tthis.#keyServers = this.#loadKeyServers().catch((error) => {\n\t\t\t\tthis.#keyServers = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\n\t\treturn this.#keyServers;\n\t}\n\n\tasync #loadKeyServers(): Promise<KeyServer[]> {\n\t\tconst keyServers = await retrieveKeyServers({\n\t\t\tobjectIds: this.#serverObjectIds,\n\t\t\tclient: this.#suiClient,\n\t\t});\n\n\t\tif (keyServers.length === 0) {\n\t\t\tthrow new InvalidKeyServerError('No key servers found');\n\t\t}\n\n\t\tif (this.#verifyKeyServers) {\n\t\t\tawait Promise.all(\n\t\t\t\tkeyServers.map(async (server) => {\n\t\t\t\t\tif (!(await verifyKeyServer(server, this.#timeout))) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(`Key server ${server.objectId} is not valid`);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn keyServers;\n\t}\n\n\t/**\n\t * Fetch keys from the key servers and update the cache.\n\t *\n\t * It is recommended to call this function once for all ids of all encrypted objects if\n\t * there are multiple, then call decrypt for each object. This avoids calling fetchKey\n\t * individually for each decrypt.\n\t *\n\t * @param ids - The ids of the encrypted objects.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold for the TSS encryptions. The function returns when a threshold of key servers had returned keys for all ids.\n\t */\n\tasync fetchKeys({\n\t\tids,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tids: string[];\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}) {\n\t\tconst keyServers = await this.getKeyServers();\n\t\tif (threshold > keyServers.length || threshold < 1 || keyServers.length < 1) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${keyServers.length} servers`,\n\t\t\t);\n\t\t}\n\n\t\tlet completedServerCount = 0;\n\t\tconst remainingKeyServers = new Set<KeyServer>();\n\t\tconst fullIds = ids.map((id) => createFullId(DST, sessionKey.getPackageId(), id));\n\n\t\t// Count a server as completed if it has keys for all fullIds.\n\t\t// Duplicated key server ids will be counted towards the threshold.\n\t\tfor (const server of keyServers) {\n\t\t\tlet hasAllKeys = true;\n\t\t\tfor (const fullId of fullIds) {\n\t\t\t\tif (!this.#cachedKeys.has(`${fullId}:${server.objectId}`)) {\n\t\t\t\t\thasAllKeys = false;\n\t\t\t\t\tremainingKeyServers.add(server);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasAllKeys) {\n\t\t\t\tcompletedServerCount++;\n\t\t\t}\n\t\t}\n\n\t\t// Return early if we have enough keys from cache.\n\t\tif (completedServerCount >= threshold) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check server validities.\n\t\tfor (const server of remainingKeyServers) {\n\t\t\tif (server.keyType !== KeyServerType.BonehFranklinBLS12381) {\n\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t`Server ${server.objectId} has invalid key type: ${server.keyType}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst cert = await sessionKey.getCertificate();\n\t\tconst signedRequest = await sessionKey.createRequestParams(txBytes);\n\n\t\tconst controller = new AbortController();\n\t\tconst errors: Error[] = [];\n\n\t\tconst keyFetches = [...remainingKeyServers].map(async (server) => {\n\t\t\ttry {\n\t\t\t\tconst allKeys = await fetchKeysForAllIds(\n\t\t\t\t\tserver.url,\n\t\t\t\t\tsignedRequest.requestSignature,\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsignedRequest.decryptionKey,\n\t\t\t\t\tcert,\n\t\t\t\t\tthis.#timeout,\n\t\t\t\t\tcontroller.signal,\n\t\t\t\t);\n\t\t\t\t// Check validity of the keys and add them to the cache.\n\t\t\t\tconst receivedIds = new Set<string>();\n\t\t\t\tfor (const { fullId, key } of allKeys) {\n\t\t\t\t\tconst keyElement = G1Element.fromBytes(key);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!BonehFranklinBLS12381Services.verifyUserSecretKey(\n\t\t\t\t\t\t\tkeyElement,\n\t\t\t\t\t\t\tfullId,\n\t\t\t\t\t\t\tG2Element.fromBytes(server.pk),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconsole.warn('Received invalid key from key server ' + server.objectId);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis.#cachedKeys.set(`${fullId}:${server.objectId}`, keyElement);\n\t\t\t\t\treceivedIds.add(fullId);\n\t\t\t\t}\n\n\t\t\t\t// Check if all the receivedIds are consistent with the requested fullIds.\n\t\t\t\t// If so, consider the key server got all keys and mark as completed.\n\t\t\t\tconst expectedIds = new Set(fullIds);\n\t\t\t\tconst hasAllKeys =\n\t\t\t\t\treceivedIds.size === expectedIds.size &&\n\t\t\t\t\t[...receivedIds].every((id) => expectedIds.has(id));\n\n\t\t\t\t// Return early if the completed servers is more than threshold.\n\t\t\t\tif (hasAllKeys) {\n\t\t\t\t\tcompletedServerCount++;\n\t\t\t\t\tif (completedServerCount >= threshold) {\n\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\terrors.push(error as Error);\n\t\t\t\t}\n\t\t\t\t// If there are too many errors that the threshold is not attainable, return early with error.\n\t\t\t\tif (remainingKeyServers.size - errors.length < threshold - completedServerCount) {\n\t\t\t\t\tcontroller.abort(error);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.allSettled(keyFetches);\n\n\t\tif (completedServerCount < threshold) {\n\t\t\tthrow toMajorityError(errors);\n\t\t}\n\t}\n\n\t/**\n\t * Get derived keys from the given services.\n\t *\n\t * @param id - The id of the encrypted object.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold.\n\t * @returns - Derived keys for the given services that are in the cache as a \"service object ID\" -> derived key map. If the call is succesful, exactly threshold keys will be returned.\n\t */\n\tasync getDerivedKeys({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tid,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tkemType?: KemType;\n\t\tid: string;\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}): Promise<Map<string, DerivedKey>> {\n\t\tswitch (kemType) {\n\t\t\tcase KemType.BonehFranklinBLS12381DemCCA:\n\t\t\t\tconst keyServers = await this.getKeyServers();\n\t\t\t\tif (threshold > this.#serverObjectIds.length) {\n\t\t\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t\t\t`Invalid threshold ${threshold} for ${this.#serverObjectIds.length} servers`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait this.fetchKeys({\n\t\t\t\t\tids: [id],\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsessionKey,\n\t\t\t\t\tthreshold,\n\t\t\t\t});\n\n\t\t\t\t// After calling fetchKeys, we can be sure that there are at least `threshold` of the required keys in the cache.\n\t\t\t\t// It is also checked there that the KeyServerType is BonehFranklinBLS12381 for all services.\n\n\t\t\t\tconst fullId = createFullId(DST, sessionKey.getPackageId(), id);\n\n\t\t\t\tconst derivedKeys = new Map<string, DerivedKey>();\n\t\t\t\tlet servicesAdded = 0;\n\t\t\t\tfor (const keyServer of keyServers) {\n\t\t\t\t\t// The code below assumes that the KeyServerType is BonehFranklinBLS12381.\n\t\t\t\t\tconst cachedKey = this.#cachedKeys.get(`${fullId}:${keyServer.objectId}`);\n\t\t\t\t\tif (cachedKey) {\n\t\t\t\t\t\tderivedKeys.set(keyServer.objectId, new BonehFranklinBLS12381DerivedKey(cachedKey));\n\t\t\t\t\t\tif (++servicesAdded === threshold) {\n\t\t\t\t\t\t\t// We have enough keys, so we can stop.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn derivedKeys;\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,iBAAgC;AAChC,sBAAqC;AACrC,qBAAwB;AAExB,iBAAsC;AACtC,qBAA0C;AAC1C,
|
|
4
|
+
"sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { EncryptedObject } from './bcs.js';\nimport { G1Element, G2Element } from './bls12381.js';\nimport { decrypt } from './decrypt.js';\nimport type { EncryptionInput } from './dem.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { DemType, encrypt, KemType } from './encrypt.js';\nimport {\n\tInconsistentKeyServersError,\n\tInvalidClientOptionsError,\n\tInvalidKeyServerError,\n\tInvalidThresholdError,\n\ttoMajorityError,\n\tTooManyFailedFetchKeyRequestsError,\n} from './error.js';\nimport { BonehFranklinBLS12381Services } from './ibe.js';\nimport {\n\tBonehFranklinBLS12381DerivedKey,\n\tKeyServerType,\n\tretrieveKeyServers,\n\tverifyKeyServer,\n} from './key-server.js';\nimport type { DerivedKey, KeyServer } from './key-server.js';\nimport { fetchKeysForAllIds } from './keys.js';\nimport type { SessionKey } from './session-key.js';\nimport type { KeyCacheKey, SealCompatibleClient } from './types.js';\nimport { createFullId, count } from './utils.js';\n\n/**\n * Configuration options for initializing a SealClient\n * @property serverObjectIds: Array of the key servers to use.\n * \t The first element is the object ID, and the second element is the weight of the key server.\n * @property verifyKeyServers: Whether to verify the key servers' authenticity.\n * \t Should be false if servers are pre-verified (e.g., getAllowlistedKeyServers).\n * \t Defaults to true.\n * @property timeout: Timeout in milliseconds for network requests. Defaults to 10 seconds.\n */\nexport interface SealClientExtensionOptions {\n\tserverObjectIds: [string, number][];\n\tverifyKeyServers?: boolean;\n\ttimeout?: number;\n}\n\nexport interface SealClientOptions extends SealClientExtensionOptions {\n\tsuiClient: SealCompatibleClient;\n}\n\nexport class SealClient {\n\t#suiClient: SealCompatibleClient;\n\t#weights: Map<string, number>;\n\t#keyServers: Promise<Map<string, KeyServer>> | null = null;\n\t#verifyKeyServers: boolean;\n\t// A caching map for: fullId:object_id -> partial key.\n\t#cachedKeys = new Map<KeyCacheKey, G1Element>();\n\t#timeout: number;\n\t#totalWeight: number;\n\n\tconstructor(options: SealClientOptions) {\n\t\tthis.#suiClient = options.suiClient;\n\n\t\tif (\n\t\t\tnew Set(options.serverObjectIds.map(([objectId, _]) => objectId)).size !==\n\t\t\toptions.serverObjectIds.length\n\t\t) {\n\t\t\tthrow new InvalidClientOptionsError('Duplicate object IDs');\n\t\t}\n\n\t\tthis.#weights = new Map(options.serverObjectIds);\n\t\tthis.#totalWeight = options.serverObjectIds\n\t\t\t.map(([_, weight]) => weight)\n\t\t\t.reduce((sum, term) => sum + term, 0);\n\n\t\tthis.#verifyKeyServers = options.verifyKeyServers ?? true;\n\t\tthis.#timeout = options.timeout ?? 10_000;\n\t}\n\n\tstatic experimental_asClientExtension(options: SealClientExtensionOptions) {\n\t\treturn {\n\t\t\tname: 'seal' as const,\n\t\t\tregister: (client: SealCompatibleClient) => {\n\t\t\t\treturn new SealClient({\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t\t...options,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Return an encrypted message under the identity.\n\t *\n\t * @param kemType - The type of KEM to use.\n\t * @param demType - The type of DEM to use.\n\t * @param threshold - The threshold for the TSS encryption.\n\t * @param packageId - the packageId namespace.\n\t * @param id - the identity to use.\n\t * @param data - the data to encrypt.\n\t * @param aad - optional additional authenticated data.\n\t * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n\t * \tSince the symmetric key can be used to decrypt, it should not be shared but can be used e.g. for backup.\n\t */\n\tasync encrypt({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tdemType = DemType.AesGcm256,\n\t\tthreshold,\n\t\tpackageId,\n\t\tid,\n\t\tdata,\n\t\taad = new Uint8Array(),\n\t}: {\n\t\tkemType?: KemType;\n\t\tdemType?: DemType;\n\t\tthreshold: number;\n\t\tpackageId: string;\n\t\tid: string;\n\t\tdata: Uint8Array;\n\t\taad?: Uint8Array;\n\t}) {\n\t\t// TODO: Verify that packageId is first version of its package (else throw error).\n\t\treturn encrypt({\n\t\t\tkeyServers: await this.#getWeightedKeyServers(),\n\t\t\tkemType,\n\t\t\tthreshold,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tencryptionInput: this.#createEncryptionInput(demType, data, aad),\n\t\t});\n\t}\n\n\t#createEncryptionInput(type: DemType, data: Uint8Array, aad: Uint8Array): EncryptionInput {\n\t\tswitch (type) {\n\t\t\tcase DemType.AesGcm256:\n\t\t\t\treturn new AesGcm256(data, aad);\n\t\t\tcase DemType.Hmac256Ctr:\n\t\t\t\treturn new Hmac256Ctr(data, aad);\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt the given encrypted bytes using cached keys.\n\t * Calls fetchKeys in case one or more of the required keys is not cached yet.\n\t * The function throws an error if the client's key servers are not a subset of\n\t * the encrypted object's key servers or if the threshold cannot be met.\n\t *\n\t * @param data - The encrypted bytes to decrypt.\n\t * @param sessionKey - The session key to use.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @returns - The decrypted plaintext corresponding to ciphertext.\n\t */\n\tasync decrypt({\n\t\tdata,\n\t\tsessionKey,\n\t\ttxBytes,\n\t}: {\n\t\tdata: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\ttxBytes: Uint8Array;\n\t}) {\n\t\tconst encryptedObject = EncryptedObject.parse(data);\n\n\t\tthis.#validateEncryptionServices(\n\t\t\tencryptedObject.services.map((s) => s[0]),\n\t\t\tencryptedObject.threshold,\n\t\t);\n\n\t\tawait this.fetchKeys({\n\t\t\tids: [encryptedObject.id],\n\t\t\ttxBytes,\n\t\t\tsessionKey,\n\t\t\tthreshold: encryptedObject.threshold,\n\t\t});\n\n\t\treturn decrypt({ encryptedObject, keys: this.#cachedKeys });\n\t}\n\n\t#weight(objectId: string) {\n\t\treturn this.#weights.get(objectId) ?? 0;\n\t}\n\n\t#validateEncryptionServices(services: string[], threshold: number) {\n\t\t// Check that the client's key servers are a subset of the encrypted object's key servers.\n\t\tif (\n\t\t\tservices.some((objectId) => {\n\t\t\t\tconst countInClient = this.#weight(objectId);\n\t\t\t\treturn countInClient > 0 && countInClient !== count(services, objectId);\n\t\t\t})\n\t\t) {\n\t\t\tthrow new InconsistentKeyServersError(\n\t\t\t\t`Client's key servers must be a subset of the encrypted object's key servers`,\n\t\t\t);\n\t\t}\n\t\t// Check that the threshold can be met with the client's key servers.\n\t\tif (threshold > this.#totalWeight) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${this.#totalWeight} servers`,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync getKeyServers(): Promise<Map<string, KeyServer>> {\n\t\tif (!this.#keyServers) {\n\t\t\tthis.#keyServers = this.#loadKeyServers().catch((error) => {\n\t\t\t\tthis.#keyServers = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\t\treturn this.#keyServers;\n\t}\n\n\t/**\n\t * Returns a list of key servers with multiplicity according to their weights.\n\t * The list is used for encryption.\n\t */\n\tasync #getWeightedKeyServers() {\n\t\tconst keyServers = await this.getKeyServers();\n\t\tconst keyServersWithMultiplicity = [];\n\t\tfor (const [objectId, weight] of this.#weights) {\n\t\t\tconst keyServer = keyServers.get(objectId)!;\n\t\t\tfor (let i = 0; i < weight; i++) {\n\t\t\t\tkeyServersWithMultiplicity.push(keyServer);\n\t\t\t}\n\t\t}\n\t\treturn keyServersWithMultiplicity;\n\t}\n\n\tasync #loadKeyServers(): Promise<Map<string, KeyServer>> {\n\t\tconst keyServers = await retrieveKeyServers({\n\t\t\tobjectIds: [...this.#weights].map(([objectId]) => objectId),\n\t\t\tclient: this.#suiClient,\n\t\t});\n\n\t\tif (keyServers.length === 0) {\n\t\t\tthrow new InvalidKeyServerError('No key servers found');\n\t\t}\n\n\t\tif (this.#verifyKeyServers) {\n\t\t\tawait Promise.all(\n\t\t\t\tkeyServers.map(async (server) => {\n\t\t\t\t\tif (!(await verifyKeyServer(server, this.#timeout))) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(`Key server ${server.objectId} is not valid`);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn new Map(keyServers.map((server) => [server.objectId, server]));\n\t}\n\n\t/**\n\t * Fetch keys from the key servers and update the cache.\n\t *\n\t * It is recommended to call this function once for all ids of all encrypted objects if\n\t * there are multiple, then call decrypt for each object. This avoids calling fetchKey\n\t * individually for each decrypt.\n\t *\n\t * @param ids - The ids of the encrypted objects.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold for the TSS encryptions. The function returns when a threshold of key servers had returned keys for all ids.\n\t */\n\tasync fetchKeys({\n\t\tids,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tids: string[];\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}) {\n\t\tif (threshold > this.#totalWeight || threshold < 1) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} servers with weights ${this.#weights}`,\n\t\t\t);\n\t\t}\n\t\tconst keyServers = await this.getKeyServers();\n\t\tconst fullIds = ids.map((id) => createFullId(sessionKey.getPackageId(), id));\n\n\t\t// Count a server as completed if it has keys for all fullIds.\n\t\t// Duplicated key server ids will be counted towards the threshold.\n\t\tlet completedWeight = 0;\n\t\tconst remainingKeyServers = [];\n\t\tlet remainingKeyServersWeight = 0;\n\t\tfor (const objectId of keyServers.keys()) {\n\t\t\tif (fullIds.every((fullId) => this.#cachedKeys.has(`${fullId}:${objectId}`))) {\n\t\t\t\tcompletedWeight += this.#weight(objectId)!;\n\t\t\t} else {\n\t\t\t\tremainingKeyServers.push(objectId);\n\t\t\t\tremainingKeyServersWeight += this.#weight(objectId)!;\n\t\t\t}\n\t\t}\n\n\t\t// Return early if we have enough keys from cache.\n\t\tif (completedWeight >= threshold) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check server validities.\n\t\tfor (const objectId of remainingKeyServers) {\n\t\t\tconst server = keyServers.get(objectId)!;\n\t\t\tif (server.keyType !== KeyServerType.BonehFranklinBLS12381) {\n\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t`Server ${server.objectId} has invalid key type: ${server.keyType}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst cert = await sessionKey.getCertificate();\n\t\tconst signedRequest = await sessionKey.createRequestParams(txBytes);\n\n\t\tconst controller = new AbortController();\n\t\tconst errors: Error[] = [];\n\n\t\tconst keyFetches = remainingKeyServers.map(async (objectId) => {\n\t\t\tconst server = keyServers.get(objectId)!;\n\t\t\ttry {\n\t\t\t\tconst allKeys = await fetchKeysForAllIds(\n\t\t\t\t\tserver.url,\n\t\t\t\t\tsignedRequest.requestSignature,\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsignedRequest.decryptionKey,\n\t\t\t\t\tcert,\n\t\t\t\t\tthis.#timeout,\n\t\t\t\t\tcontroller.signal,\n\t\t\t\t);\n\t\t\t\t// Check validity of the keys and add them to the cache.\n\t\t\t\tfor (const { fullId, key } of allKeys) {\n\t\t\t\t\tconst keyElement = G1Element.fromBytes(key);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!BonehFranklinBLS12381Services.verifyUserSecretKey(\n\t\t\t\t\t\t\tkeyElement,\n\t\t\t\t\t\t\tfullId,\n\t\t\t\t\t\t\tG2Element.fromBytes(server.pk),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconsole.warn('Received invalid key from key server ' + server.objectId);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis.#cachedKeys.set(`${fullId}:${server.objectId}`, keyElement);\n\t\t\t\t}\n\n\t\t\t\t// Check if all the receivedIds are consistent with the requested fullIds.\n\t\t\t\t// If so, consider the key server got all keys and mark as completed.\n\t\t\t\tif (fullIds.every((fullId) => this.#cachedKeys.has(`${fullId}:${server.objectId}`))) {\n\t\t\t\t\tcompletedWeight += this.#weight(objectId)!;\n\n\t\t\t\t\t// Return early if the completed servers is more than the threshold.\n\t\t\t\t\tif (completedWeight >= threshold) {\n\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\terrors.push(error as Error);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\t// If there are too many errors that the threshold is not attainable, return early with error.\n\t\t\t\tremainingKeyServersWeight -= this.#weight(objectId)!;\n\t\t\t\tif (remainingKeyServersWeight < threshold - completedWeight) {\n\t\t\t\t\tcontroller.abort(new TooManyFailedFetchKeyRequestsError());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.allSettled(keyFetches);\n\n\t\tif (completedWeight < threshold) {\n\t\t\tthrow toMajorityError(errors);\n\t\t}\n\t}\n\n\t/**\n\t * Get derived keys from the given services.\n\t *\n\t * @param id - The id of the encrypted object.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold.\n\t * @returns - Derived keys for the given services that are in the cache as a \"service object ID\" -> derived key map. If the call is succesful, exactly threshold keys will be returned.\n\t */\n\tasync getDerivedKeys({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tid,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tkemType?: KemType;\n\t\tid: string;\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}): Promise<Map<string, DerivedKey>> {\n\t\tswitch (kemType) {\n\t\t\tcase KemType.BonehFranklinBLS12381DemCCA:\n\t\t\t\tconst keyServers = await this.getKeyServers();\n\t\t\t\tif (threshold > this.#totalWeight) {\n\t\t\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t\t\t`Invalid threshold ${threshold} for ${this.#totalWeight} servers`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait this.fetchKeys({\n\t\t\t\t\tids: [id],\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsessionKey,\n\t\t\t\t\tthreshold,\n\t\t\t\t});\n\n\t\t\t\t// After calling fetchKeys, we can be sure that there are at least `threshold` of the required keys in the cache.\n\t\t\t\t// It is also checked there that the KeyServerType is BonehFranklinBLS12381 for all services.\n\n\t\t\t\tconst fullId = createFullId(sessionKey.getPackageId(), id);\n\n\t\t\t\tconst derivedKeys = new Map();\n\t\t\t\tlet weight = 0;\n\t\t\t\tfor (const objectId of keyServers.keys()) {\n\t\t\t\t\t// The code below assumes that the KeyServerType is BonehFranklinBLS12381.\n\t\t\t\t\tconst cachedKey = this.#cachedKeys.get(`${fullId}:${objectId}`);\n\t\t\t\t\tif (cachedKey) {\n\t\t\t\t\t\tderivedKeys.set(objectId, new BonehFranklinBLS12381DerivedKey(cachedKey));\n\t\t\t\t\t\tweight += this.#weight(objectId)!;\n\t\t\t\t\t\tif (weight >= threshold) {\n\t\t\t\t\t\t\t// We have enough keys, so we can stop.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn derivedKeys;\n\t\t}\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/decrypt.js
CHANGED
|
@@ -33,7 +33,7 @@ async function decrypt({ encryptedObject, keys }) {
|
|
|
33
33
|
if (!encryptedObject.encryptedShares.BonehFranklinBLS12381) {
|
|
34
34
|
throw new import_error.UnsupportedFeatureError("Encryption mode not supported");
|
|
35
35
|
}
|
|
36
|
-
const fullId = (0, import_utils.createFullId)(
|
|
36
|
+
const fullId = (0, import_utils.createFullId)(encryptedObject.packageId, encryptedObject.id);
|
|
37
37
|
const inKeystore = encryptedObject.services.map((_, i) => i).filter((i) => keys.has(`${fullId}:${encryptedObject.services[i][0]}`));
|
|
38
38
|
if (inKeystore.length < encryptedObject.threshold) {
|
|
39
39
|
throw new Error("Not enough shares. Please fetch more keys.");
|
|
@@ -56,24 +56,22 @@ async function decrypt({ encryptedObject, keys }) {
|
|
|
56
56
|
);
|
|
57
57
|
return { index, share };
|
|
58
58
|
});
|
|
59
|
-
const
|
|
60
|
-
const demKey = (0, import_kdf.deriveKey)(
|
|
59
|
+
const baseKey = await combine(shares);
|
|
60
|
+
const demKey = (0, import_kdf.deriveKey)(
|
|
61
|
+
import_kdf.KeyPurpose.DEM,
|
|
62
|
+
baseKey,
|
|
63
|
+
encryptedObject.encryptedShares.BonehFranklinBLS12381.encryptedShares,
|
|
64
|
+
encryptedObject.threshold,
|
|
65
|
+
encryptedObject.services.map(([objectId, _]) => objectId)
|
|
66
|
+
);
|
|
61
67
|
if (encryptedObject.ciphertext.Aes256Gcm) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new Error("Decryption failed");
|
|
66
|
-
}
|
|
68
|
+
return import_dem.AesGcm256.decrypt(demKey, encryptedObject.ciphertext);
|
|
69
|
+
} else if (encryptedObject.ciphertext.Hmac256Ctr) {
|
|
70
|
+
return import_dem.Hmac256Ctr.decrypt(demKey, encryptedObject.ciphertext);
|
|
67
71
|
} else if (encryptedObject.ciphertext.Plain) {
|
|
68
72
|
return demKey;
|
|
69
|
-
} else if (encryptedObject.ciphertext.Hmac256Ctr) {
|
|
70
|
-
try {
|
|
71
|
-
return import_dem.Hmac256Ctr.decrypt(demKey, encryptedObject.ciphertext);
|
|
72
|
-
} catch {
|
|
73
|
-
throw new Error("Decryption failed");
|
|
74
|
-
}
|
|
75
73
|
} else {
|
|
76
|
-
throw new
|
|
74
|
+
throw new import_error.InvalidCiphertextError("Invalid ciphertext type");
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
async function combine(shares) {
|
|
@@ -83,12 +81,7 @@ async function combine(shares) {
|
|
|
83
81
|
return Promise.resolve(shares[0].share);
|
|
84
82
|
}
|
|
85
83
|
return (0, import_shamir_secret_sharing.combine)(
|
|
86
|
-
shares.map(({ index, share }) =>
|
|
87
|
-
const packedShare = new Uint8Array(share.length + 1);
|
|
88
|
-
packedShare.set(share, 0);
|
|
89
|
-
packedShare[share.length] = index;
|
|
90
|
-
return packedShare;
|
|
91
|
-
})
|
|
84
|
+
shares.map(({ index, share }) => (0, import_utils.flatten)([share, new Uint8Array([index])]))
|
|
92
85
|
);
|
|
93
86
|
}
|
|
94
87
|
//# sourceMappingURL=decrypt.js.map
|
package/dist/cjs/decrypt.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/decrypt.ts"],
|
|
4
|
-
"sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { combine as externalCombine } from 'shamir-secret-sharing';\n\nimport type { EncryptedObject } from './bcs.js';\nimport type { G1Element } from './bls12381.js';\nimport { G2Element } from './bls12381.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { InvalidCiphertextError, UnsupportedFeatureError } from './error.js';\nimport { BonehFranklinBLS12381Services
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,iBAAwB;AACxB,mCAA2C;AAI3C,sBAA0B;AAC1B,iBAAsC;AACtC,mBAAgE;AAChE,
|
|
4
|
+
"sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { combine as externalCombine } from 'shamir-secret-sharing';\n\nimport type { EncryptedObject } from './bcs.js';\nimport type { G1Element } from './bls12381.js';\nimport { G2Element } from './bls12381.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { InvalidCiphertextError, UnsupportedFeatureError } from './error.js';\nimport { BonehFranklinBLS12381Services } from './ibe.js';\nimport { deriveKey, KeyPurpose } from './kdf.js';\nimport type { KeyCacheKey } from './types.js';\nimport { createFullId, flatten } from './utils.js';\n\nexport interface DecryptOptions {\n\tencryptedObject: typeof EncryptedObject.$inferType;\n\tkeys: Map<KeyCacheKey, G1Element>;\n}\n\n/**\n * Decrypt the given encrypted bytes with the given cached secret keys for the full ID.\n * It's assumed that fetchKeys has been called to fetch the secret keys for enough key servers\n * otherwise, this will throw an error.\n *\n * @returns - The decrypted plaintext corresponding to ciphertext.\n */\nexport async function decrypt({ encryptedObject, keys }: DecryptOptions): Promise<Uint8Array> {\n\tif (!encryptedObject.encryptedShares.BonehFranklinBLS12381) {\n\t\tthrow new UnsupportedFeatureError('Encryption mode not supported');\n\t}\n\n\tconst fullId = createFullId(encryptedObject.packageId, encryptedObject.id);\n\n\t// Get the indices of the service whose keys are in the keystore.\n\tconst inKeystore = encryptedObject.services\n\t\t.map((_, i) => i)\n\t\t.filter((i) => keys.has(`${fullId}:${encryptedObject.services[i][0]}`));\n\n\tif (inKeystore.length < encryptedObject.threshold) {\n\t\tthrow new Error('Not enough shares. Please fetch more keys.');\n\t}\n\n\tconst encryptedShares = encryptedObject.encryptedShares.BonehFranklinBLS12381.encryptedShares;\n\tif (encryptedShares.length !== encryptedObject.services.length) {\n\t\tthrow new InvalidCiphertextError(\n\t\t\t`Mismatched shares ${encryptedShares.length} and services ${encryptedObject.services.length}`,\n\t\t);\n\t}\n\n\tconst nonce = G2Element.fromBytes(encryptedObject.encryptedShares.BonehFranklinBLS12381.nonce);\n\n\t// Decrypt each share.\n\tconst shares = inKeystore.map((i) => {\n\t\tconst [objectId, index] = encryptedObject.services[i];\n\t\t// Use the index as the unique info parameter to allow for multiple shares per key server.\n\t\tconst share = BonehFranklinBLS12381Services.decrypt(\n\t\t\tnonce,\n\t\t\tkeys.get(`${fullId}:${objectId}`)!,\n\t\t\tencryptedShares[i],\n\t\t\tfromHex(fullId),\n\t\t\t[objectId, index],\n\t\t);\n\t\t// The Shamir secret sharing library expects the index/x-coordinate to be at the end of the share.\n\t\treturn { index, share };\n\t});\n\n\t// Combine the decrypted shares into the key.\n\tconst baseKey = await combine(shares);\n\n\tconst demKey = deriveKey(\n\t\tKeyPurpose.DEM,\n\t\tbaseKey,\n\t\tencryptedObject.encryptedShares.BonehFranklinBLS12381.encryptedShares,\n\t\tencryptedObject.threshold,\n\t\tencryptedObject.services.map(([objectId, _]) => objectId),\n\t);\n\n\tif (encryptedObject.ciphertext.Aes256Gcm) {\n\t\treturn AesGcm256.decrypt(demKey, encryptedObject.ciphertext);\n\t} else if (encryptedObject.ciphertext.Hmac256Ctr) {\n\t\treturn Hmac256Ctr.decrypt(demKey, encryptedObject.ciphertext);\n\t} else if (encryptedObject.ciphertext.Plain) {\n\t\t// In case `Plain` mode is used, return the key.\n\t\treturn demKey;\n\t} else {\n\t\tthrow new InvalidCiphertextError('Invalid ciphertext type');\n\t}\n}\n\n/**\n * Helper function that combines the shares into the key.\n * @param shares - The shares to combine.\n * @returns - The combined key.\n */\nasync function combine(shares: { index: number; share: Uint8Array }[]): Promise<Uint8Array> {\n\tif (shares.length === 0) {\n\t\tthrow new Error('Invalid shares length');\n\t} else if (shares.length === 1) {\n\t\t// The Shamir secret sharing library expects at least two shares.\n\t\t// If there is only one and the threshold is 1, the reconstructed secret is the same as the share.\n\t\treturn Promise.resolve(shares[0].share);\n\t}\n\n\t// The Shamir secret sharing library expects the index/x-coordinate to be at the end of the share\n\treturn externalCombine(\n\t\tshares.map(({ index, share }) => flatten([share, new Uint8Array([index])])),\n\t);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,iBAAwB;AACxB,mCAA2C;AAI3C,sBAA0B;AAC1B,iBAAsC;AACtC,mBAAgE;AAChE,iBAA8C;AAC9C,iBAAsC;AAEtC,mBAAsC;AActC,eAAsB,QAAQ,EAAE,iBAAiB,KAAK,GAAwC;AAC7F,MAAI,CAAC,gBAAgB,gBAAgB,uBAAuB;AAC3D,UAAM,IAAI,qCAAwB,+BAA+B;AAAA,EAClE;AAEA,QAAM,aAAS,2BAAa,gBAAgB,WAAW,gBAAgB,EAAE;AAGzE,QAAM,aAAa,gBAAgB,SACjC,IAAI,CAAC,GAAG,MAAM,CAAC,EACf,OAAO,CAAC,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,gBAAgB,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAEvE,MAAI,WAAW,SAAS,gBAAgB,WAAW;AAClD,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC7D;AAEA,QAAM,kBAAkB,gBAAgB,gBAAgB,sBAAsB;AAC9E,MAAI,gBAAgB,WAAW,gBAAgB,SAAS,QAAQ;AAC/D,UAAM,IAAI;AAAA,MACT,qBAAqB,gBAAgB,MAAM,iBAAiB,gBAAgB,SAAS,MAAM;AAAA,IAC5F;AAAA,EACD;AAEA,QAAM,QAAQ,0BAAU,UAAU,gBAAgB,gBAAgB,sBAAsB,KAAK;AAG7F,QAAM,SAAS,WAAW,IAAI,CAAC,MAAM;AACpC,UAAM,CAAC,UAAU,KAAK,IAAI,gBAAgB,SAAS,CAAC;AAEpD,UAAM,QAAQ,yCAA8B;AAAA,MAC3C;AAAA,MACA,KAAK,IAAI,GAAG,MAAM,IAAI,QAAQ,EAAE;AAAA,MAChC,gBAAgB,CAAC;AAAA,UACjB,oBAAQ,MAAM;AAAA,MACd,CAAC,UAAU,KAAK;AAAA,IACjB;AAEA,WAAO,EAAE,OAAO,MAAM;AAAA,EACvB,CAAC;AAGD,QAAM,UAAU,MAAM,QAAQ,MAAM;AAEpC,QAAM,aAAS;AAAA,IACd,sBAAW;AAAA,IACX;AAAA,IACA,gBAAgB,gBAAgB,sBAAsB;AAAA,IACtD,gBAAgB;AAAA,IAChB,gBAAgB,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,QAAQ;AAAA,EACzD;AAEA,MAAI,gBAAgB,WAAW,WAAW;AACzC,WAAO,qBAAU,QAAQ,QAAQ,gBAAgB,UAAU;AAAA,EAC5D,WAAW,gBAAgB,WAAW,YAAY;AACjD,WAAO,sBAAW,QAAQ,QAAQ,gBAAgB,UAAU;AAAA,EAC7D,WAAW,gBAAgB,WAAW,OAAO;AAE5C,WAAO;AAAA,EACR,OAAO;AACN,UAAM,IAAI,oCAAuB,yBAAyB;AAAA,EAC3D;AACD;AAOA,eAAe,QAAQ,QAAqE;AAC3F,MAAI,OAAO,WAAW,GAAG;AACxB,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACxC,WAAW,OAAO,WAAW,GAAG;AAG/B,WAAO,QAAQ,QAAQ,OAAO,CAAC,EAAE,KAAK;AAAA,EACvC;AAGA,aAAO,6BAAAA;AAAA,IACN,OAAO,IAAI,CAAC,EAAE,OAAO,MAAM,UAAM,sBAAQ,CAAC,OAAO,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAAA,EAC3E;AACD;",
|
|
6
6
|
"names": ["externalCombine"]
|
|
7
7
|
}
|
package/dist/cjs/dem.js
CHANGED
|
@@ -91,18 +91,22 @@ class AesGcm256 {
|
|
|
91
91
|
if (!("Aes256Gcm" in ciphertext)) {
|
|
92
92
|
throw new import_error.InvalidCiphertextError(`Invalid ciphertext ${ciphertext}`);
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
try {
|
|
95
|
+
const aesCryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]);
|
|
96
|
+
return new Uint8Array(
|
|
97
|
+
await crypto.subtle.decrypt(
|
|
98
|
+
{
|
|
99
|
+
name: "AES-GCM",
|
|
100
|
+
iv,
|
|
101
|
+
additionalData: new Uint8Array(ciphertext.Aes256Gcm.aad ?? [])
|
|
102
|
+
},
|
|
103
|
+
aesCryptoKey,
|
|
104
|
+
new Uint8Array(ciphertext.Aes256Gcm.blob)
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
throw new import_error.DecryptionError(`Decryption failed`);
|
|
109
|
+
}
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
class Plain {
|
|
@@ -142,23 +146,21 @@ class Hmac256Ctr {
|
|
|
142
146
|
const blob = new Uint8Array(ciphertext.Hmac256Ctr.blob);
|
|
143
147
|
const mac = Hmac256Ctr.computeMac(key, aad, blob);
|
|
144
148
|
if (!(0, import_utils.equalBytes)(mac, new Uint8Array(ciphertext.Hmac256Ctr.mac))) {
|
|
145
|
-
throw new import_error.
|
|
149
|
+
throw new import_error.DecryptionError(`Invalid MAC ${mac}`);
|
|
146
150
|
}
|
|
147
151
|
return Hmac256Ctr.encryptInCtrMode(key, blob);
|
|
148
152
|
}
|
|
149
153
|
static computeMac(key, aad, ciphertext) {
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const mac = (0, import_hmac.hmac)(import_sha3.sha3_256, macKey, macInput);
|
|
154
|
+
const macInput = (0, import_utils2.flatten)([MacKeyTag, toBytes(aad.length), aad, ciphertext]);
|
|
155
|
+
const mac = (0, import_hmac.hmac)(import_sha3.sha3_256, key, macInput);
|
|
153
156
|
return mac;
|
|
154
157
|
}
|
|
155
158
|
static encryptInCtrMode(key, msg) {
|
|
156
159
|
const blockSize = 32;
|
|
157
|
-
const result = Uint8Array
|
|
158
|
-
const encryptionKey = (0, import_hmac.hmac)(import_sha3.sha3_256, key, EncryptionKeyTag);
|
|
160
|
+
const result = new Uint8Array(msg.length);
|
|
159
161
|
for (let i = 0; i * blockSize < msg.length; i++) {
|
|
160
162
|
const block = msg.subarray(i * blockSize, (i + 1) * blockSize);
|
|
161
|
-
const mask = (0, import_hmac.hmac)(import_sha3.sha3_256,
|
|
163
|
+
const mask = (0, import_hmac.hmac)(import_sha3.sha3_256, key, (0, import_utils2.flatten)([EncryptionKeyTag, toBytes(i)]));
|
|
162
164
|
const encryptedBlock = (0, import_utils2.xorUnchecked)(block, mask);
|
|
163
165
|
result.set(encryptedBlock, i * blockSize);
|
|
164
166
|
}
|
|
@@ -168,6 +170,6 @@ class Hmac256Ctr {
|
|
|
168
170
|
function toBytes(n) {
|
|
169
171
|
return import_bcs.bcs.u64().serialize(n).toBytes();
|
|
170
172
|
}
|
|
171
|
-
const EncryptionKeyTag = new
|
|
172
|
-
const MacKeyTag = new
|
|
173
|
+
const EncryptionKeyTag = new TextEncoder().encode("HMAC-CTR-ENC");
|
|
174
|
+
const MacKeyTag = new TextEncoder().encode("HMAC-CTR-MAC");
|
|
173
175
|
//# sourceMappingURL=dem.js.map
|