@ajna-inc/vaults 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +202 -0
  2. package/build/VaultsApi.js +263 -0
  3. package/build/VaultsEvents.js +19 -0
  4. package/build/VaultsModule.js +71 -0
  5. package/build/crypto/wasm/VaultCrypto.js +70 -0
  6. package/build/errors/BadSuiteError.js +34 -0
  7. package/build/errors/DecryptAeadError.js +34 -0
  8. package/build/errors/DecryptKemError.js +34 -0
  9. package/build/errors/PolicyError.js +34 -0
  10. package/build/errors/VaultError.js +77 -0
  11. package/build/errors/index.js +16 -0
  12. package/build/index.js +119 -0
  13. package/build/messages/CreateVaultMessage.js +126 -0
  14. package/build/messages/DeleteVaultMessage.js +114 -0
  15. package/build/messages/DenyAccessMessage.js +114 -0
  16. package/build/messages/DenyShareMessage.js +120 -0
  17. package/build/messages/GrantAccessMessage.js +126 -0
  18. package/build/messages/ProvideShareMessage.js +126 -0
  19. package/build/messages/RequestAccessMessage.js +120 -0
  20. package/build/messages/RequestShareMessage.js +120 -0
  21. package/build/messages/RetrieveVaultMessage.js +108 -0
  22. package/build/messages/StoreVaultMessage.js +114 -0
  23. package/build/messages/UpdateVaultMessage.js +120 -0
  24. package/build/messages/VaultCreatedAckMessage.js +108 -0
  25. package/build/messages/VaultDataMessage.js +121 -0
  26. package/build/messages/VaultProblemReportMessage.js +124 -0
  27. package/build/messages/VaultStoredAckMessage.js +115 -0
  28. package/build/messages/index.js +36 -0
  29. package/build/models/ThresholdSession.js +24 -0
  30. package/build/models/VaultDocument.js +28 -0
  31. package/build/models/VaultHeader.js +31 -0
  32. package/build/models/VaultPolicy.js +29 -0
  33. package/build/models/index.js +20 -0
  34. package/build/repository/ThresholdSessionRecord.js +117 -0
  35. package/build/repository/ThresholdSessionRepository.js +216 -0
  36. package/build/repository/VaultRecord.js +128 -0
  37. package/build/repository/VaultRepository.js +200 -0
  38. package/build/repository/index.js +13 -0
  39. package/build/services/VaultEncryptionService.js +613 -0
  40. package/build/services/VaultService.js +398 -0
  41. package/build/services/index.js +8 -0
  42. package/package.json +45 -0
  43. package/wasm/README.md +166 -0
  44. package/wasm/package.json +16 -0
  45. package/wasm/vault_crypto.d.ts +526 -0
  46. package/wasm/vault_crypto.js +2137 -0
  47. package/wasm/vault_crypto_bg.wasm +0 -0
  48. package/wasm/vault_crypto_bg.wasm.d.ts +66 -0
