@push.rocks/smartsecret 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.smartconfig.json +1 -1
- package/dist_rust/smartsecret-kernel_linux_amd64 +0 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/smartsecret.kernel.protocol.d.ts +2 -2
- package/dist_ts/smartsecret.kernel.protocol.js +3 -3
- package/dist_ts/smartsecret.kernelstore.js +17 -7
- package/dist_ts/smartsecret.sealedfile.d.ts +40 -0
- package/dist_ts/smartsecret.sealedfile.error.d.ts +17 -0
- package/dist_ts/smartsecret.sealedfile.error.js +44 -0
- package/dist_ts/smartsecret.sealedfile.js +989 -0
- package/package.json +6 -6
- package/readme.hints.md +12 -1
- package/readme.md +90 -3
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +6 -0
- package/ts/smartsecret.kernel.protocol.ts +2 -2
- package/ts/smartsecret.kernelstore.ts +15 -6
- package/ts/smartsecret.sealedfile.error.ts +72 -0
- package/ts/smartsecret.sealedfile.ts +1195 -0
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
import * as plugins from './smartsecret.plugins.js';
|
|
2
|
+
import { SmartSecretKernelStoreError, } from './smartsecret.kernel.error.js';
|
|
3
|
+
import { normalizeSmartSecretKernelAccount, normalizeSmartSecretKernelService, } from './smartsecret.kernel.protocol.js';
|
|
4
|
+
import { SmartSecretSealedFileStoreError, createSmartSecretSealedFileStoreError, } from './smartsecret.sealedfile.error.js';
|
|
5
|
+
export const smartSecretSealedFileMaximumEntryBytes = 512 * 1_024;
|
|
6
|
+
const schemaVersion = 1;
|
|
7
|
+
const profile = 'smartsecret-kernel-aes-256-gcm-file-v1';
|
|
8
|
+
const manifestName = 'manifest.json';
|
|
9
|
+
const masterKeyBytes = 32;
|
|
10
|
+
const maximumEnvelopeBytes = 768 * 1_024;
|
|
11
|
+
const operationTimeoutMs = 60_000;
|
|
12
|
+
const canonicalDigestPattern = /^[A-Za-z0-9_-]{43}$/;
|
|
13
|
+
const entryFilePattern = /^[A-Za-z0-9_-]{43}\.sealed\.json$/;
|
|
14
|
+
const temporaryFilePattern = /^\.smartsecret-[A-Za-z0-9_-]{22}\.tmp$/;
|
|
15
|
+
const maximumPendingOperations = 64;
|
|
16
|
+
const encoder = new TextEncoder();
|
|
17
|
+
const sha256 = (...valuesArg) => {
|
|
18
|
+
const hash = plugins.crypto.createHash('sha256');
|
|
19
|
+
for (const value of valuesArg)
|
|
20
|
+
hash.update(value);
|
|
21
|
+
return new Uint8Array(hash.digest());
|
|
22
|
+
};
|
|
23
|
+
const digest = (...valuesArg) => Buffer.from(sha256(...valuesArg)).toString('base64url');
|
|
24
|
+
const fingerprint = (keyArg) => digest(encoder.encode('@push.rocks/smartsecret/sealed-file/master-key/v1\0'), keyArg);
|
|
25
|
+
const normalizeStoreId = (valueArg) => {
|
|
26
|
+
if (typeof valueArg !== 'string'
|
|
27
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(valueArg))
|
|
28
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
29
|
+
return valueArg;
|
|
30
|
+
};
|
|
31
|
+
const normalizeDirectoryPath = (valueArg) => {
|
|
32
|
+
if (typeof valueArg !== 'string'
|
|
33
|
+
|| !plugins.path.isAbsolute(valueArg)
|
|
34
|
+
|| plugins.path.resolve(valueArg) !== valueArg
|
|
35
|
+
|| valueArg === plugins.path.parse(valueArg).root)
|
|
36
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
37
|
+
return valueArg;
|
|
38
|
+
};
|
|
39
|
+
const dataPropertyValue = (objectArg, keyArg) => {
|
|
40
|
+
let current = objectArg;
|
|
41
|
+
while (current) {
|
|
42
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, keyArg);
|
|
43
|
+
if (descriptor) {
|
|
44
|
+
if (!('value' in descriptor)) {
|
|
45
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
46
|
+
}
|
|
47
|
+
return descriptor.value;
|
|
48
|
+
}
|
|
49
|
+
current = Object.getPrototypeOf(current);
|
|
50
|
+
}
|
|
51
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
52
|
+
};
|
|
53
|
+
const normalizeOptions = (optionsArg) => {
|
|
54
|
+
try {
|
|
55
|
+
if (!optionsArg || typeof optionsArg !== 'object' || Array.isArray(optionsArg)) {
|
|
56
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
57
|
+
}
|
|
58
|
+
const keys = Reflect.ownKeys(optionsArg);
|
|
59
|
+
if (keys.length !== 3
|
|
60
|
+
|| keys.some((keyArg) => typeof keyArg !== 'string')
|
|
61
|
+
|| !['kernelStore', 'storeId', 'directoryPath'].every((keyArg) => keys.includes(keyArg)))
|
|
62
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
63
|
+
const values = Object.create(null);
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
const descriptor = Object.getOwnPropertyDescriptor(optionsArg, key);
|
|
66
|
+
if (!descriptor?.enumerable || !('value' in descriptor)) {
|
|
67
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
68
|
+
}
|
|
69
|
+
values[key] = descriptor.value;
|
|
70
|
+
}
|
|
71
|
+
const kernelStoreValue = values.kernelStore;
|
|
72
|
+
if (!kernelStoreValue
|
|
73
|
+
|| typeof kernelStoreValue !== 'object')
|
|
74
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
75
|
+
const service = normalizeSmartSecretKernelService(dataPropertyValue(kernelStoreValue, 'service'));
|
|
76
|
+
const getEntry = dataPropertyValue(kernelStoreValue, 'getEntry');
|
|
77
|
+
const setEntry = dataPropertyValue(kernelStoreValue, 'setEntry');
|
|
78
|
+
const deleteEntry = dataPropertyValue(kernelStoreValue, 'deleteEntry');
|
|
79
|
+
if (typeof getEntry !== 'function'
|
|
80
|
+
|| typeof setEntry !== 'function'
|
|
81
|
+
|| typeof deleteEntry !== 'function')
|
|
82
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
83
|
+
const kernelStore = {
|
|
84
|
+
service,
|
|
85
|
+
getEntry: (accountArg, operationOptionsArg) => Reflect.apply(getEntry, kernelStoreValue, [accountArg, operationOptionsArg]),
|
|
86
|
+
setEntry: (accountArg, valueArg, operationOptionsArg) => Reflect.apply(setEntry, kernelStoreValue, [accountArg, valueArg, operationOptionsArg]),
|
|
87
|
+
deleteEntry: (accountArg, operationOptionsArg) => Reflect.apply(deleteEntry, kernelStoreValue, [accountArg, operationOptionsArg]),
|
|
88
|
+
};
|
|
89
|
+
const storeId = normalizeStoreId(values.storeId);
|
|
90
|
+
const directoryPath = normalizeDirectoryPath(values.directoryPath);
|
|
91
|
+
const identity = encoder.encode(`${service}\0${storeId}\0${directoryPath}`);
|
|
92
|
+
return {
|
|
93
|
+
kernelStore,
|
|
94
|
+
service,
|
|
95
|
+
storeId,
|
|
96
|
+
directoryPath,
|
|
97
|
+
masterKeyAccount: `sealed-file-master:v1:${digest(identity)}`,
|
|
98
|
+
mutexNamespace: `smartsecret-sealed-file:v1:${digest(encoder.encode(directoryPath))}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const exactObject = (valueArg, keysArg, errorCodeArg) => {
|
|
106
|
+
try {
|
|
107
|
+
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
|
|
108
|
+
throw createSmartSecretSealedFileStoreError(errorCodeArg);
|
|
109
|
+
}
|
|
110
|
+
const prototype = Object.getPrototypeOf(valueArg);
|
|
111
|
+
const keys = Reflect.ownKeys(valueArg);
|
|
112
|
+
if ((prototype !== Object.prototype && prototype !== null)
|
|
113
|
+
|| keys.length !== keysArg.length
|
|
114
|
+
|| keys.some((keyArg) => typeof keyArg !== 'string')
|
|
115
|
+
|| keysArg.some((keyArg) => !keys.includes(keyArg)))
|
|
116
|
+
throw createSmartSecretSealedFileStoreError(errorCodeArg);
|
|
117
|
+
const result = Object.create(null);
|
|
118
|
+
for (const key of keys) {
|
|
119
|
+
const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
|
|
120
|
+
if (!descriptor?.enumerable || !('value' in descriptor)) {
|
|
121
|
+
throw createSmartSecretSealedFileStoreError(errorCodeArg);
|
|
122
|
+
}
|
|
123
|
+
result[key] = descriptor.value;
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
catch (errorArg) {
|
|
128
|
+
if (errorArg instanceof SmartSecretSealedFileStoreError)
|
|
129
|
+
throw errorArg;
|
|
130
|
+
throw createSmartSecretSealedFileStoreError(errorCodeArg);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const decodeBase64Url = (valueArg, maximumBytesArg, exactBytesArg) => {
|
|
134
|
+
if (typeof valueArg !== 'string'
|
|
135
|
+
|| !/^[A-Za-z0-9_-]*$/.test(valueArg)
|
|
136
|
+
|| valueArg.length % 4 === 1
|
|
137
|
+
|| valueArg.length > Math.ceil(maximumBytesArg * 4 / 3))
|
|
138
|
+
throw createSmartSecretSealedFileStoreError('ENVELOPE_INVALID');
|
|
139
|
+
const decoded = Buffer.from(valueArg, 'base64url');
|
|
140
|
+
if (decoded.byteLength > maximumBytesArg
|
|
141
|
+
|| (exactBytesArg !== undefined && decoded.byteLength !== exactBytesArg)
|
|
142
|
+
|| decoded.toString('base64url') !== valueArg) {
|
|
143
|
+
decoded.fill(0);
|
|
144
|
+
throw createSmartSecretSealedFileStoreError('ENVELOPE_INVALID');
|
|
145
|
+
}
|
|
146
|
+
const result = new Uint8Array(decoded);
|
|
147
|
+
decoded.fill(0);
|
|
148
|
+
return result;
|
|
149
|
+
};
|
|
150
|
+
const parseEnvelope = (valueArg) => {
|
|
151
|
+
const value = exactObject(valueArg, [
|
|
152
|
+
'schemaVersion',
|
|
153
|
+
'profile',
|
|
154
|
+
'entryId',
|
|
155
|
+
'masterKeyFingerprint',
|
|
156
|
+
'nonce',
|
|
157
|
+
'ciphertext',
|
|
158
|
+
'tag',
|
|
159
|
+
], 'ENVELOPE_INVALID');
|
|
160
|
+
if (value.schemaVersion !== schemaVersion
|
|
161
|
+
|| value.profile !== profile
|
|
162
|
+
|| typeof value.entryId !== 'string'
|
|
163
|
+
|| !canonicalDigestPattern.test(value.entryId)
|
|
164
|
+
|| typeof value.masterKeyFingerprint !== 'string'
|
|
165
|
+
|| !canonicalDigestPattern.test(value.masterKeyFingerprint))
|
|
166
|
+
throw createSmartSecretSealedFileStoreError('ENVELOPE_INVALID');
|
|
167
|
+
let nonce;
|
|
168
|
+
let ciphertext;
|
|
169
|
+
let tag;
|
|
170
|
+
try {
|
|
171
|
+
nonce = decodeBase64Url(value.nonce, 12, 12);
|
|
172
|
+
ciphertext = decodeBase64Url(value.ciphertext, smartSecretSealedFileMaximumEntryBytes);
|
|
173
|
+
tag = decodeBase64Url(value.tag, 16, 16);
|
|
174
|
+
return {
|
|
175
|
+
envelope: {
|
|
176
|
+
schemaVersion,
|
|
177
|
+
profile,
|
|
178
|
+
entryId: value.entryId,
|
|
179
|
+
masterKeyFingerprint: value.masterKeyFingerprint,
|
|
180
|
+
nonce: value.nonce,
|
|
181
|
+
ciphertext: value.ciphertext,
|
|
182
|
+
tag: value.tag,
|
|
183
|
+
},
|
|
184
|
+
encryptedData: { nonce, ciphertext, tag },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (errorArg) {
|
|
188
|
+
nonce?.fill(0);
|
|
189
|
+
ciphertext?.fill(0);
|
|
190
|
+
tag?.fill(0);
|
|
191
|
+
throw errorArg;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const encodeBase64Url = (valueArg) => {
|
|
195
|
+
const copy = Buffer.from(valueArg);
|
|
196
|
+
try {
|
|
197
|
+
return copy.toString('base64url');
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
copy.fill(0);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const encodeEncryptedData = (valueArg) => ({
|
|
204
|
+
nonce: encodeBase64Url(valueArg.nonce),
|
|
205
|
+
ciphertext: encodeBase64Url(valueArg.ciphertext),
|
|
206
|
+
tag: encodeBase64Url(valueArg.tag),
|
|
207
|
+
});
|
|
208
|
+
const entryId = (optionsArg, accountArg) => digest(encoder.encode('@push.rocks/smartsecret/sealed-file/entry/v1\0'), encoder.encode(optionsArg.service), new Uint8Array([0]), encoder.encode(optionsArg.storeId), new Uint8Array([0]), encoder.encode(accountArg));
|
|
209
|
+
const entryAad = (optionsArg, accountArg, entryIdArg, fingerprintArg) => encoder.encode(JSON.stringify({
|
|
210
|
+
schemaVersion,
|
|
211
|
+
profile,
|
|
212
|
+
service: optionsArg.service,
|
|
213
|
+
storeId: optionsArg.storeId,
|
|
214
|
+
account: accountArg,
|
|
215
|
+
entryId: entryIdArg,
|
|
216
|
+
masterKeyFingerprint: fingerprintArg,
|
|
217
|
+
}));
|
|
218
|
+
const manifestFor = (optionsArg, fingerprintArg) => ({
|
|
219
|
+
schemaVersion,
|
|
220
|
+
profile,
|
|
221
|
+
serviceDigest: digest(encoder.encode(optionsArg.service)),
|
|
222
|
+
storeId: optionsArg.storeId,
|
|
223
|
+
masterKeyFingerprint: fingerprintArg,
|
|
224
|
+
});
|
|
225
|
+
const parseManifest = (valueArg, optionsArg) => {
|
|
226
|
+
const value = exactObject(valueArg, [
|
|
227
|
+
'schemaVersion',
|
|
228
|
+
'profile',
|
|
229
|
+
'serviceDigest',
|
|
230
|
+
'storeId',
|
|
231
|
+
'masterKeyFingerprint',
|
|
232
|
+
], 'FILESYSTEM_FAILED');
|
|
233
|
+
if (value.schemaVersion !== schemaVersion
|
|
234
|
+
|| value.profile !== profile
|
|
235
|
+
|| value.serviceDigest !== digest(encoder.encode(optionsArg.service))
|
|
236
|
+
|| value.storeId !== optionsArg.storeId
|
|
237
|
+
|| typeof value.masterKeyFingerprint !== 'string'
|
|
238
|
+
|| !canonicalDigestPattern.test(value.masterKeyFingerprint))
|
|
239
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
240
|
+
return value;
|
|
241
|
+
};
|
|
242
|
+
const mapKernelError = (errorArg) => {
|
|
243
|
+
if (!(errorArg instanceof SmartSecretKernelStoreError)) {
|
|
244
|
+
return createSmartSecretSealedFileStoreError('KERNEL_UNAVAILABLE');
|
|
245
|
+
}
|
|
246
|
+
if (errorArg.code === 'MUTATION_OUTCOME_UNKNOWN') {
|
|
247
|
+
return createSmartSecretSealedFileStoreError('MUTATION_OUTCOME_UNKNOWN');
|
|
248
|
+
}
|
|
249
|
+
if (errorArg.code === 'INVALID_ARGUMENT') {
|
|
250
|
+
return createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
251
|
+
}
|
|
252
|
+
return createSmartSecretSealedFileStoreError('KERNEL_UNAVAILABLE');
|
|
253
|
+
};
|
|
254
|
+
const mapMutexError = (errorArg) => {
|
|
255
|
+
if (errorArg instanceof plugins.smartipc.NamedMutexError) {
|
|
256
|
+
if (errorArg.code === 'ABORTED' || errorArg.code === 'TIMEOUT') {
|
|
257
|
+
return createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
261
|
+
};
|
|
262
|
+
const directoryHandlePath = (directoryHandleArg, nameArg) => nameArg
|
|
263
|
+
? plugins.path.join(`/proc/self/fd/${directoryHandleArg.fd}`, nameArg)
|
|
264
|
+
: `/proc/self/fd/${directoryHandleArg.fd}`;
|
|
265
|
+
const mutationOutcomeUnknown = (errorArg) => errorArg instanceof SmartSecretSealedFileStoreError
|
|
266
|
+
&& errorArg.code === 'MUTATION_OUTCOME_UNKNOWN';
|
|
267
|
+
const preferCleanupError = (operationErrorArg, cleanupErrorArg) => mutationOutcomeUnknown(operationErrorArg) ? operationErrorArg : cleanupErrorArg;
|
|
268
|
+
const closeFileHandle = async (handleArg) => {
|
|
269
|
+
try {
|
|
270
|
+
await handleArg.close();
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
try {
|
|
274
|
+
await handleArg.close();
|
|
275
|
+
}
|
|
276
|
+
catch { }
|
|
277
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const closeFileHandleWithError = async (handleArg, operationErrorArg) => {
|
|
281
|
+
try {
|
|
282
|
+
await closeFileHandle(handleArg);
|
|
283
|
+
return operationErrorArg;
|
|
284
|
+
}
|
|
285
|
+
catch (errorArg) {
|
|
286
|
+
return preferCleanupError(operationErrorArg, errorArg);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const wipeDiscardedResult = (valueArg) => {
|
|
290
|
+
if (valueArg instanceof Uint8Array)
|
|
291
|
+
valueArg.fill(0);
|
|
292
|
+
};
|
|
293
|
+
const validateDirectory = (statsArg, effectiveUidArg, finalArg) => {
|
|
294
|
+
if (!statsArg.isDirectory()) {
|
|
295
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
296
|
+
}
|
|
297
|
+
const mode = statsArg.mode & 4095n;
|
|
298
|
+
if (finalArg) {
|
|
299
|
+
if (statsArg.uid !== effectiveUidArg || mode !== 448n) {
|
|
300
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (statsArg.uid !== 0n && statsArg.uid !== effectiveUidArg) {
|
|
305
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
306
|
+
}
|
|
307
|
+
if ((mode & 18n) !== 0n
|
|
308
|
+
&& !(statsArg.uid === 0n && (mode & 512n) !== 0n))
|
|
309
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
310
|
+
};
|
|
311
|
+
const ensureDirectory = async (directoryPathArg) => {
|
|
312
|
+
let currentHandle;
|
|
313
|
+
try {
|
|
314
|
+
const effectiveUid = process.geteuid?.();
|
|
315
|
+
if (!Number.isSafeInteger(effectiveUid) || effectiveUid < 0) {
|
|
316
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
317
|
+
}
|
|
318
|
+
const root = plugins.path.parse(directoryPathArg).root;
|
|
319
|
+
const components = plugins.path.relative(root, directoryPathArg)
|
|
320
|
+
.split(plugins.path.sep)
|
|
321
|
+
.filter(Boolean);
|
|
322
|
+
currentHandle = await plugins.fs.promises.open(root, plugins.fs.constants.O_RDONLY
|
|
323
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
324
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
325
|
+
validateDirectory(await currentHandle.stat({ bigint: true }), BigInt(effectiveUid), false);
|
|
326
|
+
for (let index = 0; index < components.length; index++) {
|
|
327
|
+
const componentPath = directoryHandlePath(currentHandle, components[index]);
|
|
328
|
+
try {
|
|
329
|
+
await plugins.fs.promises.mkdir(componentPath, { mode: 0o700 });
|
|
330
|
+
}
|
|
331
|
+
catch (errorArg) {
|
|
332
|
+
if (errorArg.code !== 'EEXIST')
|
|
333
|
+
throw errorArg;
|
|
334
|
+
}
|
|
335
|
+
let nextHandle;
|
|
336
|
+
try {
|
|
337
|
+
nextHandle = await plugins.fs.promises.open(componentPath, plugins.fs.constants.O_RDONLY
|
|
338
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
339
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
340
|
+
validateDirectory(await nextHandle.stat({ bigint: true }), BigInt(effectiveUid), index === components.length - 1);
|
|
341
|
+
await closeFileHandle(currentHandle);
|
|
342
|
+
currentHandle = nextHandle;
|
|
343
|
+
nextHandle = undefined;
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
if (nextHandle)
|
|
347
|
+
await closeFileHandle(nextHandle);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const result = currentHandle;
|
|
351
|
+
currentHandle = undefined;
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
catch (errorArg) {
|
|
355
|
+
let operationError = errorArg instanceof SmartSecretSealedFileStoreError
|
|
356
|
+
? errorArg
|
|
357
|
+
: createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
358
|
+
if (currentHandle) {
|
|
359
|
+
operationError = await closeFileHandleWithError(currentHandle, operationError);
|
|
360
|
+
}
|
|
361
|
+
throw operationError;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const readStrictJsonFile = async (directoryHandleArg, fileNameArg, maximumBytesArg) => {
|
|
365
|
+
let handle;
|
|
366
|
+
let bytes;
|
|
367
|
+
let result;
|
|
368
|
+
let operationError;
|
|
369
|
+
try {
|
|
370
|
+
try {
|
|
371
|
+
handle = await plugins.fs.promises.open(directoryHandlePath(directoryHandleArg, fileNameArg), plugins.fs.constants.O_RDONLY | plugins.fs.constants.O_NOFOLLOW);
|
|
372
|
+
}
|
|
373
|
+
catch (errorArg) {
|
|
374
|
+
if (errorArg.code === 'ENOENT')
|
|
375
|
+
return undefined;
|
|
376
|
+
throw errorArg;
|
|
377
|
+
}
|
|
378
|
+
const stats = await handle.stat();
|
|
379
|
+
const effectiveUid = process.geteuid?.();
|
|
380
|
+
if (!stats.isFile()
|
|
381
|
+
|| stats.nlink !== 1
|
|
382
|
+
|| stats.size < 1
|
|
383
|
+
|| stats.size > maximumBytesArg
|
|
384
|
+
|| (effectiveUid !== undefined && stats.uid !== effectiveUid)
|
|
385
|
+
|| (stats.mode & 0o7777) !== 0o600)
|
|
386
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
387
|
+
bytes = await handle.readFile();
|
|
388
|
+
if (bytes.byteLength !== stats.size) {
|
|
389
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
390
|
+
}
|
|
391
|
+
result = JSON.parse(bytes.toString('utf8'));
|
|
392
|
+
}
|
|
393
|
+
catch (errorArg) {
|
|
394
|
+
operationError = errorArg instanceof SmartSecretSealedFileStoreError
|
|
395
|
+
? errorArg
|
|
396
|
+
: createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
397
|
+
}
|
|
398
|
+
bytes?.fill(0);
|
|
399
|
+
if (handle) {
|
|
400
|
+
try {
|
|
401
|
+
await closeFileHandle(handle);
|
|
402
|
+
}
|
|
403
|
+
catch (errorArg) {
|
|
404
|
+
operationError = preferCleanupError(operationError, errorArg);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (operationError)
|
|
408
|
+
throw operationError;
|
|
409
|
+
return result;
|
|
410
|
+
};
|
|
411
|
+
const atomicWriteJson = async (directoryHandleArg, targetNameArg, valueArg) => {
|
|
412
|
+
const temporaryName = `.smartsecret-${plugins.crypto.randomBytes(16).toString('base64url')}.tmp`;
|
|
413
|
+
const temporaryPath = directoryHandlePath(directoryHandleArg, temporaryName);
|
|
414
|
+
const targetPath = directoryHandlePath(directoryHandleArg, targetNameArg);
|
|
415
|
+
let handle;
|
|
416
|
+
let bytes;
|
|
417
|
+
let renamed = false;
|
|
418
|
+
let mutationApplied = false;
|
|
419
|
+
let operationError;
|
|
420
|
+
try {
|
|
421
|
+
bytes = Buffer.from(JSON.stringify(valueArg), 'utf8');
|
|
422
|
+
handle = await plugins.fs.promises.open(temporaryPath, plugins.fs.constants.O_WRONLY
|
|
423
|
+
| plugins.fs.constants.O_CREAT
|
|
424
|
+
| plugins.fs.constants.O_EXCL
|
|
425
|
+
| plugins.fs.constants.O_NOFOLLOW, 0o600);
|
|
426
|
+
const stats = await handle.stat();
|
|
427
|
+
const effectiveUid = process.geteuid?.();
|
|
428
|
+
if (!stats.isFile()
|
|
429
|
+
|| stats.nlink !== 1
|
|
430
|
+
|| (effectiveUid !== undefined && stats.uid !== effectiveUid)
|
|
431
|
+
|| (stats.mode & 0o7777) !== 0o600) {
|
|
432
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
433
|
+
}
|
|
434
|
+
await handle.writeFile(bytes);
|
|
435
|
+
await handle.sync();
|
|
436
|
+
await closeFileHandle(handle);
|
|
437
|
+
handle = undefined;
|
|
438
|
+
await plugins.fs.promises.rename(temporaryPath, targetPath);
|
|
439
|
+
renamed = true;
|
|
440
|
+
mutationApplied = true;
|
|
441
|
+
await directoryHandleArg.sync();
|
|
442
|
+
}
|
|
443
|
+
catch (errorArg) {
|
|
444
|
+
operationError = errorArg instanceof SmartSecretSealedFileStoreError
|
|
445
|
+
? errorArg
|
|
446
|
+
: createSmartSecretSealedFileStoreError(mutationApplied ? 'MUTATION_OUTCOME_UNKNOWN' : 'FILESYSTEM_FAILED');
|
|
447
|
+
}
|
|
448
|
+
bytes?.fill(0);
|
|
449
|
+
if (handle) {
|
|
450
|
+
try {
|
|
451
|
+
await closeFileHandle(handle);
|
|
452
|
+
}
|
|
453
|
+
catch (errorArg) {
|
|
454
|
+
operationError = preferCleanupError(operationError, errorArg);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (!renamed) {
|
|
458
|
+
try {
|
|
459
|
+
await plugins.fs.promises.unlink(temporaryPath);
|
|
460
|
+
await directoryHandleArg.sync();
|
|
461
|
+
}
|
|
462
|
+
catch (errorArg) {
|
|
463
|
+
if (errorArg.code !== 'ENOENT') {
|
|
464
|
+
operationError = createSmartSecretSealedFileStoreError('MUTATION_OUTCOME_UNKNOWN');
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (operationError)
|
|
469
|
+
throw operationError;
|
|
470
|
+
};
|
|
471
|
+
const removeAndSync = async (directoryHandleArg, fileNameArg) => {
|
|
472
|
+
try {
|
|
473
|
+
await plugins.fs.promises.unlink(directoryHandlePath(directoryHandleArg, fileNameArg));
|
|
474
|
+
try {
|
|
475
|
+
await directoryHandleArg.sync();
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
throw createSmartSecretSealedFileStoreError('MUTATION_OUTCOME_UNKNOWN');
|
|
479
|
+
}
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
catch (errorArg) {
|
|
483
|
+
if (errorArg instanceof SmartSecretSealedFileStoreError)
|
|
484
|
+
throw errorArg;
|
|
485
|
+
if (errorArg.code === 'ENOENT')
|
|
486
|
+
return false;
|
|
487
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const listOwnedFiles = async (directoryHandleArg) => {
|
|
491
|
+
try {
|
|
492
|
+
const names = await plugins.fs.promises.readdir(directoryHandlePath(directoryHandleArg));
|
|
493
|
+
if (names.some((nameArg) => (nameArg !== manifestName
|
|
494
|
+
&& !entryFilePattern.test(nameArg)
|
|
495
|
+
&& !temporaryFilePattern.test(nameArg))))
|
|
496
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
497
|
+
return names;
|
|
498
|
+
}
|
|
499
|
+
catch (errorArg) {
|
|
500
|
+
if (errorArg instanceof SmartSecretSealedFileStoreError)
|
|
501
|
+
throw errorArg;
|
|
502
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const acquireLease = async (optionsArg, deadlineArg = performance.now() + operationTimeoutMs) => {
|
|
506
|
+
const remainingMs = deadlineArg - performance.now();
|
|
507
|
+
if (remainingMs <= 0) {
|
|
508
|
+
throw createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
509
|
+
}
|
|
510
|
+
try {
|
|
511
|
+
const mutex = new plugins.smartipc.NamedMutex(optionsArg.mutexNamespace);
|
|
512
|
+
const lease = await mutex.acquire({ timeoutMs: Math.max(0, Math.floor(remainingMs)) });
|
|
513
|
+
if (performance.now() >= deadlineArg) {
|
|
514
|
+
await lease.release().catch(() => undefined);
|
|
515
|
+
throw createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
516
|
+
}
|
|
517
|
+
return lease;
|
|
518
|
+
}
|
|
519
|
+
catch (errorArg) {
|
|
520
|
+
throw mapMutexError(errorArg);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const readMasterKey = async (optionsArg) => {
|
|
524
|
+
let value;
|
|
525
|
+
try {
|
|
526
|
+
value = await optionsArg.kernelStore.getEntry(optionsArg.masterKeyAccount);
|
|
527
|
+
}
|
|
528
|
+
catch (errorArg) {
|
|
529
|
+
throw mapKernelError(errorArg);
|
|
530
|
+
}
|
|
531
|
+
if (value === null)
|
|
532
|
+
return null;
|
|
533
|
+
let result;
|
|
534
|
+
try {
|
|
535
|
+
if (!(value instanceof Uint8Array)) {
|
|
536
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
537
|
+
}
|
|
538
|
+
result = new Uint8Array(value);
|
|
539
|
+
try {
|
|
540
|
+
value.fill(0);
|
|
541
|
+
}
|
|
542
|
+
catch { }
|
|
543
|
+
if (result.byteLength !== masterKeyBytes) {
|
|
544
|
+
result.fill(0);
|
|
545
|
+
result = undefined;
|
|
546
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
catch (errorArg) {
|
|
551
|
+
result?.fill(0);
|
|
552
|
+
if (errorArg instanceof SmartSecretSealedFileStoreError)
|
|
553
|
+
throw errorArg;
|
|
554
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const createMasterKey = async (optionsArg) => {
|
|
558
|
+
const generated = plugins.smartcrypto.generateAes256GcmKey();
|
|
559
|
+
let written = false;
|
|
560
|
+
try {
|
|
561
|
+
try {
|
|
562
|
+
await optionsArg.kernelStore.setEntry(optionsArg.masterKeyAccount, generated);
|
|
563
|
+
written = true;
|
|
564
|
+
}
|
|
565
|
+
catch (errorArg) {
|
|
566
|
+
throw mapKernelError(errorArg);
|
|
567
|
+
}
|
|
568
|
+
const readback = await readMasterKey(optionsArg);
|
|
569
|
+
if (!readback
|
|
570
|
+
|| !plugins.crypto.timingSafeEqual(readback, generated)) {
|
|
571
|
+
readback?.fill(0);
|
|
572
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
573
|
+
}
|
|
574
|
+
return readback;
|
|
575
|
+
}
|
|
576
|
+
catch (errorArg) {
|
|
577
|
+
if (written) {
|
|
578
|
+
try {
|
|
579
|
+
await optionsArg.kernelStore.deleteEntry(optionsArg.masterKeyAccount);
|
|
580
|
+
}
|
|
581
|
+
catch (deleteErrorArg) {
|
|
582
|
+
throw mapKernelError(deleteErrorArg);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
throw errorArg;
|
|
586
|
+
}
|
|
587
|
+
finally {
|
|
588
|
+
generated.fill(0);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
const bootstrapUnderLease = async (optionsArg) => {
|
|
592
|
+
const directoryHandle = await ensureDirectory(optionsArg.directoryPath);
|
|
593
|
+
let key = null;
|
|
594
|
+
let result;
|
|
595
|
+
let createdMasterKey = false;
|
|
596
|
+
let operationError;
|
|
597
|
+
try {
|
|
598
|
+
const names = await listOwnedFiles(directoryHandle);
|
|
599
|
+
const manifestValue = await readStrictJsonFile(directoryHandle, manifestName, 4 * 1_024);
|
|
600
|
+
const manifest = manifestValue === undefined ? undefined : parseManifest(manifestValue, optionsArg);
|
|
601
|
+
key = await readMasterKey(optionsArg);
|
|
602
|
+
if (!key) {
|
|
603
|
+
if (names.length > 0) {
|
|
604
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_UNAVAILABLE');
|
|
605
|
+
}
|
|
606
|
+
key = await createMasterKey(optionsArg);
|
|
607
|
+
createdMasterKey = true;
|
|
608
|
+
}
|
|
609
|
+
if (key.byteLength !== masterKeyBytes) {
|
|
610
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
611
|
+
}
|
|
612
|
+
const keyFingerprint = fingerprint(key);
|
|
613
|
+
if (manifest && manifest.masterKeyFingerprint !== keyFingerprint) {
|
|
614
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
615
|
+
}
|
|
616
|
+
for (const name of names.filter((nameArg) => temporaryFilePattern.test(nameArg))) {
|
|
617
|
+
await removeAndSync(directoryHandle, name);
|
|
618
|
+
}
|
|
619
|
+
if (!manifest) {
|
|
620
|
+
await atomicWriteJson(directoryHandle, manifestName, manifestFor(optionsArg, keyFingerprint));
|
|
621
|
+
}
|
|
622
|
+
result = new Uint8Array(key);
|
|
623
|
+
key.fill(0);
|
|
624
|
+
key = null;
|
|
625
|
+
}
|
|
626
|
+
catch (errorArg) {
|
|
627
|
+
operationError = errorArg;
|
|
628
|
+
}
|
|
629
|
+
key?.fill(0);
|
|
630
|
+
operationError = await closeFileHandleWithError(directoryHandle, operationError);
|
|
631
|
+
if (operationError) {
|
|
632
|
+
result?.fill(0);
|
|
633
|
+
result = undefined;
|
|
634
|
+
if (createdMasterKey && !mutationOutcomeUnknown(operationError)) {
|
|
635
|
+
try {
|
|
636
|
+
await optionsArg.kernelStore.deleteEntry(optionsArg.masterKeyAccount);
|
|
637
|
+
}
|
|
638
|
+
catch (deleteErrorArg) {
|
|
639
|
+
const deleteError = mapKernelError(deleteErrorArg);
|
|
640
|
+
throw preferCleanupError(operationError, deleteError);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
throw operationError;
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* Stores one small master key in a strict kernel store and authenticated
|
|
649
|
+
* ciphertext in a caller-owned private directory. The kernel store lifecycle
|
|
650
|
+
* remains owned by the caller.
|
|
651
|
+
*/
|
|
652
|
+
export class SmartSecretSealedFileStore {
|
|
653
|
+
constructor(options, masterKey) {
|
|
654
|
+
this.options = options;
|
|
655
|
+
this.masterKey = masterKey;
|
|
656
|
+
this.lifecycleState = 'ready';
|
|
657
|
+
this.operationTail = Promise.resolve();
|
|
658
|
+
this.pendingOperations = 0;
|
|
659
|
+
this.service = options.service;
|
|
660
|
+
this.storeId = options.storeId;
|
|
661
|
+
this.directoryPath = options.directoryPath;
|
|
662
|
+
}
|
|
663
|
+
static async create(optionsArg) {
|
|
664
|
+
const options = normalizeOptions(optionsArg);
|
|
665
|
+
if (process.platform !== 'linux' || typeof process.geteuid !== 'function') {
|
|
666
|
+
throw createSmartSecretSealedFileStoreError('KERNEL_UNAVAILABLE');
|
|
667
|
+
}
|
|
668
|
+
const lease = await acquireLease(options);
|
|
669
|
+
let key;
|
|
670
|
+
let operationError;
|
|
671
|
+
try {
|
|
672
|
+
key = await bootstrapUnderLease(options);
|
|
673
|
+
}
|
|
674
|
+
catch (errorArg) {
|
|
675
|
+
operationError = errorArg;
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
await lease.release();
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
operationError = preferCleanupError(operationError, createSmartSecretSealedFileStoreError('MUTEX_FAILED'));
|
|
682
|
+
}
|
|
683
|
+
if (operationError) {
|
|
684
|
+
key?.fill(0);
|
|
685
|
+
throw operationError;
|
|
686
|
+
}
|
|
687
|
+
const store = new SmartSecretSealedFileStore(options, key);
|
|
688
|
+
key = undefined;
|
|
689
|
+
return store;
|
|
690
|
+
}
|
|
691
|
+
/** Explicitly discards this store's ciphertext and kernel master key. */
|
|
692
|
+
static async reset(optionsArg) {
|
|
693
|
+
const options = normalizeOptions(optionsArg);
|
|
694
|
+
if (process.platform !== 'linux' || typeof process.geteuid !== 'function') {
|
|
695
|
+
throw createSmartSecretSealedFileStoreError('KERNEL_UNAVAILABLE');
|
|
696
|
+
}
|
|
697
|
+
const lease = await acquireLease(options);
|
|
698
|
+
let key;
|
|
699
|
+
let operationError;
|
|
700
|
+
try {
|
|
701
|
+
const directoryHandle = await ensureDirectory(options.directoryPath);
|
|
702
|
+
try {
|
|
703
|
+
const names = await listOwnedFiles(directoryHandle);
|
|
704
|
+
if (names.some((nameArg) => (nameArg === manifestName || entryFilePattern.test(nameArg)))) {
|
|
705
|
+
const manifestValue = await readStrictJsonFile(directoryHandle, manifestName, 4 * 1_024);
|
|
706
|
+
if (manifestValue === undefined) {
|
|
707
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
708
|
+
}
|
|
709
|
+
parseManifest(manifestValue, options);
|
|
710
|
+
}
|
|
711
|
+
try {
|
|
712
|
+
await options.kernelStore.deleteEntry(options.masterKeyAccount);
|
|
713
|
+
}
|
|
714
|
+
catch (errorArg) {
|
|
715
|
+
throw mapKernelError(errorArg);
|
|
716
|
+
}
|
|
717
|
+
const deletionOrder = names.filter((nameArg) => nameArg !== manifestName);
|
|
718
|
+
if (names.includes(manifestName))
|
|
719
|
+
deletionOrder.push(manifestName);
|
|
720
|
+
for (const name of deletionOrder) {
|
|
721
|
+
await removeAndSync(directoryHandle, name);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch (errorArg) {
|
|
725
|
+
operationError = errorArg;
|
|
726
|
+
}
|
|
727
|
+
operationError = await closeFileHandleWithError(directoryHandle, operationError);
|
|
728
|
+
if (operationError)
|
|
729
|
+
throw operationError;
|
|
730
|
+
key = await bootstrapUnderLease(options);
|
|
731
|
+
}
|
|
732
|
+
catch (errorArg) {
|
|
733
|
+
operationError = errorArg;
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
await lease.release();
|
|
737
|
+
}
|
|
738
|
+
catch {
|
|
739
|
+
operationError = preferCleanupError(operationError, createSmartSecretSealedFileStoreError('MUTEX_FAILED'));
|
|
740
|
+
}
|
|
741
|
+
if (operationError) {
|
|
742
|
+
key?.fill(0);
|
|
743
|
+
throw operationError;
|
|
744
|
+
}
|
|
745
|
+
const store = new SmartSecretSealedFileStore(options, key);
|
|
746
|
+
key = undefined;
|
|
747
|
+
return store;
|
|
748
|
+
}
|
|
749
|
+
getEntry(accountArg) {
|
|
750
|
+
let account;
|
|
751
|
+
try {
|
|
752
|
+
account = normalizeSmartSecretKernelAccount(accountArg);
|
|
753
|
+
}
|
|
754
|
+
catch {
|
|
755
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
756
|
+
}
|
|
757
|
+
return this.reserveOperation((deadlineArg, markAcquiredArg) => this.withLease(deadlineArg, markAcquiredArg, async () => {
|
|
758
|
+
const directoryHandle = await ensureDirectory(this.directoryPath);
|
|
759
|
+
const id = entryId(this.options, account);
|
|
760
|
+
let value;
|
|
761
|
+
let operationError;
|
|
762
|
+
try {
|
|
763
|
+
await this.verifyManifest(directoryHandle);
|
|
764
|
+
value = await readStrictJsonFile(directoryHandle, `${id}.sealed.json`, maximumEnvelopeBytes);
|
|
765
|
+
}
|
|
766
|
+
catch (errorArg) {
|
|
767
|
+
operationError = errorArg;
|
|
768
|
+
}
|
|
769
|
+
operationError = await closeFileHandleWithError(directoryHandle, operationError);
|
|
770
|
+
if (operationError)
|
|
771
|
+
throw operationError;
|
|
772
|
+
if (value === undefined)
|
|
773
|
+
return null;
|
|
774
|
+
const parsed = parseEnvelope(value);
|
|
775
|
+
const aad = entryAad(this.options, account, id, fingerprint(this.masterKey));
|
|
776
|
+
try {
|
|
777
|
+
if (parsed.envelope.entryId !== id
|
|
778
|
+
|| parsed.envelope.masterKeyFingerprint !== fingerprint(this.masterKey))
|
|
779
|
+
throw createSmartSecretSealedFileStoreError('ENVELOPE_INVALID');
|
|
780
|
+
try {
|
|
781
|
+
return await plugins.smartcrypto.aesGcmDecrypt({
|
|
782
|
+
key: this.masterKey,
|
|
783
|
+
encryptedData: parsed.encryptedData,
|
|
784
|
+
aad,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
catch {
|
|
788
|
+
throw createSmartSecretSealedFileStoreError('AUTHENTICATION_FAILED');
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
finally {
|
|
792
|
+
aad.fill(0);
|
|
793
|
+
parsed.encryptedData.nonce.fill(0);
|
|
794
|
+
parsed.encryptedData.ciphertext.fill(0);
|
|
795
|
+
parsed.encryptedData.tag.fill(0);
|
|
796
|
+
}
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
799
|
+
setEntry(accountArg, valueArg) {
|
|
800
|
+
let account;
|
|
801
|
+
try {
|
|
802
|
+
account = normalizeSmartSecretKernelAccount(accountArg);
|
|
803
|
+
}
|
|
804
|
+
catch {
|
|
805
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
806
|
+
}
|
|
807
|
+
let value;
|
|
808
|
+
try {
|
|
809
|
+
if (!(valueArg instanceof Uint8Array)) {
|
|
810
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
811
|
+
}
|
|
812
|
+
if (valueArg.byteLength > smartSecretSealedFileMaximumEntryBytes) {
|
|
813
|
+
throw createSmartSecretSealedFileStoreError('SIZE_LIMIT_EXCEEDED');
|
|
814
|
+
}
|
|
815
|
+
value = new Uint8Array(valueArg);
|
|
816
|
+
}
|
|
817
|
+
catch (errorArg) {
|
|
818
|
+
if (errorArg instanceof SmartSecretSealedFileStoreError)
|
|
819
|
+
throw errorArg;
|
|
820
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
821
|
+
}
|
|
822
|
+
let result;
|
|
823
|
+
try {
|
|
824
|
+
result = this.reserveOperation((deadlineArg, markAcquiredArg) => this.withLease(deadlineArg, markAcquiredArg, async () => {
|
|
825
|
+
const directoryHandle = await ensureDirectory(this.directoryPath);
|
|
826
|
+
let encrypted;
|
|
827
|
+
const id = entryId(this.options, account);
|
|
828
|
+
const keyFingerprint = fingerprint(this.masterKey);
|
|
829
|
+
const aad = entryAad(this.options, account, id, keyFingerprint);
|
|
830
|
+
let operationError;
|
|
831
|
+
try {
|
|
832
|
+
await this.verifyManifest(directoryHandle);
|
|
833
|
+
try {
|
|
834
|
+
encrypted = await plugins.smartcrypto.aesGcmEncrypt({
|
|
835
|
+
key: this.masterKey,
|
|
836
|
+
plaintext: value,
|
|
837
|
+
aad,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
catch {
|
|
841
|
+
throw createSmartSecretSealedFileStoreError('AUTHENTICATION_FAILED');
|
|
842
|
+
}
|
|
843
|
+
const envelope = {
|
|
844
|
+
schemaVersion,
|
|
845
|
+
profile,
|
|
846
|
+
entryId: id,
|
|
847
|
+
masterKeyFingerprint: keyFingerprint,
|
|
848
|
+
...encodeEncryptedData(encrypted),
|
|
849
|
+
};
|
|
850
|
+
await atomicWriteJson(directoryHandle, `${id}.sealed.json`, envelope);
|
|
851
|
+
}
|
|
852
|
+
catch (errorArg) {
|
|
853
|
+
operationError = errorArg;
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
aad.fill(0);
|
|
857
|
+
encrypted?.nonce.fill(0);
|
|
858
|
+
encrypted?.ciphertext.fill(0);
|
|
859
|
+
encrypted?.tag.fill(0);
|
|
860
|
+
}
|
|
861
|
+
finally {
|
|
862
|
+
operationError = await closeFileHandleWithError(directoryHandle, operationError);
|
|
863
|
+
}
|
|
864
|
+
if (operationError)
|
|
865
|
+
throw operationError;
|
|
866
|
+
}));
|
|
867
|
+
}
|
|
868
|
+
catch (errorArg) {
|
|
869
|
+
value.fill(0);
|
|
870
|
+
throw errorArg;
|
|
871
|
+
}
|
|
872
|
+
return result.finally(() => value.fill(0));
|
|
873
|
+
}
|
|
874
|
+
deleteEntry(accountArg) {
|
|
875
|
+
let account;
|
|
876
|
+
try {
|
|
877
|
+
account = normalizeSmartSecretKernelAccount(accountArg);
|
|
878
|
+
}
|
|
879
|
+
catch {
|
|
880
|
+
throw createSmartSecretSealedFileStoreError('INVALID_ARGUMENT');
|
|
881
|
+
}
|
|
882
|
+
return this.reserveOperation((deadlineArg, markAcquiredArg) => this.withLease(deadlineArg, markAcquiredArg, async () => {
|
|
883
|
+
const directoryHandle = await ensureDirectory(this.directoryPath);
|
|
884
|
+
let result;
|
|
885
|
+
let operationError;
|
|
886
|
+
try {
|
|
887
|
+
await this.verifyManifest(directoryHandle);
|
|
888
|
+
const id = entryId(this.options, account);
|
|
889
|
+
result = await removeAndSync(directoryHandle, `${id}.sealed.json`);
|
|
890
|
+
}
|
|
891
|
+
catch (errorArg) {
|
|
892
|
+
operationError = errorArg;
|
|
893
|
+
}
|
|
894
|
+
operationError = await closeFileHandleWithError(directoryHandle, operationError);
|
|
895
|
+
if (operationError)
|
|
896
|
+
throw operationError;
|
|
897
|
+
return result;
|
|
898
|
+
}));
|
|
899
|
+
}
|
|
900
|
+
close() {
|
|
901
|
+
if (this.closePromise)
|
|
902
|
+
return this.closePromise;
|
|
903
|
+
this.lifecycleState = 'closing';
|
|
904
|
+
const operationTail = this.operationTail;
|
|
905
|
+
const closePromise = operationTail.finally(() => {
|
|
906
|
+
this.masterKey.fill(0);
|
|
907
|
+
this.lifecycleState = 'closed';
|
|
908
|
+
});
|
|
909
|
+
this.closePromise = closePromise;
|
|
910
|
+
return closePromise;
|
|
911
|
+
}
|
|
912
|
+
reserveOperation(operationArg) {
|
|
913
|
+
if (this.lifecycleState !== 'ready') {
|
|
914
|
+
throw createSmartSecretSealedFileStoreError('STORE_CLOSED');
|
|
915
|
+
}
|
|
916
|
+
if (this.pendingOperations >= maximumPendingOperations) {
|
|
917
|
+
throw createSmartSecretSealedFileStoreError('OPERATION_LIMIT_REACHED');
|
|
918
|
+
}
|
|
919
|
+
this.pendingOperations += 1;
|
|
920
|
+
const deadline = performance.now() + operationTimeoutMs;
|
|
921
|
+
let admissionExpired = false;
|
|
922
|
+
let admissionTimer;
|
|
923
|
+
let rejectAdmission;
|
|
924
|
+
const admissionTimeout = new Promise((_resolveArg, rejectArg) => {
|
|
925
|
+
rejectAdmission = rejectArg;
|
|
926
|
+
});
|
|
927
|
+
const clearAdmissionTimer = () => {
|
|
928
|
+
if (admissionTimer) {
|
|
929
|
+
clearTimeout(admissionTimer);
|
|
930
|
+
admissionTimer = undefined;
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
const markAcquired = () => clearAdmissionTimer();
|
|
934
|
+
admissionTimer = setTimeout(() => {
|
|
935
|
+
admissionExpired = true;
|
|
936
|
+
admissionTimer = undefined;
|
|
937
|
+
rejectAdmission(createSmartSecretSealedFileStoreError('MUTEX_FAILED'));
|
|
938
|
+
}, operationTimeoutMs);
|
|
939
|
+
const execution = this.operationTail.then(() => {
|
|
940
|
+
if (admissionExpired || performance.now() >= deadline) {
|
|
941
|
+
throw createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
942
|
+
}
|
|
943
|
+
return operationArg(deadline, markAcquired);
|
|
944
|
+
});
|
|
945
|
+
this.operationTail = execution.then(() => undefined, () => undefined);
|
|
946
|
+
execution.then(() => {
|
|
947
|
+
clearAdmissionTimer();
|
|
948
|
+
this.pendingOperations -= 1;
|
|
949
|
+
}, () => {
|
|
950
|
+
clearAdmissionTimer();
|
|
951
|
+
this.pendingOperations -= 1;
|
|
952
|
+
});
|
|
953
|
+
return Promise.race([execution, admissionTimeout]);
|
|
954
|
+
}
|
|
955
|
+
async withLease(deadlineArg, markAcquiredArg, operationArg) {
|
|
956
|
+
const lease = await acquireLease(this.options, deadlineArg);
|
|
957
|
+
markAcquiredArg();
|
|
958
|
+
let result;
|
|
959
|
+
let operationError;
|
|
960
|
+
try {
|
|
961
|
+
result = await operationArg();
|
|
962
|
+
}
|
|
963
|
+
catch (errorArg) {
|
|
964
|
+
operationError = errorArg;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
await lease.release();
|
|
968
|
+
}
|
|
969
|
+
catch {
|
|
970
|
+
wipeDiscardedResult(result);
|
|
971
|
+
if (mutationOutcomeUnknown(operationError))
|
|
972
|
+
throw operationError;
|
|
973
|
+
throw createSmartSecretSealedFileStoreError('MUTEX_FAILED');
|
|
974
|
+
}
|
|
975
|
+
if (operationError)
|
|
976
|
+
throw operationError;
|
|
977
|
+
return result;
|
|
978
|
+
}
|
|
979
|
+
async verifyManifest(directoryHandleArg) {
|
|
980
|
+
const value = await readStrictJsonFile(directoryHandleArg, manifestName, 4 * 1_024);
|
|
981
|
+
if (value === undefined)
|
|
982
|
+
throw createSmartSecretSealedFileStoreError('FILESYSTEM_FAILED');
|
|
983
|
+
const manifest = parseManifest(value, this.options);
|
|
984
|
+
if (manifest.masterKeyFingerprint !== fingerprint(this.masterKey)) {
|
|
985
|
+
throw createSmartSecretSealedFileStoreError('MASTER_KEY_CONFLICT');
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzZWNyZXQuc2VhbGVkZmlsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c2VjcmV0LnNlYWxlZGZpbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEVBQ0wsMkJBQTJCLEdBQzVCLE1BQU0sK0JBQStCLENBQUM7QUFDdkMsT0FBTyxFQUNMLGlDQUFpQyxFQUNqQyxpQ0FBaUMsR0FFbEMsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxQyxPQUFPLEVBQ0wsK0JBQStCLEVBQy9CLHFDQUFxQyxHQUN0QyxNQUFNLG1DQUFtQyxDQUFDO0FBRTNDLE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLEdBQUcsR0FBRyxLQUFLLENBQUM7QUFFbEUsTUFBTSxhQUFhLEdBQUcsQ0FBVSxDQUFDO0FBQ2pDLE1BQU0sT0FBTyxHQUFHLHdDQUFpRCxDQUFDO0FBQ2xFLE1BQU0sWUFBWSxHQUFHLGVBQWUsQ0FBQztBQUNyQyxNQUFNLGNBQWMsR0FBRyxFQUFFLENBQUM7QUFDMUIsTUFBTSxvQkFBb0IsR0FBRyxHQUFHLEdBQUcsS0FBSyxDQUFDO0FBQ3pDLE1BQU0sa0JBQWtCLEdBQUcsTUFBTSxDQUFDO0FBQ2xDLE1BQU0sc0JBQXNCLEdBQUcscUJBQXFCLENBQUM7QUFDckQsTUFBTSxnQkFBZ0IsR0FBRyxtQ0FBbUMsQ0FBQztBQUM3RCxNQUFNLG9CQUFvQixHQUFHLHdDQUF3QyxDQUFDO0FBQ3RFLE1BQU0sd0JBQXdCLEdBQUcsRUFBRSxDQUFDO0FBQ3BDLE1BQU0sT0FBTyxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7QUF1RGxDLE1BQU0sTUFBTSxHQUFHLENBQUMsR0FBRyxTQUF1QixFQUFjLEVBQUU7SUFDeEQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDakQsS0FBSyxNQUFNLEtB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
|