@tinycloud/sdk-services 3.11.0-beta.2 → 3.12.0-beta.10
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/{BaseService-Ma2MwJAd.d.ts → BaseService-BRDG69KR.d.ts} +1 -1
- package/dist/{BaseService-O0iyuFLk.d.cts → BaseService-CdBkfPGE.d.cts} +1 -1
- package/dist/{IHooksService-4QNODVy6.d.cts → IHooksService-C9p3stsB.d.cts} +1 -1
- package/dist/{IHooksService-CvTpJbQD.d.ts → IHooksService-D6eyZat1.d.ts} +1 -1
- package/dist/{ISQLService-Aly1CBsW.d.ts → ISQLService-B_4KcGA-.d.ts} +1 -1
- package/dist/{ISQLService-BOgWzHLT.d.cts → ISQLService-C6nNpcls.d.cts} +1 -1
- package/dist/context.cjs +4 -0
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.cts +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/context.js +4 -0
- package/dist/context.js.map +1 -1
- package/dist/encryption/index.cjs +276 -14
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +26 -28
- package/dist/encryption/index.d.ts +26 -28
- package/dist/encryption/index.js +268 -14
- package/dist/encryption/index.js.map +1 -1
- package/dist/encryption/named-keys.cjs +225 -14
- package/dist/encryption/named-keys.cjs.map +1 -1
- package/dist/encryption/named-keys.d.cts +7 -2
- package/dist/encryption/named-keys.d.ts +7 -2
- package/dist/encryption/named-keys.js +225 -14
- package/dist/encryption/named-keys.js.map +1 -1
- package/dist/encryption/resource.cjs.map +1 -1
- package/dist/encryption/resource.js.map +1 -1
- package/dist/feed/index.cjs +4 -0
- package/dist/feed/index.cjs.map +1 -1
- package/dist/feed/index.d.cts +3 -3
- package/dist/feed/index.d.ts +3 -3
- package/dist/feed/index.js +4 -0
- package/dist/feed/index.js.map +1 -1
- package/dist/index.cjs +509 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -12
- package/dist/index.d.ts +14 -12
- package/dist/index.js +499 -18
- package/dist/index.js.map +1 -1
- package/dist/internal/decrypt-transport-response-error.d.cts +1 -1
- package/dist/internal/decrypt-transport-response-error.d.ts +1 -1
- package/dist/kv/index.cjs +34 -0
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +4 -4
- package/dist/kv/index.d.ts +4 -4
- package/dist/kv/index.js +34 -0
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/replication/index.cjs +39 -8
- package/dist/kv/replication/index.cjs.map +1 -1
- package/dist/kv/replication/index.d.cts +3 -3
- package/dist/kv/replication/index.d.ts +3 -3
- package/dist/kv/replication/index.js +38 -7
- package/dist/kv/replication/index.js.map +1 -1
- package/dist/{scope-DEgc9f-W.d.cts → scope-DtcbyRKY.d.cts} +1 -1
- package/dist/{scope-BZr0QwxX.d.ts → scope-J20PQP3u.d.ts} +1 -1
- package/dist/sql/index.cjs +36 -0
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +4 -4
- package/dist/sql/index.d.ts +4 -4
- package/dist/sql/index.js +36 -0
- package/dist/sql/index.js.map +1 -1
- package/dist/telemetry/requestLog.cjs +4 -0
- package/dist/telemetry/requestLog.cjs.map +1 -1
- package/dist/telemetry/requestLog.d.cts +1 -1
- package/dist/telemetry/requestLog.d.ts +1 -1
- package/dist/telemetry/requestLog.js +4 -0
- package/dist/telemetry/requestLog.js.map +1 -1
- package/dist/{types-D4XbIfFJ.d.cts → types-DfqNz9Nu.d.cts} +135 -6
- package/dist/{types-CSd0RYir.d.ts → types-DjAP8dve.d.cts} +4 -0
- package/dist/{types-CSd0RYir.d.cts → types-DjAP8dve.d.ts} +4 -0
- package/dist/{types-Bf91gup6.d.ts → types-Nvi_RckJ.d.ts} +135 -6
- package/package.json +4 -1
|
@@ -120,9 +120,9 @@ function base64Decode(s) {
|
|
|
120
120
|
function utf8Encode(s) {
|
|
121
121
|
return new TextEncoder().encode(s);
|
|
122
122
|
}
|
|
123
|
-
function canonicalHashHex(
|
|
123
|
+
function canonicalHashHex(sha2562, value) {
|
|
124
124
|
const canonical = canonicalize(value);
|
|
125
|
-
return hexEncode(
|
|
125
|
+
return hexEncode(sha2562(utf8Encode(canonical)));
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// src/encryption/networkId.ts
|
|
@@ -170,6 +170,26 @@ function parseNetworkId(networkId) {
|
|
|
170
170
|
}
|
|
171
171
|
return { networkId, ownerDid, name };
|
|
172
172
|
}
|
|
173
|
+
function parseEncryptionResource(resource) {
|
|
174
|
+
const networkSeparator = resource.lastIndexOf(":");
|
|
175
|
+
const nameAndPath = resource.slice(networkSeparator + 1);
|
|
176
|
+
const pathSeparator = nameAndPath.indexOf("/");
|
|
177
|
+
const name = pathSeparator === -1 ? nameAndPath : nameAndPath.slice(0, pathSeparator);
|
|
178
|
+
const networkId = pathSeparator === -1 ? resource : `${resource.slice(0, networkSeparator + 1)}${name}`;
|
|
179
|
+
const parsed = parseNetworkId(networkId);
|
|
180
|
+
if (pathSeparator === -1) return parsed;
|
|
181
|
+
const keyPath = nameAndPath.slice(pathSeparator + 1);
|
|
182
|
+
const segments = keyPath.split("/");
|
|
183
|
+
const prefix = keyPath.endsWith("/");
|
|
184
|
+
if (keyPath.length === 0 || /[\u0000-\u001f\u007f\\]/.test(keyPath) || segments.some(
|
|
185
|
+
(segment, index) => segment === "." || segment === ".." || segment.length === 0 && !(prefix && index === segments.length - 1)
|
|
186
|
+
)) {
|
|
187
|
+
throw new NetworkIdError(
|
|
188
|
+
`encryption resource has an invalid keyPath (got ${JSON.stringify(resource)})`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return { ...parsed, keyPath };
|
|
192
|
+
}
|
|
173
193
|
var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
|
|
174
194
|
|
|
175
195
|
// src/encryption/receiverKey.ts
|
|
@@ -205,6 +225,8 @@ function defaultEncryptionMessage(input) {
|
|
|
205
225
|
return input.message ?? input.cause.message;
|
|
206
226
|
case "INVALID_INPUT":
|
|
207
227
|
return input.message;
|
|
228
|
+
default:
|
|
229
|
+
return input.message ?? input.code;
|
|
208
230
|
}
|
|
209
231
|
}
|
|
210
232
|
function encryptionError(input) {
|
|
@@ -215,6 +237,152 @@ function encryptionError(input) {
|
|
|
215
237
|
};
|
|
216
238
|
}
|
|
217
239
|
|
|
240
|
+
// src/encryption/keyWrap.ts
|
|
241
|
+
var import_aes = require("@noble/ciphers/aes");
|
|
242
|
+
var import_ed25519 = require("@noble/curves/ed25519");
|
|
243
|
+
var import_hkdf = require("@noble/hashes/hkdf");
|
|
244
|
+
var import_sha256 = require("@noble/hashes/sha256");
|
|
245
|
+
var import_utils = require("@noble/hashes/utils");
|
|
246
|
+
var KEY_WRAP_V2_ALG = "x25519-hkdf-aes256gcm/v2";
|
|
247
|
+
var MAGIC = utf8Encode("TCW2");
|
|
248
|
+
var INFO = utf8Encode("tinycloud.encryption.key-wrap/v2");
|
|
249
|
+
var MAX_HEADER = 65536;
|
|
250
|
+
function concat(...parts) {
|
|
251
|
+
const result = new Uint8Array(parts.reduce((length, part) => length + part.length, 0));
|
|
252
|
+
let offset = 0;
|
|
253
|
+
for (const part of parts) {
|
|
254
|
+
result.set(part, offset);
|
|
255
|
+
offset += part.length;
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
function equal(a, b) {
|
|
260
|
+
if (a.length !== b.length) return false;
|
|
261
|
+
let difference = 0;
|
|
262
|
+
for (let i = 0; i < a.length; i++) difference |= a[i] ^ b[i];
|
|
263
|
+
return difference === 0;
|
|
264
|
+
}
|
|
265
|
+
function decode32(value) {
|
|
266
|
+
if (typeof value !== "string") throw new Error("Invalid key wrap encoding");
|
|
267
|
+
const decoded = base64Decode(value);
|
|
268
|
+
if (decoded.length !== 32 || base64Encode(decoded) !== value) throw new Error("Invalid key wrap encoding");
|
|
269
|
+
return decoded;
|
|
270
|
+
}
|
|
271
|
+
function isWellFormed(value) {
|
|
272
|
+
for (let i = 0; i < value.length; i++) {
|
|
273
|
+
const unit = value.charCodeAt(i);
|
|
274
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
275
|
+
const next = value.charCodeAt(++i);
|
|
276
|
+
if (!(next >= 56320 && next <= 57343)) return false;
|
|
277
|
+
} else if (unit >= 56320 && unit <= 57343) return false;
|
|
278
|
+
}
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
function validateContext(context) {
|
|
282
|
+
if (context.purpose !== "network-key" && context.purpose !== "receiver-key" || typeof context.networkId !== "string" || typeof context.keyPath !== "string" || !isWellFormed(context.networkId) || !isWellFormed(context.keyPath) || !Number.isSafeInteger(context.keyVersion) || context.keyVersion <= 0 || !context.metadata || typeof context.metadata !== "object" || Array.isArray(context.metadata) || Object.entries(context.metadata).some(([key, value]) => !isWellFormed(key) || typeof value !== "string" || !isWellFormed(value))) {
|
|
283
|
+
throw new Error("Invalid key wrap context");
|
|
284
|
+
}
|
|
285
|
+
if (context.networkId !== "") {
|
|
286
|
+
const resource = parseEncryptionResource(context.keyPath === "" ? context.networkId : `${context.networkId}/${context.keyPath}`);
|
|
287
|
+
if (resource.networkId !== context.networkId || (resource.keyPath ?? "") !== context.keyPath || context.keyPath.endsWith("/")) {
|
|
288
|
+
throw new Error("Invalid key wrap resource");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function isKeyWrapV2(wrapped) {
|
|
293
|
+
return wrapped.length >= 4 && MAGIC.every((byte, index) => wrapped[index] === byte);
|
|
294
|
+
}
|
|
295
|
+
function readKeyWrapV2(wrapped) {
|
|
296
|
+
if (!isKeyWrapV2(wrapped) || wrapped.length < 8) throw new Error("Invalid v2 key wrap frame");
|
|
297
|
+
const length = new DataView(wrapped.buffer, wrapped.byteOffset, wrapped.byteLength).getUint32(4);
|
|
298
|
+
if (length === 0 || length > MAX_HEADER || wrapped.length < 8 + length + 12 + 16) throw new Error("Invalid v2 key wrap length");
|
|
299
|
+
const bytes = wrapped.subarray(8, 8 + length);
|
|
300
|
+
const header = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
301
|
+
const fields = ["v", "alg", "kdf", "aead", "salt", "ephemeralPublicKey", "recipientPublicKey", "purpose", "networkId", "keyPath", "keyVersion", "metadata"].sort();
|
|
302
|
+
if (!header || typeof header !== "object" || Array.isArray(header) || Object.keys(header).sort().join(",") !== fields.join(",") || header.v !== 2 || header.alg !== KEY_WRAP_V2_ALG || header.kdf !== "hkdf-sha256" || header.aead !== "aes-256-gcm") {
|
|
303
|
+
throw new Error("Invalid v2 key wrap header");
|
|
304
|
+
}
|
|
305
|
+
validateContext(header);
|
|
306
|
+
decode32(header.salt);
|
|
307
|
+
decode32(header.ephemeralPublicKey);
|
|
308
|
+
decode32(header.recipientPublicKey);
|
|
309
|
+
if (!equal(bytes, utf8Encode(canonicalize({ ...header })))) throw new Error("Non-canonical v2 key wrap header");
|
|
310
|
+
const end = 8 + length;
|
|
311
|
+
return { header, aad: wrapped.subarray(0, end), nonce: wrapped.subarray(end, end + 12), ciphertext: wrapped.subarray(end + 12) };
|
|
312
|
+
}
|
|
313
|
+
function assertKeyWrapContext(header, expected) {
|
|
314
|
+
validateContext(expected);
|
|
315
|
+
for (const field of ["purpose", "networkId", "keyPath", "keyVersion"]) {
|
|
316
|
+
if (header[field] !== expected[field]) throw new Error("Key wrap context mismatch");
|
|
317
|
+
}
|
|
318
|
+
if (canonicalize(header.metadata) !== canonicalize(expected.metadata)) throw new Error("Key wrap metadata mismatch");
|
|
319
|
+
}
|
|
320
|
+
function deriveKey(secret, ephemeral, recipient, salt) {
|
|
321
|
+
const shared = import_ed25519.x25519.getSharedSecret(secret, ephemeral);
|
|
322
|
+
if (shared.every((byte) => byte === 0)) throw new Error("Invalid key agreement");
|
|
323
|
+
try {
|
|
324
|
+
return (0, import_hkdf.hkdf)(import_sha256.sha256, shared, salt, concat(INFO, ephemeral, recipient), 32);
|
|
325
|
+
} finally {
|
|
326
|
+
shared.fill(0);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function sealKeyV2(recipientPublicKey, plaintext, context) {
|
|
330
|
+
validateContext(context);
|
|
331
|
+
if (recipientPublicKey.length !== 32) throw new Error("Invalid recipient public key");
|
|
332
|
+
const secret = (0, import_utils.randomBytes)(32);
|
|
333
|
+
const ephemeral = import_ed25519.x25519.getPublicKey(secret);
|
|
334
|
+
const salt = (0, import_utils.randomBytes)(32);
|
|
335
|
+
const header = {
|
|
336
|
+
...context,
|
|
337
|
+
v: 2,
|
|
338
|
+
alg: KEY_WRAP_V2_ALG,
|
|
339
|
+
kdf: "hkdf-sha256",
|
|
340
|
+
aead: "aes-256-gcm",
|
|
341
|
+
salt: base64Encode(salt),
|
|
342
|
+
ephemeralPublicKey: base64Encode(ephemeral),
|
|
343
|
+
recipientPublicKey: base64Encode(recipientPublicKey)
|
|
344
|
+
};
|
|
345
|
+
const bytes = utf8Encode(canonicalize({ ...header }));
|
|
346
|
+
if (bytes.length > MAX_HEADER) throw new Error("Key wrap header too large");
|
|
347
|
+
const prefix = new Uint8Array(8);
|
|
348
|
+
prefix.set(MAGIC);
|
|
349
|
+
new DataView(prefix.buffer).setUint32(4, bytes.length);
|
|
350
|
+
const aad = concat(prefix, bytes);
|
|
351
|
+
const nonce = (0, import_utils.randomBytes)(12);
|
|
352
|
+
let key;
|
|
353
|
+
try {
|
|
354
|
+
const shared = import_ed25519.x25519.getSharedSecret(secret, recipientPublicKey);
|
|
355
|
+
try {
|
|
356
|
+
if (shared.every((byte) => byte === 0)) throw new Error("Invalid key agreement");
|
|
357
|
+
key = (0, import_hkdf.hkdf)(import_sha256.sha256, shared, salt, concat(INFO, ephemeral, recipientPublicKey), 32);
|
|
358
|
+
} finally {
|
|
359
|
+
shared.fill(0);
|
|
360
|
+
}
|
|
361
|
+
return concat(aad, nonce, (0, import_aes.gcm)(key, nonce, aad).encrypt(plaintext));
|
|
362
|
+
} finally {
|
|
363
|
+
secret.fill(0);
|
|
364
|
+
key?.fill(0);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function openKeyV2(recipientPrivateKey, wrapped, expected) {
|
|
368
|
+
const { header, aad, nonce, ciphertext } = readKeyWrapV2(wrapped);
|
|
369
|
+
assertKeyWrapContext(header, expected);
|
|
370
|
+
const recipient = import_ed25519.x25519.getPublicKey(recipientPrivateKey);
|
|
371
|
+
if (!equal(recipient, decode32(header.recipientPublicKey))) throw new Error("Key wrap recipient mismatch");
|
|
372
|
+
const key = deriveKey(recipientPrivateKey, decode32(header.ephemeralPublicKey), recipient, decode32(header.salt));
|
|
373
|
+
try {
|
|
374
|
+
return (0, import_aes.gcm)(key, nonce, aad).decrypt(ciphertext);
|
|
375
|
+
} finally {
|
|
376
|
+
key.fill(0);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function assertKeyWrapAlgorithm(wrapped, alg, requireV2 = false) {
|
|
380
|
+
if (alg !== "x25519-aes256gcm/v1" && alg !== KEY_WRAP_V2_ALG) throw new Error("Unsupported key wrap algorithm");
|
|
381
|
+
if (requireV2 && alg !== KEY_WRAP_V2_ALG) throw new Error("V2 network key wrap required");
|
|
382
|
+
if (isKeyWrapV2(wrapped) !== (alg === KEY_WRAP_V2_ALG)) throw new Error("Key wrap algorithm mismatch");
|
|
383
|
+
if (alg === KEY_WRAP_V2_ALG) readKeyWrapV2(wrapped);
|
|
384
|
+
}
|
|
385
|
+
|
|
218
386
|
// src/encryption/response.ts
|
|
219
387
|
function canonicalSignedResponse(response) {
|
|
220
388
|
const { nodeSignature: _drop, ...rest } = response;
|
|
@@ -258,7 +426,7 @@ function verifyDecryptResponse(input) {
|
|
|
258
426
|
})
|
|
259
427
|
};
|
|
260
428
|
}
|
|
261
|
-
if (response.alg !==
|
|
429
|
+
if (response.alg !== "x25519-aes256gcm/v1" && response.alg !== KEY_WRAP_V2_ALG) {
|
|
262
430
|
return {
|
|
263
431
|
ok: false,
|
|
264
432
|
error: encryptionError({
|
|
@@ -315,7 +483,7 @@ function verifyDecryptResponse(input) {
|
|
|
315
483
|
})
|
|
316
484
|
};
|
|
317
485
|
}
|
|
318
|
-
if (facts.encryptedSymmetricKeyHash !== response.encryptedSymmetricKeyHash || facts.receiverPublicKeyHash !== response.receiverPublicKeyHash || facts.networkId !== response.networkId || facts.targetNode !== response.targetNode || facts.alg !==
|
|
486
|
+
if (facts.encryptedSymmetricKeyHash !== response.encryptedSymmetricKeyHash || facts.receiverPublicKeyHash !== response.receiverPublicKeyHash || facts.networkId !== response.networkId || facts.targetNode !== response.targetNode || facts.alg !== request.alg || facts.keyVersion !== response.keyVersion) {
|
|
319
487
|
return {
|
|
320
488
|
ok: false,
|
|
321
489
|
error: encryptionError({
|
|
@@ -338,11 +506,31 @@ function verifyDecryptResponse(input) {
|
|
|
338
506
|
}
|
|
339
507
|
return { ok: true, data: response };
|
|
340
508
|
}
|
|
341
|
-
function openWrappedKey(crypto, receiverPrivateKey, response) {
|
|
509
|
+
function openWrappedKey(crypto, receiverPrivateKey, response, expected) {
|
|
342
510
|
const wrapped = base64Decode(response.wrappedKey);
|
|
511
|
+
assertKeyWrapAlgorithm(wrapped, response.alg);
|
|
512
|
+
if (response.alg === KEY_WRAP_V2_ALG) {
|
|
513
|
+
if (!expected) throw new Error("V2 response requires expected key context");
|
|
514
|
+
return openKeyV2(receiverPrivateKey, wrapped, expected);
|
|
515
|
+
}
|
|
343
516
|
return crypto.openWithReceiverKey(receiverPrivateKey, wrapped);
|
|
344
517
|
}
|
|
345
518
|
|
|
519
|
+
// src/encryption/envelope.ts
|
|
520
|
+
function validateEnvelopeKeyWrap(envelope, requireKeyWrapV2 = false) {
|
|
521
|
+
const wrapped = base64Decode(envelope.encryptedSymmetricKey);
|
|
522
|
+
assertKeyWrapAlgorithm(wrapped, envelope.alg, requireKeyWrapV2);
|
|
523
|
+
if (envelope.alg === KEY_WRAP_V2_ALG) {
|
|
524
|
+
assertKeyWrapContext(readKeyWrapV2(wrapped).header, {
|
|
525
|
+
purpose: "network-key",
|
|
526
|
+
networkId: envelope.networkId,
|
|
527
|
+
keyPath: envelope.keyPath ?? "",
|
|
528
|
+
keyVersion: envelope.keyVersion,
|
|
529
|
+
metadata: envelope.metadata ?? {}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
346
534
|
// src/encryption/resource.ts
|
|
347
535
|
var import_bootstrap = require("@tinycloud/bootstrap");
|
|
348
536
|
var SEGMENT_SOURCE = "[a-z0-9][a-z0-9._-]{0,63}";
|
|
@@ -425,7 +613,7 @@ function assertPath(path, what) {
|
|
|
425
613
|
);
|
|
426
614
|
}
|
|
427
615
|
}
|
|
428
|
-
function
|
|
616
|
+
function parseEncryptionResource2(uri) {
|
|
429
617
|
if (typeof uri !== "string" || !uri.startsWith(ENCRYPTION_NETWORK_URN_PREFIX)) {
|
|
430
618
|
throw new EncryptionResourceError(
|
|
431
619
|
"not_encryption_urn",
|
|
@@ -486,7 +674,7 @@ function canonicalNetworkId(networkId) {
|
|
|
486
674
|
return parsed.networkId;
|
|
487
675
|
}
|
|
488
676
|
function parseBuilt(resource, networkId) {
|
|
489
|
-
if (
|
|
677
|
+
if (parseEncryptionResource2(resource).networkId !== networkId) {
|
|
490
678
|
throw new EncryptionResourceError(
|
|
491
679
|
"malformed",
|
|
492
680
|
`built resource does not round-trip (got ${JSON.stringify(resource)})`
|
|
@@ -585,7 +773,7 @@ function mapNodeKeyError(status, text, context) {
|
|
|
585
773
|
function requireKeyTarget(resource) {
|
|
586
774
|
let parsed;
|
|
587
775
|
try {
|
|
588
|
-
parsed =
|
|
776
|
+
parsed = parseEncryptionResource2(resource);
|
|
589
777
|
} catch (error) {
|
|
590
778
|
throw new NamedKeyError(
|
|
591
779
|
"INVALID_KEY_TARGET",
|
|
@@ -887,16 +1075,21 @@ var NamedKeyClient = class {
|
|
|
887
1075
|
const { crypto } = this.config;
|
|
888
1076
|
const symmetricKey = crypto.randomBytes(32);
|
|
889
1077
|
const ciphertext = crypto.authEncrypt(symmetricKey, plaintext, options?.aad);
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1078
|
+
const keyWrapV2 = options?.keyWrapV2 ?? this.config.keyWrapV2 ?? false;
|
|
1079
|
+
const publicKey = base64Decode(descriptor.publicEncryptionKey);
|
|
1080
|
+
const wrapped = keyWrapV2 ? sealKeyV2(publicKey, symmetricKey, {
|
|
1081
|
+
purpose: "network-key",
|
|
1082
|
+
networkId: descriptor.networkId,
|
|
1083
|
+
keyPath: descriptor.keyPath,
|
|
1084
|
+
keyVersion: descriptor.keyVersion,
|
|
1085
|
+
metadata: options?.metadata ?? {}
|
|
1086
|
+
}) : crypto.sealToNetworkKey(publicKey, symmetricKey);
|
|
894
1087
|
const encryptedSymmetricKey = base64Encode(wrapped);
|
|
895
1088
|
return {
|
|
896
1089
|
v: NAMED_KEY_ENVELOPE_VERSION,
|
|
897
1090
|
networkId: descriptor.networkId,
|
|
898
1091
|
keyPath: descriptor.keyPath,
|
|
899
|
-
alg:
|
|
1092
|
+
alg: keyWrapV2 ? KEY_WRAP_V2_ALG : DEFAULT_ENCRYPTION_ALG,
|
|
900
1093
|
keyVersion: descriptor.keyVersion,
|
|
901
1094
|
encryptedSymmetricKey,
|
|
902
1095
|
encryptedSymmetricKeyHash: canonicalHashHex(crypto.sha256, encryptedSymmetricKey),
|
|
@@ -941,6 +1134,13 @@ var NamedKeyClient = class {
|
|
|
941
1134
|
return results;
|
|
942
1135
|
}
|
|
943
1136
|
async decryptRoot(envelope) {
|
|
1137
|
+
if (this.config.requireKeyWrapV2) {
|
|
1138
|
+
try {
|
|
1139
|
+
validateEnvelopeKeyWrap(envelope, true);
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
throw new NamedKeyError("INVALID_ENVELOPE", "Envelope key wrap context is invalid", { cause: error });
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
944
1144
|
if (this.config.decryptRoot === void 0) {
|
|
945
1145
|
throw new NamedKeyError("DECRYPT_FAILED", "no root-key decrypt path is configured");
|
|
946
1146
|
}
|
|
@@ -977,6 +1177,11 @@ var NamedKeyClient = class {
|
|
|
977
1177
|
"envelope.encryptedSymmetricKeyHash does not match encryptedSymmetricKey"
|
|
978
1178
|
);
|
|
979
1179
|
}
|
|
1180
|
+
try {
|
|
1181
|
+
validateEnvelopeKeyWrap(e, this.config.requireKeyWrapV2 ?? false);
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
throw new NamedKeyError("INVALID_ENVELOPE", "Envelope key wrap context is invalid", { cause: error });
|
|
1184
|
+
}
|
|
980
1185
|
return e;
|
|
981
1186
|
}
|
|
982
1187
|
async decryptKey(envelope) {
|
|
@@ -1038,7 +1243,13 @@ var NamedKeyClient = class {
|
|
|
1038
1243
|
if (!verified.ok) {
|
|
1039
1244
|
throw new NamedKeyError("INVALID_RESPONSE", safeEcho(verified.error.message), { cause: verified.error });
|
|
1040
1245
|
}
|
|
1041
|
-
const symmetricKey = openWrappedKey(crypto, receiverKey.privateKey, verified.data
|
|
1246
|
+
const symmetricKey = openWrappedKey(crypto, receiverKey.privateKey, verified.data, {
|
|
1247
|
+
purpose: "receiver-key",
|
|
1248
|
+
networkId: body.networkId,
|
|
1249
|
+
keyPath: body.keyPath,
|
|
1250
|
+
keyVersion: body.keyVersion,
|
|
1251
|
+
metadata: {}
|
|
1252
|
+
});
|
|
1042
1253
|
try {
|
|
1043
1254
|
return crypto.authDecrypt(
|
|
1044
1255
|
symmetricKey,
|