@@ -0,0 +1,613 @@
1
+ "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __generator = (this && this.__generator) || function (thisArg, body) {
46
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48
+ function verb(n) { return function (v) { return step([n, v]); }; }
49
+ function step(op) {
50
+ if (f) throw new TypeError("Generator is already executing.");
51
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
52
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
53
+ if (y = 0, t) op = [op[0] & 2, t.value];
54
+ switch (op[0]) {
55
+ case 0: case 1: t = op; break;
56
+ case 4: _.label++; return { value: op[1], done: false };
57
+ case 5: _.label++; y = op[1]; op = [0]; continue;
58
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
59
+ default:
60
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64
+ if (t[2]) _.ops.pop();
65
+ _.trys.pop(); continue;
66
+ }
67
+ op = body.call(thisArg, _);
68
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70
+ }
71
+ };
72
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
73
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
74
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
75
+ };
76
+ Object.defineProperty(exports, "__esModule", { value: true });
77
+ exports.VaultEncryptionService = void 0;
78
+ var core_1 = require("@credo-ts/core");
79
+ var hash_wasm_1 = require("hash-wasm");
80
+ var VaultCrypto_1 = require("../crypto/wasm/VaultCrypto");
81
+ var errors_1 = require("../errors");
82
+ // ═══════════════════════════════════════════════════════════════════════════
83
+ // Service
84
+ // ═══════════════════════════════════════════════════════════════════════════
85
+ /**
86
+ * Vault Encryption Service
87
+ *
88
+ * Provides client-side encryption/decryption for all vault policy modes:
89
+ * - S3 suite: Passphrase-based (Argon2id → AES-256-GCM)
90
+ * - P1 suite: Post-quantum (ML-KEM-768 → AES-256-GCM)
91
+ *
92
+ * All encryption and decryption happens client-side per ZK-Vault spec.
93
+ */
94
+ var VaultEncryptionService = function () {
95
+ var _classDecorators = [(0, core_1.injectable)()];
96
+ var _classDescriptor;
97
+ var _classExtraInitializers = [];
98
+ var _classThis;
99
+ var VaultEncryptionService = _classThis = /** @class */ (function () {
100
+ function VaultEncryptionService_1() {
101
+ }
102
+ // ═══════════════════════════════════════════════════════════════════════
103
+ // S3 SUITE (PASSPHRASE) - For Local Storage
104
+ // Spec §4.1: CEK = Argon2id(passphrase, salt, m=64-128MiB, t=2-3)
105
+ // ═══════════════════════════════════════════════════════════════════════
106
+ /**
107
+ * Encrypt data with passphrase (S3 suite)
108
+ * All encryption happens CLIENT-SIDE
109
+ *
110
+ * @param plaintext - Data to encrypt
111
+ * @param passphrase - User passphrase
112
+ * @param options - Encryption options
113
+ * @returns Encrypted vault document
114
+ */
115
+ VaultEncryptionService_1.prototype.encryptWithPassphrase = function (plaintext, passphrase, options) {
116
+ var _a, _b, _c, _d;
117
+ if (options === void 0) { options = {}; }
118
+ return __awaiter(this, void 0, void 0, function () {
119
+ var salt, argon2Params, cek, nonce, docId, vaultId, epoch, aadFields, aad, encResult, ciphertext, kcmp, header;
120
+ return __generator(this, function (_e) {
121
+ switch (_e.label) {
122
+ case 0:
123
+ salt = (0, VaultCrypto_1.randomBytes)(32);
124
+ argon2Params = {
125
+ memory: (_a = options.memory) !== null && _a !== void 0 ? _a : 65536, // 64 MiB
126
+ iterations: (_b = options.iterations) !== null && _b !== void 0 ? _b : 3,
127
+ parallelism: 1,
128
+ };
129
+ return [4 /*yield*/, this.deriveKeyArgon2(passphrase, salt, argon2Params)
130
+ // 3. Generate nonce (12 bytes for AES-GCM)
131
+ ];
132
+ case 1:
133
+ cek = _e.sent();
134
+ nonce = (0, VaultCrypto_1.generateNonceAesGcm)();
135
+ docId = (_c = options.docId) !== null && _c !== void 0 ? _c : (0, VaultCrypto_1.generateUuid)();
136
+ vaultId = (_d = options.vaultId) !== null && _d !== void 0 ? _d : docId;
137
+ epoch = 1;
138
+ aadFields = {
139
+ v: 1,
140
+ suite: 'S3',
141
+ aead: 'AES-256-GCM',
142
+ docId: docId,
143
+ vaultId: vaultId,
144
+ epoch: epoch,
145
+ policy: { mode: 'passphrase' },
146
+ };
147
+ aad = (0, VaultCrypto_1.canonicalAad)(aadFields);
148
+ encResult = (0, VaultCrypto_1.aesGcmEncrypt)(cek, nonce, plaintext, aad);
149
+ ciphertext = encResult.ciphertext();
150
+ kcmp = (0, VaultCrypto_1.keyCommitment)(cek);
151
+ header = {
152
+ v: 1,
153
+ suite: 'S3',
154
+ aead: 'AES-256-GCM',
155
+ docId: docId,
156
+ vaultId: vaultId,
157
+ epoch: epoch,
158
+ nonce: (0, VaultCrypto_1.toBase64Url)(nonce),
159
+ kcmp: (0, VaultCrypto_1.toBase64Url)(kcmp),
160
+ salt: (0, VaultCrypto_1.toBase64Url)(salt),
161
+ argon2: argon2Params,
162
+ policy: { mode: 'passphrase' },
163
+ };
164
+ // 9. Zero out CEK from memory (security best practice)
165
+ this.zeroize(cek);
166
+ return [2 /*return*/, { header: header, ciphertext: ciphertext }];
167
+ }
168
+ });
169
+ });
170
+ };
171
+ /**
172
+ * Decrypt passphrase-protected vault (S3 suite)
173
+ * All decryption happens CLIENT-SIDE
174
+ *
175
+ * @param vault - Encrypted vault document
176
+ * @param passphrase - User passphrase
177
+ * @returns Decrypted plaintext
178
+ */
179
+ VaultEncryptionService_1.prototype.decryptWithPassphrase = function (vault, passphrase) {
180
+ return __awaiter(this, void 0, void 0, function () {
181
+ var header, ciphertext, salt, cek, expectedKcmp, aad, nonce, plaintext;
182
+ return __generator(this, function (_a) {
183
+ switch (_a.label) {
184
+ case 0:
185
+ header = vault.header, ciphertext = vault.ciphertext;
186
+ // 1. Validate suite
187
+ if (header.suite !== 'S3' && header.suite !== 'S1') {
188
+ throw new errors_1.BadSuiteError("Expected S1/S3 suite for passphrase vault, got ".concat(header.suite));
189
+ }
190
+ if (!header.salt || !header.argon2) {
191
+ throw new errors_1.PolicyError('Missing salt or argon2 params for passphrase vault');
192
+ }
193
+ salt = (0, VaultCrypto_1.fromBase64Url)(header.salt);
194
+ return [4 /*yield*/, this.deriveKeyArgon2(passphrase, salt, header.argon2)
195
+ // 3. Verify key commitment (spec §9.2 step 3)
196
+ ];
197
+ case 1:
198
+ cek = _a.sent();
199
+ expectedKcmp = (0, VaultCrypto_1.fromBase64Url)(header.kcmp);
200
+ if (!(0, VaultCrypto_1.verifyKeyCommitment)(cek, expectedKcmp)) {
201
+ this.zeroize(cek);
202
+ throw new errors_1.DecryptAeadError('Key commitment verification failed - wrong passphrase');
203
+ }
204
+ aad = (0, VaultCrypto_1.canonicalAad)({
205
+ v: header.v,
206
+ suite: header.suite,
207
+ aead: header.aead,
208
+ docId: header.docId,
209
+ vaultId: header.vaultId,
210
+ epoch: header.epoch,
211
+ policy: header.policy,
212
+ });
213
+ nonce = (0, VaultCrypto_1.fromBase64Url)(header.nonce);
214
+ try {
215
+ plaintext = (0, VaultCrypto_1.aesGcmDecrypt)(cek, nonce, ciphertext, aad);
216
+ this.zeroize(cek);
217
+ return [2 /*return*/, plaintext];
218
+ }
219
+ catch (e) {
220
+ this.zeroize(cek);
221
+ throw new errors_1.DecryptAeadError('AEAD decryption failed - data may be corrupted or tampered');
222
+ }
223
+ return [2 /*return*/];
224
+ }
225
+ });
226
+ });
227
+ };
228
+ // ═══════════════════════════════════════════════════════════════════════
229
+ // P1 SUITE (POST-QUANTUM) - For Multi-Party Sharing
230
+ // Spec §4.1: KEM = ML-KEM-768, AEAD = AES-256-GCM
231
+ // ═══════════════════════════════════════════════════════════════════════
232
+ /**
233
+ * Encrypt with any-of policy (spec §6.1)
234
+ * Each recipient gets their own KEM-wrapped CEK
235
+ *
236
+ * @param plaintext - Data to encrypt
237
+ * @param recipients - Array of recipient public keys
238
+ * @param options - Encryption options
239
+ * @returns Encrypted vault document
240
+ */
241
+ VaultEncryptionService_1.prototype.encryptAnyOf = function (plaintext, recipients, options) {
242
+ var _a, _b;
243
+ if (options === void 0) { options = {}; }
244
+ return __awaiter(this, void 0, void 0, function () {
245
+ var cek, nonce, docId, vaultId, epoch, aad, encResult, ciphertext, recipientWraps, kcmp, header;
246
+ return __generator(this, function (_c) {
247
+ if (recipients.length === 0) {
248
+ throw new errors_1.PolicyError('At least one recipient required for any-of policy');
249
+ }
250
+ cek = (0, VaultCrypto_1.generateCek)();
251
+ nonce = (0, VaultCrypto_1.generateNonceAesGcm)();
252
+ docId = (_a = options.docId) !== null && _a !== void 0 ? _a : (0, VaultCrypto_1.generateUuid)();
253
+ vaultId = (_b = options.vaultId) !== null && _b !== void 0 ? _b : docId;
254
+ epoch = 1;
255
+ aad = (0, VaultCrypto_1.canonicalAad)({
256
+ v: 1,
257
+ suite: 'P1',
258
+ aead: 'AES-256-GCM',
259
+ docId: docId,
260
+ vaultId: vaultId,
261
+ epoch: epoch,
262
+ policy: { mode: 'any-of' },
263
+ });
264
+ encResult = (0, VaultCrypto_1.aesGcmEncrypt)(cek, nonce, plaintext, aad);
265
+ ciphertext = encResult.ciphertext();
266
+ recipientWraps = recipients.map(function (r) { return ({
267
+ kid: r.kid,
268
+ kem: 'ML-KEM-768',
269
+ ct: (0, VaultCrypto_1.toBase64Url)((0, VaultCrypto_1.kemWrapCek)(cek, r.publicKey)),
270
+ }); });
271
+ kcmp = (0, VaultCrypto_1.keyCommitment)(cek);
272
+ header = {
273
+ v: 1,
274
+ suite: 'P1',
275
+ aead: 'AES-256-GCM',
276
+ docId: docId,
277
+ vaultId: vaultId,
278
+ epoch: epoch,
279
+ nonce: (0, VaultCrypto_1.toBase64Url)(nonce),
280
+ kcmp: (0, VaultCrypto_1.toBase64Url)(kcmp),
281
+ policy: { mode: 'any-of' },
282
+ recipients: recipientWraps,
283
+ };
284
+ this.zeroize(cek);
285
+ return [2 /*return*/, { header: header, ciphertext: ciphertext }];
286
+ });
287
+ });
288
+ };
289
+ /**
290
+ * Decrypt any-of vault (spec §9.2)
291
+ * Try the specified recipient's wrap
292
+ *
293
+ * @param vault - Encrypted vault document
294
+ * @param recipientSecretKey - Recipient's ML-KEM secret key
295
+ * @param recipientKid - Recipient's key identifier
296
+ * @returns Decrypted plaintext
297
+ */
298
+ VaultEncryptionService_1.prototype.decryptAnyOf = function (vault, recipientSecretKey, recipientKid) {
299
+ var _a;
300
+ return __awaiter(this, void 0, void 0, function () {
301
+ var header, ciphertext, recipient, wrappedCek, cek, aad, nonce, plaintext;
302
+ return __generator(this, function (_b) {
303
+ header = vault.header, ciphertext = vault.ciphertext;
304
+ if (((_a = header.policy) === null || _a === void 0 ? void 0 : _a.mode) !== 'any-of' || !header.recipients) {
305
+ throw new errors_1.PolicyError('Not an any-of policy vault');
306
+ }
307
+ recipient = header.recipients.find(function (r) { return r.kid === recipientKid; });
308
+ if (!recipient) {
309
+ throw new errors_1.DecryptKemError("No recipient wrap found for kid: ".concat(recipientKid));
310
+ }
311
+ wrappedCek = (0, VaultCrypto_1.fromBase64Url)(recipient.ct);
312
+ try {
313
+ cek = (0, VaultCrypto_1.kemUnwrapCek)(wrappedCek, recipientSecretKey);
314
+ }
315
+ catch (e) {
316
+ throw new errors_1.DecryptKemError('Failed to unwrap CEK - invalid key or corrupted wrap');
317
+ }
318
+ // Verify key commitment
319
+ if (!(0, VaultCrypto_1.verifyKeyCommitment)(cek, (0, VaultCrypto_1.fromBase64Url)(header.kcmp))) {
320
+ this.zeroize(cek);
321
+ throw new errors_1.DecryptAeadError('Key commitment verification failed');
322
+ }
323
+ aad = (0, VaultCrypto_1.canonicalAad)({
324
+ v: header.v,
325
+ suite: header.suite,
326
+ aead: header.aead,
327
+ docId: header.docId,
328
+ vaultId: header.vaultId,
329
+ epoch: header.epoch,
330
+ policy: header.policy,
331
+ });
332
+ nonce = (0, VaultCrypto_1.fromBase64Url)(header.nonce);
333
+ try {
334
+ plaintext = (0, VaultCrypto_1.aesGcmDecrypt)(cek, nonce, ciphertext, aad);
335
+ this.zeroize(cek);
336
+ return [2 /*return*/, plaintext];
337
+ }
338
+ catch (e) {
339
+ this.zeroize(cek);
340
+ throw new errors_1.DecryptAeadError('AEAD decryption failed');
341
+ }
342
+ return [2 /*return*/];
343
+ });
344
+ });
345
+ };
346
+ /**
347
+ * Encrypt with all-of policy (spec §6.2)
348
+ * Requires ALL listed keys to decrypt via HKDF-join
349
+ *
350
+ * @param plaintext - Data to encrypt
351
+ * @param participants - Array of participant public keys
352
+ * @param options - Encryption options
353
+ * @returns Encrypted vault document
354
+ */
355
+ VaultEncryptionService_1.prototype.encryptAllOf = function (plaintext, participants, options) {
356
+ var _a, _b;
357
+ if (options === void 0) { options = {}; }
358
+ return __awaiter(this, void 0, void 0, function () {
359
+ var cek, nonce, wrapNonce, docId, vaultId, epoch, keyMaterials, context, wrapKey, wrappedCekResult, aad, encResult, ciphertext, header;
360
+ return __generator(this, function (_c) {
361
+ if (participants.length < 2) {
362
+ throw new errors_1.PolicyError('At least 2 participants required for all-of policy');
363
+ }
364
+ cek = (0, VaultCrypto_1.generateCek)();
365
+ nonce = (0, VaultCrypto_1.generateNonceAesGcm)();
366
+ wrapNonce = (0, VaultCrypto_1.generateNonceAesGcm)();
367
+ docId = (_a = options.docId) !== null && _a !== void 0 ? _a : (0, VaultCrypto_1.generateUuid)();
368
+ vaultId = (_b = options.vaultId) !== null && _b !== void 0 ? _b : docId;
369
+ epoch = 1;
370
+ keyMaterials = participants.map(function (p) { return ({
371
+ kid: p.kid,
372
+ key: p.publicKey,
373
+ }); });
374
+ context = "wrap:doc:".concat(docId, ":epoch:").concat(epoch);
375
+ wrapKey = (0, VaultCrypto_1.hkdfJoin)(keyMaterials, context);
376
+ wrappedCekResult = (0, VaultCrypto_1.aesGcmEncrypt)(wrapKey, wrapNonce, cek, new Uint8Array(0));
377
+ aad = (0, VaultCrypto_1.canonicalAad)({
378
+ v: 1,
379
+ suite: 'P1',
380
+ aead: 'AES-256-GCM',
381
+ docId: docId,
382
+ vaultId: vaultId,
383
+ epoch: epoch,
384
+ policy: { mode: 'all-of' },
385
+ });
386
+ encResult = (0, VaultCrypto_1.aesGcmEncrypt)(cek, nonce, plaintext, aad);
387
+ ciphertext = encResult.ciphertext();
388
+ header = {
389
+ v: 1,
390
+ suite: 'P1',
391
+ aead: 'AES-256-GCM',
392
+ docId: docId,
393
+ vaultId: vaultId,
394
+ epoch: epoch,
395
+ nonce: (0, VaultCrypto_1.toBase64Url)(nonce),
396
+ kcmp: (0, VaultCrypto_1.toBase64Url)((0, VaultCrypto_1.keyCommitment)(cek)),
397
+ policy: { mode: 'all-of' },
398
+ wrap_all: {
399
+ alg: 'HKDF-join',
400
+ kids: participants.map(function (p) { return p.kid; }),
401
+ ct: (0, VaultCrypto_1.toBase64Url)(wrappedCekResult.ciphertext()),
402
+ },
403
+ };
404
+ this.zeroize(cek);
405
+ this.zeroize(wrapKey);
406
+ return [2 /*return*/, { header: header, ciphertext: ciphertext }];
407
+ });
408
+ });
409
+ };
410
+ /**
411
+ * Encrypt with threshold policy (spec §6.3)
412
+ * Any t-of-n participants can decrypt
413
+ *
414
+ * @param plaintext - Data to encrypt
415
+ * @param threshold - Minimum shares required (t)
416
+ * @param participants - Array of participant public keys (n)
417
+ * @param options - Encryption options
418
+ * @returns Encrypted vault document
419
+ */
420
+ VaultEncryptionService_1.prototype.encryptThreshold = function (plaintext, threshold, participants, options) {
421
+ var _a, _b;
422
+ if (options === void 0) { options = {}; }
423
+ return __awaiter(this, void 0, void 0, function () {
424
+ var n, cek, nonce, docId, vaultId, epoch, shares, thresholdShares, aad, encResult, ciphertext, header, _i, shares_1, share;
425
+ return __generator(this, function (_c) {
426
+ n = participants.length;
427
+ if (threshold < 1 || threshold > n) {
428
+ throw new errors_1.PolicyError("Invalid threshold: ".concat(threshold, " of ").concat(n));
429
+ }
430
+ cek = (0, VaultCrypto_1.generateCek)();
431
+ nonce = (0, VaultCrypto_1.generateNonceAesGcm)();
432
+ docId = (_a = options.docId) !== null && _a !== void 0 ? _a : (0, VaultCrypto_1.generateUuid)();
433
+ vaultId = (_b = options.vaultId) !== null && _b !== void 0 ? _b : docId;
434
+ epoch = 1;
435
+ shares = (0, VaultCrypto_1.shamirSplit)(cek, threshold, n);
436
+ thresholdShares = participants.map(function (p, i) { return ({
437
+ kid: p.kid,
438
+ enc_share: {
439
+ kem: 'ML-KEM-768',
440
+ ct: (0, VaultCrypto_1.toBase64Url)((0, VaultCrypto_1.kemWrapCek)(shares[i], p.publicKey)),
441
+ },
442
+ }); });
443
+ aad = (0, VaultCrypto_1.canonicalAad)({
444
+ v: 1,
445
+ suite: 'P1',
446
+ aead: 'AES-256-GCM',
447
+ docId: docId,
448
+ vaultId: vaultId,
449
+ epoch: epoch,
450
+ policy: { mode: 'threshold', t: threshold, n: n },
451
+ });
452
+ encResult = (0, VaultCrypto_1.aesGcmEncrypt)(cek, nonce, plaintext, aad);
453
+ ciphertext = encResult.ciphertext();
454
+ header = {
455
+ v: 1,
456
+ suite: 'P1',
457
+ aead: 'AES-256-GCM',
458
+ docId: docId,
459
+ vaultId: vaultId,
460
+ epoch: epoch,
461
+ nonce: (0, VaultCrypto_1.toBase64Url)(nonce),
462
+ kcmp: (0, VaultCrypto_1.toBase64Url)((0, VaultCrypto_1.keyCommitment)(cek)),
463
+ policy: { mode: 'threshold', t: threshold, n: n },
464
+ shares: thresholdShares,
465
+ };
466
+ this.zeroize(cek);
467
+ // Zero out shares
468
+ for (_i = 0, shares_1 = shares; _i < shares_1.length; _i++) {
469
+ share = shares_1[_i];
470
+ this.zeroize(share);
471
+ }
472
+ return [2 /*return*/, { header: header, ciphertext: ciphertext }];
473
+ });
474
+ });
475
+ };
476
+ /**
477
+ * Decrypt threshold vault with collected shares
478
+ *
479
+ * @param vault - Encrypted vault document
480
+ * @param decryptedShares - Array of decrypted shares (at least t shares)
481
+ * @returns Decrypted plaintext
482
+ */
483
+ VaultEncryptionService_1.prototype.decryptThreshold = function (vault, decryptedShares) {
484
+ var _a, _b;
485
+ return __awaiter(this, void 0, void 0, function () {
486
+ var header, ciphertext, threshold, shareData, cek, aad, nonce, plaintext;
487
+ return __generator(this, function (_c) {
488
+ header = vault.header, ciphertext = vault.ciphertext;
489
+ if (((_a = header.policy) === null || _a === void 0 ? void 0 : _a.mode) !== 'threshold') {
490
+ throw new errors_1.PolicyError('Not a threshold policy vault');
491
+ }
492
+ threshold = (_b = header.policy.t) !== null && _b !== void 0 ? _b : 1;
493
+ if (decryptedShares.length < threshold) {
494
+ throw new errors_1.PolicyError("Need ".concat(threshold, " shares, got ").concat(decryptedShares.length));
495
+ }
496
+ shareData = decryptedShares.map(function (s) { return ({
497
+ index: s.index,
498
+ data: s.share,
499
+ }); });
500
+ cek = (0, VaultCrypto_1.shamirReconstruct)(shareData);
501
+ // Verify key commitment
502
+ if (!(0, VaultCrypto_1.verifyKeyCommitment)(cek, (0, VaultCrypto_1.fromBase64Url)(header.kcmp))) {
503
+ this.zeroize(cek);
504
+ throw new errors_1.DecryptAeadError('Key commitment verification failed');
505
+ }
506
+ aad = (0, VaultCrypto_1.canonicalAad)({
507
+ v: header.v,
508
+ suite: header.suite,
509
+ aead: header.aead,
510
+ docId: header.docId,
511
+ vaultId: header.vaultId,
512
+ epoch: header.epoch,
513
+ policy: header.policy,
514
+ });
515
+ nonce = (0, VaultCrypto_1.fromBase64Url)(header.nonce);
516
+ try {
517
+ plaintext = (0, VaultCrypto_1.aesGcmDecrypt)(cek, nonce, ciphertext, aad);
518
+ this.zeroize(cek);
519
+ return [2 /*return*/, plaintext];
520
+ }
521
+ catch (e) {
522
+ this.zeroize(cek);
523
+ throw new errors_1.DecryptAeadError('AEAD decryption failed');
524
+ }
525
+ return [2 /*return*/];
526
+ });
527
+ });
528
+ };
529
+ /**
530
+ * Unwrap a single threshold share for a participant
531
+ *
532
+ * @param vault - Encrypted vault document
533
+ * @param participantSecretKey - Participant's ML-KEM secret key
534
+ * @param participantKid - Participant's key identifier
535
+ * @returns Decrypted share with its index
536
+ */
537
+ VaultEncryptionService_1.prototype.unwrapThresholdShare = function (vault, participantSecretKey, participantKid) {
538
+ var _a;
539
+ return __awaiter(this, void 0, void 0, function () {
540
+ var header, shareIndex, shareEntry, wrappedShare, share;
541
+ return __generator(this, function (_b) {
542
+ header = vault.header;
543
+ if (((_a = header.policy) === null || _a === void 0 ? void 0 : _a.mode) !== 'threshold' || !header.shares) {
544
+ throw new errors_1.PolicyError('Not a threshold policy vault');
545
+ }
546
+ shareIndex = header.shares.findIndex(function (s) { return s.kid === participantKid; });
547
+ if (shareIndex === -1) {
548
+ throw new errors_1.DecryptKemError("No share found for kid: ".concat(participantKid));
549
+ }
550
+ shareEntry = header.shares[shareIndex];
551
+ wrappedShare = (0, VaultCrypto_1.fromBase64Url)(shareEntry.enc_share.ct);
552
+ try {
553
+ share = (0, VaultCrypto_1.kemUnwrapCek)(wrappedShare, participantSecretKey);
554
+ }
555
+ catch (e) {
556
+ throw new errors_1.DecryptKemError('Failed to unwrap share - invalid key');
557
+ }
558
+ // Return with 1-based index (Shamir convention)
559
+ return [2 /*return*/, {
560
+ index: shareIndex + 1,
561
+ share: share,
562
+ }];
563
+ });
564
+ });
565
+ };
566
+ // ═══════════════════════════════════════════════════════════════════════
567
+ // HELPER METHODS
568
+ // ═══════════════════════════════════════════════════════════════════════
569
+ /**
570
+ * Derive key from passphrase using Argon2id
571
+ * Spec §4.1 S1/S3 suite
572
+ */
573
+ VaultEncryptionService_1.prototype.deriveKeyArgon2 = function (passphrase, salt, params) {
574
+ return __awaiter(this, void 0, void 0, function () {
575
+ var hash;
576
+ return __generator(this, function (_a) {
577
+ switch (_a.label) {
578
+ case 0: return [4 /*yield*/, (0, hash_wasm_1.argon2id)({
579
+ password: passphrase,
580
+ salt: salt,
581
+ parallelism: params.parallelism,
582
+ iterations: params.iterations,
583
+ memorySize: params.memory,
584
+ hashLength: 32,
585
+ outputType: 'binary',
586
+ })];
587
+ case 1:
588
+ hash = _a.sent();
589
+ return [2 /*return*/, new Uint8Array(hash)];
590
+ }
591
+ });
592
+ });
593
+ };
594
+ /**
595
+ * Zero out sensitive data from memory
596
+ * Security best practice to minimize exposure window
597
+ */
598
+ VaultEncryptionService_1.prototype.zeroize = function (data) {
599
+ data.fill(0);
600
+ };
601
+ return VaultEncryptionService_1;
602
+ }());
603
+ __setFunctionName(_classThis, "VaultEncryptionService");
604
+ (function () {
605
+ var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
606
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
607
+ VaultEncryptionService = _classThis = _classDescriptor.value;
608
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
609
+ __runInitializers(_classThis, _classExtraInitializers);
610
+ })();
611
+ return VaultEncryptionService = _classThis;
612
+ }();
613
+ exports.VaultEncryptionService = VaultEncryptionService;