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