@qntx/wallet-worker 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +64 -2
- package/dist/browser.d.mts +20 -0
- package/dist/browser.mjs +227 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/kernel.node.js +10618 -0
- package/dist/kernel.worker.js +10678 -0
- package/dist/node.d.mts +21 -0
- package/dist/node.mjs +1044 -0
- package/dist/node.mjs.map +1 -0
- package/package.json +80 -4
package/dist/node.mjs
ADDED
|
@@ -0,0 +1,1044 @@
|
|
|
1
|
+
import { WalletError } from "@qntx/wallet";
|
|
2
|
+
import { connectKernel } from "@qntx/wallet/host";
|
|
3
|
+
import { MessageChannel, Worker } from "node:worker_threads";
|
|
4
|
+
//#region ../wallet-kernel/src/errors.ts
|
|
5
|
+
/**
|
|
6
|
+
* The only error type kernel code throws. Its code is the public `WalletErrorCode`: the kernel
|
|
7
|
+
* returns it verbatim in the error payload. Messages are static strings: they must never echo user
|
|
8
|
+
* input or secret contents.
|
|
9
|
+
*/
|
|
10
|
+
var KernelError = class extends Error {
|
|
11
|
+
code;
|
|
12
|
+
retryAt;
|
|
13
|
+
/**
|
|
14
|
+
* True when the error proves the stored vault itself is corrupt — the kernel latches the
|
|
15
|
+
* corrupted state on it. Ordinary failures (a wrong factor, a bad param) leave it false.
|
|
16
|
+
*/
|
|
17
|
+
corrupted;
|
|
18
|
+
constructor(code, message, retryAt, corrupted = false) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "KernelError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.retryAt = retryAt;
|
|
23
|
+
this.corrupted = corrupted;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/utils.js
|
|
28
|
+
/**
|
|
29
|
+
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
|
|
30
|
+
* @param a - value to test
|
|
31
|
+
* @returns `true` when the value is a Uint8Array-compatible view.
|
|
32
|
+
* @example
|
|
33
|
+
* Check whether a value is a Uint8Array-compatible view.
|
|
34
|
+
* ```ts
|
|
35
|
+
* isBytes(new Uint8Array([1, 2, 3]));
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function isBytes(a) {
|
|
39
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
40
|
+
}
|
|
41
|
+
const atitle = (title) => title ? `"${title}" ` : "";
|
|
42
|
+
/**
|
|
43
|
+
* Asserts something is a non-negative integer.
|
|
44
|
+
* @param n - number to validate
|
|
45
|
+
* @param title - label included in thrown errors
|
|
46
|
+
* @returns The validated number.
|
|
47
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
48
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
49
|
+
* @example
|
|
50
|
+
* Validate a non-negative integer option.
|
|
51
|
+
* ```ts
|
|
52
|
+
* anumber(32, 'length');
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function anumber(n, title = "") {
|
|
56
|
+
if (typeof n !== "number") throw new TypeError(atitle(title) + "expected number, got " + typeof n);
|
|
57
|
+
if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(atitle(title) + "expected integer >= 0, got " + n);
|
|
58
|
+
return n;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Asserts something is Uint8Array.
|
|
62
|
+
* @param value - value to validate
|
|
63
|
+
* @param length - optional exact length constraint
|
|
64
|
+
* @param title - label included in thrown errors
|
|
65
|
+
* @returns The validated byte array.
|
|
66
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
67
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
68
|
+
* @example
|
|
69
|
+
* Validate that a value is a byte array.
|
|
70
|
+
* ```ts
|
|
71
|
+
* abytes(new Uint8Array([1, 2, 3]));
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
function abytes(value, length, title = "") {
|
|
75
|
+
if (isBytes(value) && (length === void 0 || value.length === length)) return value;
|
|
76
|
+
if (length !== void 0) anumber(length, "length");
|
|
77
|
+
const bytes = isBytes(value);
|
|
78
|
+
const ofLen = length !== void 0 ? ` of length ${length}` : "";
|
|
79
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
80
|
+
const message = atitle(title) + "expected Uint8Array" + ofLen + ", got " + got;
|
|
81
|
+
if (!bytes) throw new TypeError(message);
|
|
82
|
+
throw new RangeError(message);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Asserts something is a wrapped hash constructor.
|
|
86
|
+
* @param h - hash constructor to validate
|
|
87
|
+
* @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}
|
|
88
|
+
* @throws On invalid hash metadata ranges or values. {@link RangeError}
|
|
89
|
+
* @throws If the hash metadata allows empty outputs or block sizes. {@link Error}
|
|
90
|
+
* @example
|
|
91
|
+
* Validate a callable hash wrapper.
|
|
92
|
+
* ```ts
|
|
93
|
+
* import { ahash } from '@noble/hashes/utils.js';
|
|
94
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
95
|
+
* ahash(sha256);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
function ahash(h) {
|
|
99
|
+
if (typeof h !== "function" || typeof h.create !== "function") throw new TypeError("expected hash wrapped by utils.createHasher");
|
|
100
|
+
anumber(h.outputLen);
|
|
101
|
+
anumber(h.blockLen);
|
|
102
|
+
if (h.outputLen < 1 || h.blockLen < 1) throw new Error("hash blockLen / outputLen must be >= 1");
|
|
103
|
+
}
|
|
104
|
+
const aobject = (value, label) => {
|
|
105
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
|
|
106
|
+
};
|
|
107
|
+
const aopts = (value, label) => {
|
|
108
|
+
aobject(value, label);
|
|
109
|
+
const proto = Object.getPrototypeOf(value);
|
|
110
|
+
if (proto !== Object.prototype && proto !== null) throw new TypeError(`"${label}" expected plain object`);
|
|
111
|
+
if (Object.hasOwn(value, "__proto__")) throw new TypeError(`"${label}.__proto__" is not allowed`);
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Asserts a hash instance has not been destroyed or finished.
|
|
115
|
+
* @param instance - hash instance to validate
|
|
116
|
+
* @param checkFinished - whether to reject finalized instances
|
|
117
|
+
* @throws If the hash instance has already been destroyed or finalized. {@link Error}
|
|
118
|
+
* @example
|
|
119
|
+
* Validate that a hash instance is still usable.
|
|
120
|
+
* ```ts
|
|
121
|
+
* import { aexists } from '@noble/hashes/utils.js';
|
|
122
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
123
|
+
* const hash = sha256.create();
|
|
124
|
+
* aexists(hash);
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
function aexists(instance, checkFinished = true) {
|
|
128
|
+
if (instance.destroyed) throw new Error("hash was destroyed");
|
|
129
|
+
if (checkFinished && instance.finished) throw new Error("digest() was already called");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Asserts output is a sufficiently-sized byte array.
|
|
133
|
+
* @param out - destination buffer
|
|
134
|
+
* @param instance - hash instance providing output length
|
|
135
|
+
* Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.
|
|
136
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
137
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
138
|
+
* @example
|
|
139
|
+
* Validate a caller-provided digest buffer.
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { aoutput } from '@noble/hashes/utils.js';
|
|
142
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
143
|
+
* const hash = sha256.create();
|
|
144
|
+
* aoutput(new Uint8Array(hash.outputLen), hash);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
function aoutput(out, instance) {
|
|
148
|
+
abytes(out, void 0, "output");
|
|
149
|
+
const min = instance.outputLen;
|
|
150
|
+
if (!(out.length >= min)) throw new RangeError("\"output\" expected length >= " + min);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Zeroizes typed arrays in place. Warning: JS provides no guarantees.
|
|
154
|
+
* @param arrays - arrays to overwrite with zeros
|
|
155
|
+
* @example
|
|
156
|
+
* Zeroize sensitive buffers in place.
|
|
157
|
+
* ```ts
|
|
158
|
+
* clean(new Uint8Array([1, 2, 3]));
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
function clean(...arrays) {
|
|
162
|
+
for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Creates a DataView for byte-level manipulation.
|
|
166
|
+
* @param arr - source typed array
|
|
167
|
+
* @returns DataView over the same buffer region.
|
|
168
|
+
* @example
|
|
169
|
+
* Create a DataView over an existing buffer.
|
|
170
|
+
* ```ts
|
|
171
|
+
* createView(new Uint8Array(4));
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
function createView(arr) {
|
|
175
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Rotate-right operation for uint32 values.
|
|
179
|
+
* @param word - source word
|
|
180
|
+
* @param shift - shift amount in bits
|
|
181
|
+
* @returns Rotated word.
|
|
182
|
+
* @example
|
|
183
|
+
* Rotate a 32-bit word to the right.
|
|
184
|
+
* ```ts
|
|
185
|
+
* rotr(0x12345678, 8);
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
function rotr(word, shift) {
|
|
189
|
+
return word << 32 - shift | word >>> shift;
|
|
190
|
+
}
|
|
191
|
+
const hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
|
|
192
|
+
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
193
|
+
/**
|
|
194
|
+
* Convert byte array to hex string.
|
|
195
|
+
* Uses the built-in function when available and assumes it matches the tested
|
|
196
|
+
* fallback semantics.
|
|
197
|
+
* @param bytes - bytes to encode
|
|
198
|
+
* @returns Lowercase hexadecimal string.
|
|
199
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
200
|
+
* @example
|
|
201
|
+
* Convert bytes to lowercase hexadecimal.
|
|
202
|
+
* ```ts
|
|
203
|
+
* bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
function bytesToHex(bytes) {
|
|
207
|
+
abytes(bytes);
|
|
208
|
+
if (hasHexBuiltin) return bytes.toHex();
|
|
209
|
+
let hex = "";
|
|
210
|
+
for (let i = 0; i < bytes.length; i++) hex += hexes[bytes[i]];
|
|
211
|
+
return hex;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Converts string to bytes using UTF8 encoding.
|
|
215
|
+
* Built-in doesn't validate input to be string: we do the check.
|
|
216
|
+
* Non-ASCII details are delegated to the platform `TextEncoder`.
|
|
217
|
+
* @param str - string to encode
|
|
218
|
+
* @returns UTF-8 encoded bytes.
|
|
219
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
220
|
+
* @example
|
|
221
|
+
* Encode a string as UTF-8 bytes.
|
|
222
|
+
* ```ts
|
|
223
|
+
* utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
function utf8ToBytes(str) {
|
|
227
|
+
if (typeof str !== "string") throw new TypeError("string expected");
|
|
228
|
+
const encoded = new TextEncoder().encode(str);
|
|
229
|
+
try {
|
|
230
|
+
return new Uint8Array(encoded);
|
|
231
|
+
} finally {
|
|
232
|
+
clean(encoded);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Copies several Uint8Arrays into one.
|
|
237
|
+
* @param arrays - arrays to concatenate
|
|
238
|
+
* @returns Concatenated byte array.
|
|
239
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
240
|
+
* @example
|
|
241
|
+
* Concatenate multiple byte arrays.
|
|
242
|
+
* ```ts
|
|
243
|
+
* concatBytes(new Uint8Array([1]), new Uint8Array([2]));
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
function concatBytes(...arrays) {
|
|
247
|
+
let sum = 0;
|
|
248
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
249
|
+
const a = arrays[i];
|
|
250
|
+
abytes(a);
|
|
251
|
+
sum += a.length;
|
|
252
|
+
}
|
|
253
|
+
const res = new Uint8Array(sum);
|
|
254
|
+
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
255
|
+
const a = arrays[i];
|
|
256
|
+
res.set(a, pad);
|
|
257
|
+
pad += a.length;
|
|
258
|
+
}
|
|
259
|
+
return res;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Merges default options and passed options.
|
|
263
|
+
* @param defaults - base option object
|
|
264
|
+
* @param opts - user overrides
|
|
265
|
+
* @param title - label included in thrown override errors
|
|
266
|
+
* @returns Fresh merged option object with a null prototype.
|
|
267
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
268
|
+
* @example
|
|
269
|
+
* Merge user overrides onto default options.
|
|
270
|
+
* ```ts
|
|
271
|
+
* checkOpts({ dkLen: 32 }, { asyncTick: 10 });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
function checkOpts(defaults, opts, title = "opts") {
|
|
275
|
+
aopts(defaults, "defaults");
|
|
276
|
+
if (opts !== void 0) aopts(opts, title);
|
|
277
|
+
return Object.assign(Object.create(null), defaults, opts);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Creates a callable hash function from a stateful class constructor.
|
|
281
|
+
* @param hashCons - hash constructor or factory
|
|
282
|
+
* @param info - optional metadata such as DER OID
|
|
283
|
+
* @returns Frozen callable hash wrapper with `.create()`.
|
|
284
|
+
* Wrapper construction eagerly calls `hashCons(undefined)` once to read
|
|
285
|
+
* `outputLen` / `blockLen`, so constructor side effects happen at module
|
|
286
|
+
* init time.
|
|
287
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
288
|
+
* @example
|
|
289
|
+
* Wrap a stateful hash constructor into a callable helper.
|
|
290
|
+
* ```ts
|
|
291
|
+
* import { createHasher } from '@noble/hashes/utils.js';
|
|
292
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
293
|
+
* const wrapped = createHasher(sha256.create, { oid: sha256.oid });
|
|
294
|
+
* wrapped(new Uint8Array([1]));
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
function createHasher(hashCons, info = {}) {
|
|
298
|
+
if (typeof hashCons !== "function") throw new TypeError("\"hashCons\" expected function, got type=" + typeof hashCons);
|
|
299
|
+
info = checkOpts({}, info, "info");
|
|
300
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
301
|
+
const tmp = hashCons(void 0);
|
|
302
|
+
hashC.outputLen = tmp.outputLen;
|
|
303
|
+
hashC.blockLen = tmp.blockLen;
|
|
304
|
+
hashC.canXOF = tmp.canXOF;
|
|
305
|
+
hashC.create = (opts) => hashCons(opts);
|
|
306
|
+
Object.assign(hashC, info);
|
|
307
|
+
return Object.freeze(hashC);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.
|
|
311
|
+
* @param suffix - final OID byte for the selected hash.
|
|
312
|
+
* The helper accepts any byte even though only the documented NIST hash
|
|
313
|
+
* suffixes are meaningful downstream.
|
|
314
|
+
* @returns Object containing the DER-encoded OID.
|
|
315
|
+
* @example
|
|
316
|
+
* Build OID metadata for a NIST hash.
|
|
317
|
+
* ```ts
|
|
318
|
+
* oidNist(0x01);
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
const oidNist = (suffix) => ({ oid: Uint8Array.from([
|
|
322
|
+
6,
|
|
323
|
+
9,
|
|
324
|
+
96,
|
|
325
|
+
134,
|
|
326
|
+
72,
|
|
327
|
+
1,
|
|
328
|
+
101,
|
|
329
|
+
3,
|
|
330
|
+
4,
|
|
331
|
+
2,
|
|
332
|
+
suffix
|
|
333
|
+
]) });
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/hmac.js
|
|
336
|
+
/**
|
|
337
|
+
* HMAC: RFC2104 message authentication code.
|
|
338
|
+
* @module
|
|
339
|
+
*/
|
|
340
|
+
/**
|
|
341
|
+
* Internal class for HMAC.
|
|
342
|
+
* Accepts any byte key, although RFC 2104 §3 recommends keys at least
|
|
343
|
+
* `HashLen` bytes long.
|
|
344
|
+
*/
|
|
345
|
+
var _HMAC = class {
|
|
346
|
+
oHash;
|
|
347
|
+
iHash;
|
|
348
|
+
blockLen;
|
|
349
|
+
outputLen;
|
|
350
|
+
canXOF = false;
|
|
351
|
+
finished = false;
|
|
352
|
+
destroyed = false;
|
|
353
|
+
constructor(hash, key) {
|
|
354
|
+
ahash(hash);
|
|
355
|
+
abytes(key, void 0, "key");
|
|
356
|
+
this.iHash = hash.create();
|
|
357
|
+
if (typeof this.iHash.update !== "function") throw new Error("expected Hash instance");
|
|
358
|
+
this.blockLen = this.iHash.blockLen;
|
|
359
|
+
this.outputLen = this.iHash.outputLen;
|
|
360
|
+
const blockLen = this.blockLen;
|
|
361
|
+
const pad = new Uint8Array(blockLen);
|
|
362
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
363
|
+
for (let i = 0; i < pad.length; i++) pad[i] ^= 54;
|
|
364
|
+
this.iHash.update(pad);
|
|
365
|
+
this.oHash = hash.create();
|
|
366
|
+
for (let i = 0; i < pad.length; i++) pad[i] ^= 106;
|
|
367
|
+
this.oHash.update(pad);
|
|
368
|
+
clean(pad);
|
|
369
|
+
}
|
|
370
|
+
update(buf) {
|
|
371
|
+
aexists(this);
|
|
372
|
+
this.iHash.update(buf);
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
digestInto(out) {
|
|
376
|
+
aexists(this);
|
|
377
|
+
aoutput(out, this);
|
|
378
|
+
this.finished = true;
|
|
379
|
+
const buf = out.subarray(0, this.outputLen);
|
|
380
|
+
this.iHash.digestInto(buf);
|
|
381
|
+
this.oHash.update(buf);
|
|
382
|
+
this.oHash.digestInto(buf);
|
|
383
|
+
this.destroy();
|
|
384
|
+
}
|
|
385
|
+
digest() {
|
|
386
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
387
|
+
this.digestInto(out);
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
_cloneInto(to) {
|
|
391
|
+
to ||= Object.create(Object.getPrototypeOf(this), {});
|
|
392
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen, canXOF } = this;
|
|
393
|
+
to = to;
|
|
394
|
+
to.finished = finished;
|
|
395
|
+
to.destroyed = destroyed;
|
|
396
|
+
to.blockLen = blockLen;
|
|
397
|
+
to.outputLen = outputLen;
|
|
398
|
+
to.canXOF = canXOF;
|
|
399
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
400
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
401
|
+
return to;
|
|
402
|
+
}
|
|
403
|
+
clone() {
|
|
404
|
+
return this._cloneInto();
|
|
405
|
+
}
|
|
406
|
+
destroy() {
|
|
407
|
+
this.destroyed = true;
|
|
408
|
+
this.oHash.destroy();
|
|
409
|
+
this.iHash.destroy();
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const hmac = /* @__PURE__ */ (() => {
|
|
413
|
+
const hmac_ = ((hash, key, message) => new _HMAC(hash, key).update(message).digest());
|
|
414
|
+
hmac_.create = (hash, key) => new _HMAC(hash, key);
|
|
415
|
+
return hmac_;
|
|
416
|
+
})();
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/_u64.js
|
|
419
|
+
const fromNumH = (n) => n / 2 ** 32 | 0;
|
|
420
|
+
const fromNumL = (n) => n >>> 0;
|
|
421
|
+
function setU64FromNum(view, byteOffset, n, isLE) {
|
|
422
|
+
const h = fromNumH(n);
|
|
423
|
+
const l = fromNumL(n);
|
|
424
|
+
view.setUint32(byteOffset, isLE ? l : h, isLE);
|
|
425
|
+
view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/_md.js
|
|
429
|
+
/**
|
|
430
|
+
* Internal Merkle-Damgard hash utils.
|
|
431
|
+
* @module
|
|
432
|
+
*/
|
|
433
|
+
/**
|
|
434
|
+
* Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.
|
|
435
|
+
* Returns bits from `b` when `a` is set, otherwise from `c`.
|
|
436
|
+
* The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never
|
|
437
|
+
* set the same bit.
|
|
438
|
+
* @param a - selector word
|
|
439
|
+
* @param b - word chosen when selector bit is set
|
|
440
|
+
* @param c - word chosen when selector bit is clear
|
|
441
|
+
* @returns Mixed 32-bit word.
|
|
442
|
+
* @example
|
|
443
|
+
* Combine three words with the shared 32-bit choice primitive.
|
|
444
|
+
* ```ts
|
|
445
|
+
* Chi(0xffffffff, 0x12345678, 0x87654321);
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
function Chi(a, b, c) {
|
|
449
|
+
return a & b ^ ~a & c;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Shared 32-bit majority primitive reused by SHA-256 and SHA-1.
|
|
453
|
+
* Returns bits shared by at least two inputs.
|
|
454
|
+
* @param a - first input word
|
|
455
|
+
* @param b - second input word
|
|
456
|
+
* @param c - third input word
|
|
457
|
+
* @returns Mixed 32-bit word.
|
|
458
|
+
* @example
|
|
459
|
+
* Combine three words with the shared 32-bit majority primitive.
|
|
460
|
+
* ```ts
|
|
461
|
+
* Maj(0xffffffff, 0x12345678, 0x87654321);
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
function Maj(a, b, c) {
|
|
465
|
+
return a & b ^ a & c ^ b & c;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Merkle-Damgard hash construction base class.
|
|
469
|
+
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
470
|
+
* Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit
|
|
471
|
+
* strings with partial-byte tails.
|
|
472
|
+
* @param blockLen - internal block size in bytes
|
|
473
|
+
* @param outputLen - digest size in bytes
|
|
474
|
+
* @param padOffset - trailing length field size in bytes
|
|
475
|
+
* @param isLE - whether length and state words are encoded in little-endian
|
|
476
|
+
* @example
|
|
477
|
+
* Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.
|
|
478
|
+
* ```ts
|
|
479
|
+
* import { _SHA1 } from '@noble/hashes/legacy.js';
|
|
480
|
+
* const hash = new _SHA1();
|
|
481
|
+
* hash.update(new Uint8Array([97, 98, 99]));
|
|
482
|
+
* hash.digest();
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
var HashMD = class {
|
|
486
|
+
blockLen;
|
|
487
|
+
outputLen;
|
|
488
|
+
canXOF = false;
|
|
489
|
+
padOffset;
|
|
490
|
+
isLE;
|
|
491
|
+
buffer;
|
|
492
|
+
view;
|
|
493
|
+
finished = false;
|
|
494
|
+
length = 0;
|
|
495
|
+
pos = 0;
|
|
496
|
+
destroyed = false;
|
|
497
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
498
|
+
this.blockLen = blockLen;
|
|
499
|
+
this.outputLen = outputLen;
|
|
500
|
+
this.padOffset = padOffset;
|
|
501
|
+
this.isLE = isLE;
|
|
502
|
+
this.buffer = new Uint8Array(blockLen);
|
|
503
|
+
this.view = createView(this.buffer);
|
|
504
|
+
}
|
|
505
|
+
update(data) {
|
|
506
|
+
aexists(this);
|
|
507
|
+
abytes(data);
|
|
508
|
+
const { view, buffer, blockLen } = this;
|
|
509
|
+
const len = data.length;
|
|
510
|
+
let processed = false;
|
|
511
|
+
for (let pos = 0; pos < len;) {
|
|
512
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
513
|
+
if (take === blockLen) {
|
|
514
|
+
const dataView = createView(data);
|
|
515
|
+
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
|
|
516
|
+
processed = true;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
buffer.set(pos === 0 && take === len ? data : data.subarray(pos, pos + take), this.pos);
|
|
520
|
+
this.pos += take;
|
|
521
|
+
pos += take;
|
|
522
|
+
if (this.pos === blockLen) {
|
|
523
|
+
this.process(view, 0);
|
|
524
|
+
this.pos = 0;
|
|
525
|
+
processed = true;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
this.length += data.length;
|
|
529
|
+
if (processed) this.roundClean();
|
|
530
|
+
return this;
|
|
531
|
+
}
|
|
532
|
+
digestInto(out) {
|
|
533
|
+
aexists(this);
|
|
534
|
+
aoutput(out, this);
|
|
535
|
+
this.finished = true;
|
|
536
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
537
|
+
let { pos } = this;
|
|
538
|
+
buffer[pos++] = 128;
|
|
539
|
+
buffer.fill(0, pos);
|
|
540
|
+
if (this.padOffset > blockLen - pos) {
|
|
541
|
+
this.process(view, 0);
|
|
542
|
+
buffer.fill(0);
|
|
543
|
+
}
|
|
544
|
+
setU64FromNum(view, blockLen - 8, this.length * 8, isLE);
|
|
545
|
+
this.process(view, 0);
|
|
546
|
+
this.roundClean();
|
|
547
|
+
const oview = out === buffer ? view : createView(out);
|
|
548
|
+
const len = this.outputLen;
|
|
549
|
+
const outLen = len / 4;
|
|
550
|
+
const state = this.get();
|
|
551
|
+
if (len % 4 || outLen > state.length) throw new Error("invalid outputLen");
|
|
552
|
+
for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);
|
|
553
|
+
}
|
|
554
|
+
digest() {
|
|
555
|
+
const { buffer, outputLen } = this;
|
|
556
|
+
this.digestInto(buffer);
|
|
557
|
+
const res = buffer.slice(0, outputLen);
|
|
558
|
+
this.destroy();
|
|
559
|
+
return res;
|
|
560
|
+
}
|
|
561
|
+
_cloneIntoMeta(to) {
|
|
562
|
+
const { buffer, length, finished, destroyed, pos } = this;
|
|
563
|
+
to.destroyed = destroyed;
|
|
564
|
+
to.finished = finished;
|
|
565
|
+
to.length = length;
|
|
566
|
+
to.pos = pos;
|
|
567
|
+
if (pos) to.buffer.set(buffer);
|
|
568
|
+
return to;
|
|
569
|
+
}
|
|
570
|
+
clone() {
|
|
571
|
+
return this._cloneInto();
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
/**
|
|
575
|
+
* Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.
|
|
576
|
+
* Check out `test/misc/sha2-gen-iv.js` for recomputation guide.
|
|
577
|
+
*/
|
|
578
|
+
/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the
|
|
579
|
+
* square roots of the first eight prime numbers. Exported as a shared table; callers must treat
|
|
580
|
+
* it as read-only because constructors copy words from it by index. */
|
|
581
|
+
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
582
|
+
1779033703,
|
|
583
|
+
3144134277,
|
|
584
|
+
1013904242,
|
|
585
|
+
2773480762,
|
|
586
|
+
1359893119,
|
|
587
|
+
2600822924,
|
|
588
|
+
528734635,
|
|
589
|
+
1541459225
|
|
590
|
+
]);
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/sha2.js
|
|
593
|
+
/**
|
|
594
|
+
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
595
|
+
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
596
|
+
* Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and
|
|
597
|
+
* {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.
|
|
598
|
+
* @module
|
|
599
|
+
*/
|
|
600
|
+
/**
|
|
601
|
+
* SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits
|
|
602
|
+
* of the cube roots of the first 64 primes (2..311).
|
|
603
|
+
*/
|
|
604
|
+
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
605
|
+
1116352408,
|
|
606
|
+
1899447441,
|
|
607
|
+
3049323471,
|
|
608
|
+
3921009573,
|
|
609
|
+
961987163,
|
|
610
|
+
1508970993,
|
|
611
|
+
2453635748,
|
|
612
|
+
2870763221,
|
|
613
|
+
3624381080,
|
|
614
|
+
310598401,
|
|
615
|
+
607225278,
|
|
616
|
+
1426881987,
|
|
617
|
+
1925078388,
|
|
618
|
+
2162078206,
|
|
619
|
+
2614888103,
|
|
620
|
+
3248222580,
|
|
621
|
+
3835390401,
|
|
622
|
+
4022224774,
|
|
623
|
+
264347078,
|
|
624
|
+
604807628,
|
|
625
|
+
770255983,
|
|
626
|
+
1249150122,
|
|
627
|
+
1555081692,
|
|
628
|
+
1996064986,
|
|
629
|
+
2554220882,
|
|
630
|
+
2821834349,
|
|
631
|
+
2952996808,
|
|
632
|
+
3210313671,
|
|
633
|
+
3336571891,
|
|
634
|
+
3584528711,
|
|
635
|
+
113926993,
|
|
636
|
+
338241895,
|
|
637
|
+
666307205,
|
|
638
|
+
773529912,
|
|
639
|
+
1294757372,
|
|
640
|
+
1396182291,
|
|
641
|
+
1695183700,
|
|
642
|
+
1986661051,
|
|
643
|
+
2177026350,
|
|
644
|
+
2456956037,
|
|
645
|
+
2730485921,
|
|
646
|
+
2820302411,
|
|
647
|
+
3259730800,
|
|
648
|
+
3345764771,
|
|
649
|
+
3516065817,
|
|
650
|
+
3600352804,
|
|
651
|
+
4094571909,
|
|
652
|
+
275423344,
|
|
653
|
+
430227734,
|
|
654
|
+
506948616,
|
|
655
|
+
659060556,
|
|
656
|
+
883997877,
|
|
657
|
+
958139571,
|
|
658
|
+
1322822218,
|
|
659
|
+
1537002063,
|
|
660
|
+
1747873779,
|
|
661
|
+
1955562222,
|
|
662
|
+
2024104815,
|
|
663
|
+
2227730452,
|
|
664
|
+
2361852424,
|
|
665
|
+
2428436474,
|
|
666
|
+
2756734187,
|
|
667
|
+
3204031479,
|
|
668
|
+
3329325298
|
|
669
|
+
]);
|
|
670
|
+
/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */
|
|
671
|
+
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
672
|
+
/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */
|
|
673
|
+
var SHA2_32B = class extends HashMD {
|
|
674
|
+
A = 0;
|
|
675
|
+
B = 0;
|
|
676
|
+
C = 0;
|
|
677
|
+
D = 0;
|
|
678
|
+
E = 0;
|
|
679
|
+
F = 0;
|
|
680
|
+
G = 0;
|
|
681
|
+
H = 0;
|
|
682
|
+
constructor(outputLen, IV) {
|
|
683
|
+
super(64, outputLen, 8, false);
|
|
684
|
+
this.A = IV[0] | 0;
|
|
685
|
+
this.B = IV[1] | 0;
|
|
686
|
+
this.C = IV[2] | 0;
|
|
687
|
+
this.D = IV[3] | 0;
|
|
688
|
+
this.E = IV[4] | 0;
|
|
689
|
+
this.F = IV[5] | 0;
|
|
690
|
+
this.G = IV[6] | 0;
|
|
691
|
+
this.H = IV[7] | 0;
|
|
692
|
+
}
|
|
693
|
+
get() {
|
|
694
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
695
|
+
return [
|
|
696
|
+
A,
|
|
697
|
+
B,
|
|
698
|
+
C,
|
|
699
|
+
D,
|
|
700
|
+
E,
|
|
701
|
+
F,
|
|
702
|
+
G,
|
|
703
|
+
H
|
|
704
|
+
];
|
|
705
|
+
}
|
|
706
|
+
set(A, B, C, D, E, F, G, H) {
|
|
707
|
+
this.A = A | 0;
|
|
708
|
+
this.B = B | 0;
|
|
709
|
+
this.C = C | 0;
|
|
710
|
+
this.D = D | 0;
|
|
711
|
+
this.E = E | 0;
|
|
712
|
+
this.F = F | 0;
|
|
713
|
+
this.G = G | 0;
|
|
714
|
+
this.H = H | 0;
|
|
715
|
+
}
|
|
716
|
+
_cloneInto(to) {
|
|
717
|
+
(to ||= new this.constructor()).set(...this.get());
|
|
718
|
+
return this._cloneIntoMeta(to);
|
|
719
|
+
}
|
|
720
|
+
process(view, offset) {
|
|
721
|
+
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
|
|
722
|
+
for (let i = 16; i < 64; i++) {
|
|
723
|
+
const W15 = SHA256_W[i - 15];
|
|
724
|
+
const W2 = SHA256_W[i - 2];
|
|
725
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
726
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
727
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
728
|
+
}
|
|
729
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
730
|
+
for (let i = 0; i < 64; i++) {
|
|
731
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
732
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
733
|
+
const T2 = (rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22)) + Maj(A, B, C) | 0;
|
|
734
|
+
H = G;
|
|
735
|
+
G = F;
|
|
736
|
+
F = E;
|
|
737
|
+
E = D + T1 | 0;
|
|
738
|
+
D = C;
|
|
739
|
+
C = B;
|
|
740
|
+
B = A;
|
|
741
|
+
A = T1 + T2 | 0;
|
|
742
|
+
}
|
|
743
|
+
A = A + this.A | 0;
|
|
744
|
+
B = B + this.B | 0;
|
|
745
|
+
C = C + this.C | 0;
|
|
746
|
+
D = D + this.D | 0;
|
|
747
|
+
E = E + this.E | 0;
|
|
748
|
+
F = F + this.F | 0;
|
|
749
|
+
G = G + this.G | 0;
|
|
750
|
+
H = H + this.H | 0;
|
|
751
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
752
|
+
}
|
|
753
|
+
roundClean() {
|
|
754
|
+
clean(SHA256_W);
|
|
755
|
+
}
|
|
756
|
+
destroy() {
|
|
757
|
+
this.destroyed = true;
|
|
758
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
759
|
+
clean(this.buffer);
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */
|
|
763
|
+
var _SHA256 = class extends SHA2_32B {
|
|
764
|
+
constructor() {
|
|
765
|
+
super(32, SHA256_IV);
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
/**
|
|
769
|
+
* SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:
|
|
770
|
+
*
|
|
771
|
+
* - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.
|
|
772
|
+
* - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
773
|
+
* - Each sha256 hash is executing 2^18 bit operations.
|
|
774
|
+
* - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.
|
|
775
|
+
* @param msg - message bytes to hash
|
|
776
|
+
* @param opts - Reserved hash options.
|
|
777
|
+
* @returns Digest bytes.
|
|
778
|
+
* @example
|
|
779
|
+
* Hash a message with SHA2-256.
|
|
780
|
+
* ```ts
|
|
781
|
+
* sha256(new Uint8Array([97, 98, 99]));
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(), /* @__PURE__ */ oidNist(1));
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region ../wallet-kernel/src/vault/memory.ts
|
|
787
|
+
function cloneRecord(record) {
|
|
788
|
+
return {
|
|
789
|
+
...record,
|
|
790
|
+
sealed: Uint8Array.from(record.sealed)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function cloneFactor(factor) {
|
|
794
|
+
return {
|
|
795
|
+
...factor,
|
|
796
|
+
sealed: Uint8Array.from(factor.sealed)
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function cloneMeta(meta) {
|
|
800
|
+
return { ...meta };
|
|
801
|
+
}
|
|
802
|
+
function cloneImage(image) {
|
|
803
|
+
return {
|
|
804
|
+
meta: image.meta === void 0 ? void 0 : cloneMeta(image.meta),
|
|
805
|
+
records: image.records.map(cloneRecord),
|
|
806
|
+
factors: image.factors.map(cloneFactor)
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* In-memory RecordStore for the Node host and kernel tests. Loads and commits deep-copy every
|
|
811
|
+
* Uint8Array, so callers can never mutate stored state. `commit` applies ops to a clone and swaps
|
|
812
|
+
* atomically: a failing `expect-uninitialized` leaves the store untouched.
|
|
813
|
+
*/
|
|
814
|
+
function createMemoryStore() {
|
|
815
|
+
let image = {
|
|
816
|
+
meta: void 0,
|
|
817
|
+
records: [],
|
|
818
|
+
factors: []
|
|
819
|
+
};
|
|
820
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
821
|
+
function apply(ops) {
|
|
822
|
+
let { meta } = image;
|
|
823
|
+
let { records } = image;
|
|
824
|
+
let { factors } = image;
|
|
825
|
+
for (const op of ops) switch (op.op) {
|
|
826
|
+
case "expect-uninitialized":
|
|
827
|
+
if (meta !== void 0 || records.length > 0 || factors.length > 0) throw new KernelError("integrity", "vault already initialized");
|
|
828
|
+
break;
|
|
829
|
+
case "put-meta":
|
|
830
|
+
meta = cloneMeta(op.meta);
|
|
831
|
+
break;
|
|
832
|
+
case "put-record":
|
|
833
|
+
records = [...records.filter((record) => !(record.kind === op.record.kind && record.id === op.record.id)), cloneRecord(op.record)];
|
|
834
|
+
break;
|
|
835
|
+
case "delete-record":
|
|
836
|
+
records = records.filter((record) => !(record.kind === op.kind && record.id === op.id));
|
|
837
|
+
break;
|
|
838
|
+
case "put-factor":
|
|
839
|
+
factors = [...factors.filter((factor) => factor.id !== op.factor.id), cloneFactor(op.factor)];
|
|
840
|
+
break;
|
|
841
|
+
case "delete-factor":
|
|
842
|
+
factors = factors.filter((factor) => factor.id !== op.id);
|
|
843
|
+
break;
|
|
844
|
+
case "clear":
|
|
845
|
+
meta = void 0;
|
|
846
|
+
records = [];
|
|
847
|
+
factors = [];
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
meta,
|
|
851
|
+
records,
|
|
852
|
+
factors
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
async load() {
|
|
857
|
+
return cloneImage(image);
|
|
858
|
+
},
|
|
859
|
+
async commit(ops) {
|
|
860
|
+
image = apply(ops);
|
|
861
|
+
for (const listener of listeners) listener();
|
|
862
|
+
},
|
|
863
|
+
watch(listener) {
|
|
864
|
+
listeners.add(listener);
|
|
865
|
+
return () => {
|
|
866
|
+
listeners.delete(listener);
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
//#endregion
|
|
872
|
+
//#region src/shared.ts
|
|
873
|
+
function encodeImage(image) {
|
|
874
|
+
return {
|
|
875
|
+
meta: image.meta,
|
|
876
|
+
factors: image.factors,
|
|
877
|
+
records: image.records
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
//#endregion
|
|
881
|
+
//#region src/node/store-server.ts
|
|
882
|
+
function isStoreRequest(raw) {
|
|
883
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return false;
|
|
884
|
+
const request = raw;
|
|
885
|
+
if (typeof request["id"] !== "number") return false;
|
|
886
|
+
return request["t"] === "load" || request["t"] === "commit" && Array.isArray(request["ops"]);
|
|
887
|
+
}
|
|
888
|
+
function serveStore(store, port) {
|
|
889
|
+
let ownCommits = 0;
|
|
890
|
+
const post = (reply) => {
|
|
891
|
+
port.postMessage(reply);
|
|
892
|
+
};
|
|
893
|
+
const answer = async (request) => {
|
|
894
|
+
if (request.t === "load") {
|
|
895
|
+
post({
|
|
896
|
+
t: "loaded",
|
|
897
|
+
id: request.id,
|
|
898
|
+
image: encodeImage(await store.load())
|
|
899
|
+
});
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
ownCommits++;
|
|
903
|
+
try {
|
|
904
|
+
await store.commit(request.ops);
|
|
905
|
+
} catch (error) {
|
|
906
|
+
ownCommits--;
|
|
907
|
+
throw error;
|
|
908
|
+
}
|
|
909
|
+
post({
|
|
910
|
+
t: "committed",
|
|
911
|
+
id: request.id
|
|
912
|
+
});
|
|
913
|
+
};
|
|
914
|
+
const onMessage = (raw) => {
|
|
915
|
+
if (!isStoreRequest(raw)) return;
|
|
916
|
+
(async () => {
|
|
917
|
+
try {
|
|
918
|
+
await answer(raw);
|
|
919
|
+
} catch {}
|
|
920
|
+
})();
|
|
921
|
+
};
|
|
922
|
+
const unwatch = store.watch(() => {
|
|
923
|
+
if (ownCommits > 0) {
|
|
924
|
+
ownCommits--;
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
post({ t: "changed" });
|
|
928
|
+
});
|
|
929
|
+
port.on("message", onMessage);
|
|
930
|
+
port.start();
|
|
931
|
+
return () => {
|
|
932
|
+
unwatch();
|
|
933
|
+
port.off("message", onMessage);
|
|
934
|
+
port.close();
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/node/software-passkey.ts
|
|
939
|
+
/**
|
|
940
|
+
* CreateSoftwarePasskey — a TEST-ONLY PasskeyCeremony. There is no authenticator, no user presence
|
|
941
|
+
* check, no credential store: the "passkey" is a deterministic function of `seed`, so anyone
|
|
942
|
+
* holding the seed owns every wallet it ever protects. Use it in tests and demos only.
|
|
943
|
+
*
|
|
944
|
+
* CredentialId = SHA-256(seed ‖ "credential" ‖ u32be(n)) (n increments per create) PRF(salt) =
|
|
945
|
+
* HMAC-SHA256(HMAC-SHA256(seed, credentialId), SHA-256(UTF-8("WebAuthn PRF") ‖ 0x00 ‖ salt))
|
|
946
|
+
*/
|
|
947
|
+
function createSoftwarePasskey(seed) {
|
|
948
|
+
let counter = 0;
|
|
949
|
+
let lastCreated;
|
|
950
|
+
const known = /* @__PURE__ */ new Map();
|
|
951
|
+
const prf = (credentialId, salt) => {
|
|
952
|
+
const key = hmac(sha256, seed, credentialId);
|
|
953
|
+
const message = sha256(concatBytes(utf8ToBytes("WebAuthn PRF"), new Uint8Array([0]), salt));
|
|
954
|
+
return hmac(sha256, key, message);
|
|
955
|
+
};
|
|
956
|
+
const result = (credentialId, salts) => ({
|
|
957
|
+
credentialId: new Uint8Array(credentialId),
|
|
958
|
+
first: prf(credentialId, salts.first),
|
|
959
|
+
...salts.second === void 0 ? {} : { second: prf(credentialId, salts.second) }
|
|
960
|
+
});
|
|
961
|
+
return {
|
|
962
|
+
create: async ({ salts }) => {
|
|
963
|
+
const counterBytes = /* @__PURE__ */ new Uint8Array(4);
|
|
964
|
+
new DataView(counterBytes.buffer).setUint32(0, counter);
|
|
965
|
+
counter++;
|
|
966
|
+
const credentialId = sha256(concatBytes(seed, utf8ToBytes("credential"), counterBytes));
|
|
967
|
+
known.set(bytesToHex(credentialId), credentialId);
|
|
968
|
+
lastCreated = credentialId;
|
|
969
|
+
return result(credentialId, salts);
|
|
970
|
+
},
|
|
971
|
+
assert: async ({ credentialIds, salts }) => {
|
|
972
|
+
const credentialId = credentialIds === void 0 ? lastCreated : credentialIds.find((id) => known.has(bytesToHex(id)));
|
|
973
|
+
if (credentialId === void 0) throw new WalletError("factor-unavailable", credentialIds === void 0 ? "no software credential exists yet" : "unknown software credential");
|
|
974
|
+
return result(credentialId, salts);
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region src/node/index.ts
|
|
980
|
+
/**
|
|
981
|
+
* The Node host: `createNodeWallet` spawns the bundled kernel worker (dist/kernel.node.js) and
|
|
982
|
+
* keeps the vault image — ciphertext + public headers, never secrets — in the main thread's
|
|
983
|
+
* in-memory store, served to each worker over a MessageChannel. That is what lets a lock destroy
|
|
984
|
+
* the whole worker without losing the vault.
|
|
985
|
+
*/
|
|
986
|
+
const MAX_SESSION_MS = 432e5;
|
|
987
|
+
async function createNodeWallet(options) {
|
|
988
|
+
const { autoLock } = options;
|
|
989
|
+
if (autoLock.platform !== "web") throw new WalletError("invalid-input", "createNodeWallet accepts only the web autoLock policy");
|
|
990
|
+
const workerData = {
|
|
991
|
+
idleMs: autoLock.idleMinutes * 6e4,
|
|
992
|
+
maxSessionMs: MAX_SESSION_MS
|
|
993
|
+
};
|
|
994
|
+
const store = createMemoryStore();
|
|
995
|
+
const kernelUrl = options.kernelUrl ?? new URL("kernel.node.js", import.meta.url);
|
|
996
|
+
const spawn = () => {
|
|
997
|
+
const channel = new MessageChannel();
|
|
998
|
+
const stopStore = serveStore(store, channel.port1);
|
|
999
|
+
const worker = new Worker(kernelUrl, {
|
|
1000
|
+
workerData: {
|
|
1001
|
+
...workerData,
|
|
1002
|
+
storePort: channel.port2
|
|
1003
|
+
},
|
|
1004
|
+
transferList: [channel.port2]
|
|
1005
|
+
});
|
|
1006
|
+
options.onSpawn?.(worker);
|
|
1007
|
+
return {
|
|
1008
|
+
post: (message, transfer) => {
|
|
1009
|
+
worker.postMessage(message, transfer);
|
|
1010
|
+
},
|
|
1011
|
+
listen: (handler) => {
|
|
1012
|
+
const listener = (message) => {
|
|
1013
|
+
handler(message);
|
|
1014
|
+
};
|
|
1015
|
+
worker.on("message", listener);
|
|
1016
|
+
return () => {
|
|
1017
|
+
worker.off("message", listener);
|
|
1018
|
+
};
|
|
1019
|
+
},
|
|
1020
|
+
onExit: (handler) => {
|
|
1021
|
+
const listener = () => {
|
|
1022
|
+
handler();
|
|
1023
|
+
};
|
|
1024
|
+
worker.once("exit", listener);
|
|
1025
|
+
return () => {
|
|
1026
|
+
worker.off("exit", listener);
|
|
1027
|
+
};
|
|
1028
|
+
},
|
|
1029
|
+
terminate: () => {
|
|
1030
|
+
stopStore();
|
|
1031
|
+
worker.terminate();
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
};
|
|
1035
|
+
return connectKernel({
|
|
1036
|
+
spawn,
|
|
1037
|
+
passkey: options.passkey,
|
|
1038
|
+
ceremony: options.authenticator
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
//#endregion
|
|
1042
|
+
export { createNodeWallet, createSoftwarePasskey };
|
|
1043
|
+
|
|
1044
|
+
//# sourceMappingURL=node.mjs.map
|