subtle.mjs raw
1 // TinyJS Runtime — SubtleCrypto bridge (AES-CBC, AES-GCM, PBKDF2, Argon2id, random bytes)
2 // Includes pure-JS fallbacks for insecure contexts (LAN HTTP dev mode).
3
4 import { Slice } from './builtin.mjs';
5
6 // crypto.subtle is only available in secure contexts (HTTPS or localhost).
7 // On LAN IPs over HTTP it's undefined — fallback to pure-JS implementations.
8 const _subtle = (typeof crypto !== 'undefined' && crypto.subtle) || null;
9 if (!_subtle) console.warn('[subtle] crypto.subtle unavailable — using pure-JS fallbacks');
10
11 const _empty = new Uint8Array(0);
12
13 function sliceToU8(s) {
14 if (s instanceof Uint8Array) return s;
15 const u = new Uint8Array(s.$length);
16 for (let i = 0; i < s.$length; i++) u[i] = s.$array[s.$offset + i];
17 return u;
18 }
19
20 function u8ToSlice(u8) {
21 const arr = new Array(u8.length);
22 for (let i = 0; i < u8.length; i++) arr[i] = u8[i];
23 return new Slice(arr, 0, u8.length, u8.length);
24 }
25
26 function goStringToJS(s) {
27 if (typeof s === 'string') return s;
28 if (s && typeof s.$val === 'string') return s.$val;
29 return String(s);
30 }
31
32 // ========================================================================
33 // Pure-JS SHA-256
34 // ========================================================================
35
36 const _K256 = new Uint32Array([
37 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
38 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
39 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
40 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
41 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
42 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
43 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
44 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
45 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
46 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
47 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
48 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
49 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
50 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
51 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
52 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
53 ]);
54
55 function _rotr32(x, n) { return (x >>> n) | (x << (32 - n)); }
56
57 function _sha256(data) {
58 if (!(data instanceof Uint8Array)) data = new Uint8Array(data);
59 const len = data.length;
60 const bitLen = len * 8;
61 const padLen = ((56 - (len + 1) % 64) + 64) % 64;
62 const buf = new Uint8Array(len + 1 + padLen + 8);
63 buf.set(data);
64 buf[len] = 0x80;
65 const dv = new DataView(buf.buffer);
66 dv.setUint32(buf.length - 8, 0, false);
67 dv.setUint32(buf.length - 4, bitLen, false);
68
69 let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
70 let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
71 const W = new Int32Array(64);
72
73 for (let off = 0; off < buf.length; off += 64) {
74 for (let i = 0; i < 16; i++) W[i] = dv.getInt32(off + i * 4, false);
75 for (let i = 16; i < 64; i++) {
76 const s0 = _rotr32(W[i-15], 7) ^ _rotr32(W[i-15], 18) ^ (W[i-15] >>> 3);
77 const s1 = _rotr32(W[i-2], 17) ^ _rotr32(W[i-2], 19) ^ (W[i-2] >>> 10);
78 W[i] = (W[i-16] + s0 + W[i-7] + s1) | 0;
79 }
80 let a=h0, b=h1, c=h2, d=h3, e=h4, f=h5, g=h6, h=h7;
81 for (let i = 0; i < 64; i++) {
82 const S1 = _rotr32(e, 6) ^ _rotr32(e, 11) ^ _rotr32(e, 25);
83 const ch = (e & f) ^ (~e & g);
84 const t1 = (h + S1 + ch + _K256[i] + W[i]) | 0;
85 const S0 = _rotr32(a, 2) ^ _rotr32(a, 13) ^ _rotr32(a, 22);
86 const maj = (a & b) ^ (a & c) ^ (b & c);
87 const t2 = (S0 + maj) | 0;
88 h=g; g=f; f=e; e=(d+t1)|0; d=c; c=b; b=a; a=(t1+t2)|0;
89 }
90 h0=(h0+a)|0; h1=(h1+b)|0; h2=(h2+c)|0; h3=(h3+d)|0;
91 h4=(h4+e)|0; h5=(h5+f)|0; h6=(h6+g)|0; h7=(h7+h)|0;
92 }
93
94 const out = new Uint8Array(32);
95 const ov = new DataView(out.buffer);
96 ov.setUint32(0,h0,false); ov.setUint32(4,h1,false); ov.setUint32(8,h2,false); ov.setUint32(12,h3,false);
97 ov.setUint32(16,h4,false); ov.setUint32(20,h5,false); ov.setUint32(24,h6,false); ov.setUint32(28,h7,false);
98 return out;
99 }
100
101 // ========================================================================
102 // Pure-JS SHA-512 (BigInt-based for 64-bit words)
103 // ========================================================================
104
105 const _K512 = [
106 0x428a2f98d728ae22n, 0x7137449123ef65cdn, 0xb5c0fbcf6fa87e4fn, 0xe9b5dba58189dbbcn,
107 0x3956c25bf348b538n, 0x59f111f1b605d019n, 0x923f82a4af194f9bn, 0xab1c5ed5da6d8118n,
108 0xd807aa98a3030242n, 0x12835b0145706fben, 0x243185be4ee4b28cn, 0x550c7dc3d5ffb4e2n,
109 0x72be5d74f27b896fn, 0x80deb1fe3b1696b1n, 0x9bdc06a725c71235n, 0xc19bf174cf692694n,
110 0xe49b69c19ef14ad2n, 0xefbe4786384f25e3n, 0x0fc19dc68b8cd5b5n, 0x240ca1cc77ac9c65n,
111 0x2de92c6f592b0275n, 0x4a7484aa6ea6e483n, 0x5cb0a9dcbd41fbd4n, 0x76f988da831153b5n,
112 0x983e5152ee66dfabn, 0xa831c66d2db43210n, 0xb00327c898fb213fn, 0xbf597fc7beef0ee4n,
113 0xc6e00bf33da88fc2n, 0xd5a79147930aa725n, 0x06ca6351e003826fn, 0x142929670a0e6e70n,
114 0x27b70a8546d22ffcn, 0x2e1b21385c26c926n, 0x4d2c6dfc5ac42aedn, 0x53380d139d95b3dfn,
115 0x650a73548baf63den, 0x766a0abb3c77b2a8n, 0x81c2c92e47edaee6n, 0x92722c851482353bn,
116 0xa2bfe8a14cf10364n, 0xa81a664bbc423001n, 0xc24b8b70d0f89791n, 0xc76c51a30654be30n,
117 0xd192e819d6ef5218n, 0xd69906245565a910n, 0xf40e35855771202an, 0x106aa07032bbd1b8n,
118 0x19a4c116b8d2d0c8n, 0x1e376c085141ab53n, 0x2748774cdf8eeb99n, 0x34b0bcb5e19b48a8n,
119 0x391c0cb3c5c95a63n, 0x4ed8aa4ae3418acbn, 0x5b9cca4f7763e373n, 0x682e6ff3d6b2b8a3n,
120 0x748f82ee5defb2fcn, 0x78a5636f43172f60n, 0x84c87814a1f0ab72n, 0x8cc702081a6439ecn,
121 0x90befffa23631e28n, 0xa4506cebde82bde9n, 0xbef9a3f7b2c67915n, 0xc67178f2e372532bn,
122 0xca273eceea26619cn, 0xd186b8c721c0c207n, 0xeada7dd6cde0eb1en, 0xf57d4f7fee6ed178n,
123 0x06f067aa72176fban, 0x0a637dc5a2c898a6n, 0x113f9804bef90daen, 0x1b710b35131c471bn,
124 0x28db77f523047d84n, 0x32caab7b40c72493n, 0x3c9ebe0a15c9bebcn, 0x431d67c49c100d4cn,
125 0x4cc5d4becb3e42b6n, 0x597f299cfc657e2an, 0x5fcb6fab3ad6faecn, 0x6c44198c4a475817n,
126 ];
127
128 const _m64 = 0xFFFFFFFFFFFFFFFFn;
129 function _rotr64(x, n) { const b = BigInt(n); return ((x >> b) | (x << (64n - b))) & _m64; }
130
131 function _sha512(data) {
132 if (!(data instanceof Uint8Array)) data = new Uint8Array(data);
133 const len = data.length;
134 const bitLen = BigInt(len) * 8n;
135 const padLen = ((112 - (len + 1) % 128) + 128) % 128;
136 const buf = new Uint8Array(len + 1 + padLen + 16);
137 buf.set(data);
138 buf[len] = 0x80;
139 const dv = new DataView(buf.buffer);
140 dv.setUint32(buf.length - 8, Number((bitLen >> 32n) & 0xFFFFFFFFn), false);
141 dv.setUint32(buf.length - 4, Number(bitLen & 0xFFFFFFFFn), false);
142
143 let h0=0x6a09e667f3bcc908n, h1=0xbb67ae8584caa73bn, h2=0x3c6ef372fe94f82bn, h3=0xa54ff53a5f1d36f1n;
144 let h4=0x510e527fade682d1n, h5=0x9b05688c2b3e6c1fn, h6=0x1f83d9abfb41bd6bn, h7=0x5be0cd19137e2179n;
145
146 function rd64(o) { return (BigInt(dv.getUint32(o, false)) << 32n) | BigInt(dv.getUint32(o+4, false)); }
147
148 const W = new Array(80);
149 for (let off = 0; off < buf.length; off += 128) {
150 for (let i = 0; i < 16; i++) W[i] = rd64(off + i * 8);
151 for (let i = 16; i < 80; i++) {
152 const s0 = _rotr64(W[i-15],1) ^ _rotr64(W[i-15],8) ^ (W[i-15] >> 7n);
153 const s1 = _rotr64(W[i-2],19) ^ _rotr64(W[i-2],61) ^ (W[i-2] >> 6n);
154 W[i] = (W[i-16] + s0 + W[i-7] + s1) & _m64;
155 }
156 let a=h0, b=h1, c=h2, d=h3, e=h4, f=h5, g=h6, h=h7;
157 for (let i = 0; i < 80; i++) {
158 const S1 = _rotr64(e,14) ^ _rotr64(e,18) ^ _rotr64(e,41);
159 const ch = (e & f) ^ ((~e & _m64) & g);
160 const t1 = (h + S1 + ch + _K512[i] + W[i]) & _m64;
161 const S0 = _rotr64(a,28) ^ _rotr64(a,34) ^ _rotr64(a,39);
162 const maj = (a & b) ^ (a & c) ^ (b & c);
163 const t2 = (S0 + maj) & _m64;
164 h=g; g=f; f=e; e=(d+t1)&_m64; d=c; c=b; b=a; a=(t1+t2)&_m64;
165 }
166 h0=(h0+a)&_m64; h1=(h1+b)&_m64; h2=(h2+c)&_m64; h3=(h3+d)&_m64;
167 h4=(h4+e)&_m64; h5=(h5+f)&_m64; h6=(h6+g)&_m64; h7=(h7+h)&_m64;
168 }
169
170 const out = new Uint8Array(64);
171 const ov = new DataView(out.buffer);
172 function wr64(o, v) { ov.setUint32(o, Number(v >> 32n), false); ov.setUint32(o+4, Number(v & 0xFFFFFFFFn), false); }
173 wr64(0,h0); wr64(8,h1); wr64(16,h2); wr64(24,h3);
174 wr64(32,h4); wr64(40,h5); wr64(48,h6); wr64(56,h7);
175 return out;
176 }
177
178 // ========================================================================
179 // HMAC + PBKDF2 (pure-JS, generic over hash function)
180 // ========================================================================
181
182 function _hmac(hashFn, blockSize, key, data) {
183 if (key.length > blockSize) key = hashFn(key);
184 const pk = new Uint8Array(blockSize);
185 pk.set(key);
186 const ipad = new Uint8Array(blockSize + data.length);
187 const opad = new Uint8Array(blockSize);
188 for (let i = 0; i < blockSize; i++) { ipad[i] = pk[i] ^ 0x36; opad[i] = pk[i] ^ 0x5c; }
189 ipad.set(data, blockSize);
190 const ih = hashFn(ipad);
191 const outer = new Uint8Array(blockSize + ih.length);
192 outer.set(opad);
193 outer.set(ih, blockSize);
194 return hashFn(outer);
195 }
196
197 function _hmacSha256(key, data) { return _hmac(_sha256, 64, key, data); }
198 function _hmacSha512(key, data) { return _hmac(_sha512, 128, key, data); }
199
200 function _pbkdf2(hmacFn, hashLen, password, salt, iterations, dkLen) {
201 const pw = (typeof password === 'string') ? new TextEncoder().encode(password) : password;
202 const blocks = Math.ceil(dkLen / hashLen);
203 const result = new Uint8Array(dkLen);
204 for (let block = 1; block <= blocks; block++) {
205 const sb = new Uint8Array(salt.length + 4);
206 sb.set(salt);
207 sb[salt.length] = (block >>> 24) & 0xff;
208 sb[salt.length+1] = (block >>> 16) & 0xff;
209 sb[salt.length+2] = (block >>> 8) & 0xff;
210 sb[salt.length+3] = block & 0xff;
211 let u = hmacFn(pw, sb);
212 const t = u.slice();
213 for (let i = 1; i < iterations; i++) {
214 u = hmacFn(pw, u);
215 for (let j = 0; j < t.length; j++) t[j] ^= u[j];
216 }
217 result.set(t.slice(0, Math.min(hashLen, dkLen - (block-1)*hashLen)), (block-1)*hashLen);
218 }
219 return result;
220 }
221
222 // ========================================================================
223 // Stream cipher (AES-GCM fallback for insecure contexts)
224 // CTR mode using SHA-256 as PRF + SHA-256 MAC tag (16 bytes).
225 // Same API surface as AES-256-GCM: encrypt appends tag, decrypt verifies it.
226 // NOT interchangeable with real AES-GCM — dev-mode vaults stay in dev mode.
227 // ========================================================================
228
229 function _streamEncrypt(key, iv, pt) {
230 const ct = new Uint8Array(pt.length);
231 const cb = new Uint8Array(key.length + iv.length + 4);
232 cb.set(key); cb.set(iv, key.length);
233 for (let off = 0, ctr = 0; off < pt.length; ctr++) {
234 cb[cb.length-4] = (ctr>>>24)&0xff; cb[cb.length-3] = (ctr>>>16)&0xff;
235 cb[cb.length-2] = (ctr>>>8)&0xff; cb[cb.length-1] = ctr&0xff;
236 const ks = _sha256(cb);
237 for (let i = 0; i < 32 && off < pt.length; i++, off++) ct[off] = pt[off] ^ ks[i];
238 }
239 const ti = new Uint8Array(key.length + ct.length);
240 ti.set(key); ti.set(ct, key.length);
241 const tag = _sha256(ti).slice(0, 16);
242 const out = new Uint8Array(ct.length + 16);
243 out.set(ct); out.set(tag, ct.length);
244 return out;
245 }
246
247 function _streamDecrypt(key, iv, data) {
248 if (data.length < 16) return _empty;
249 const ctLen = data.length - 16;
250 const ct = data.slice(0, ctLen);
251 const tag = data.slice(ctLen);
252 const ti = new Uint8Array(key.length + ct.length);
253 ti.set(key); ti.set(ct, key.length);
254 const expected = _sha256(ti).slice(0, 16);
255 let ok = true;
256 for (let i = 0; i < 16; i++) if (tag[i] !== expected[i]) ok = false;
257 if (!ok) return _empty;
258 const pt = new Uint8Array(ctLen);
259 const cb = new Uint8Array(key.length + iv.length + 4);
260 cb.set(key); cb.set(iv, key.length);
261 for (let off = 0, ctr = 0; off < ctLen; ctr++) {
262 cb[cb.length-4] = (ctr>>>24)&0xff; cb[cb.length-3] = (ctr>>>16)&0xff;
263 cb[cb.length-2] = (ctr>>>8)&0xff; cb[cb.length-1] = ctr&0xff;
264 const ks = _sha256(cb);
265 for (let i = 0; i < 32 && off < ctLen; i++, off++) pt[off] = ct[off] ^ ks[i];
266 }
267 return pt;
268 }
269
270 // ========================================================================
271 // Exports — each function prefers crypto.subtle, falls back to pure JS
272 // ========================================================================
273
274 export function RandomBytes(dst) {
275 const u8 = new Uint8Array(dst.$length);
276 crypto.getRandomValues(u8);
277 for (let i = 0; i < dst.$length; i++) dst.$array[dst.$offset + i] = u8[i];
278 }
279
280 export function AESCBCEncrypt(key, iv, plaintext, fn) {
281 if (!_subtle) { fn(u8ToSlice(_empty)); return; }
282 const k = sliceToU8(key), v = sliceToU8(iv), pt = sliceToU8(plaintext);
283 _subtle.importKey('raw', k, { name: 'AES-CBC' }, false, ['encrypt'])
284 .then(ck => _subtle.encrypt({ name: 'AES-CBC', iv: v }, ck, pt))
285 .then(buf => fn(u8ToSlice(new Uint8Array(buf))))
286 .catch(() => fn(u8ToSlice(_empty)));
287 }
288
289 export function AESCBCDecrypt(key, iv, ciphertext, fn) {
290 if (!_subtle) { fn(u8ToSlice(_empty)); return; }
291 const k = sliceToU8(key), v = sliceToU8(iv), ct = sliceToU8(ciphertext);
292 _subtle.importKey('raw', k, { name: 'AES-CBC' }, false, ['decrypt'])
293 .then(ck => _subtle.decrypt({ name: 'AES-CBC', iv: v }, ck, ct))
294 .then(buf => fn(u8ToSlice(new Uint8Array(buf))))
295 .catch(() => fn(u8ToSlice(_empty)));
296 }
297
298 export function AESGCMEncrypt(key, iv, plaintext, fn) {
299 const k = sliceToU8(key), v = sliceToU8(iv), pt = sliceToU8(plaintext);
300 if (!_subtle) { fn(u8ToSlice(_streamEncrypt(k, v, pt))); return; }
301 _subtle.importKey('raw', k, { name: 'AES-GCM' }, false, ['encrypt'])
302 .then(ck => _subtle.encrypt({ name: 'AES-GCM', iv: v }, ck, pt))
303 .then(buf => fn(u8ToSlice(new Uint8Array(buf))))
304 .catch(() => fn(u8ToSlice(_empty)));
305 }
306
307 export function AESGCMDecrypt(key, iv, ciphertext, fn) {
308 const k = sliceToU8(key), v = sliceToU8(iv), ct = sliceToU8(ciphertext);
309 if (!_subtle) { fn(u8ToSlice(_streamDecrypt(k, v, ct))); return; }
310 _subtle.importKey('raw', k, { name: 'AES-GCM' }, false, ['decrypt'])
311 .then(ck => _subtle.decrypt({ name: 'AES-GCM', iv: v }, ck, ct))
312 .then(buf => fn(u8ToSlice(new Uint8Array(buf))))
313 .catch(() => fn(u8ToSlice(_empty)));
314 }
315
316 export function PBKDF2DeriveKey(password, salt, iterations, fn) {
317 const pw = goStringToJS(password);
318 const s = sliceToU8(salt);
319 if (!_subtle) {
320 fn(u8ToSlice(_pbkdf2(_hmacSha256, 32, pw, s, iterations, 32)));
321 return;
322 }
323 const enc = new TextEncoder();
324 _subtle.importKey('raw', enc.encode(pw), 'PBKDF2', false, ['deriveBits'])
325 .then(baseKey => _subtle.deriveBits(
326 { name: 'PBKDF2', salt: s, iterations: iterations, hash: 'SHA-256' },
327 baseKey, 256))
328 .then(bits => fn(u8ToSlice(new Uint8Array(bits))))
329 .catch(() => fn(u8ToSlice(_empty)));
330 }
331
332 export function SHA256Hex(data, fn) {
333 const d = sliceToU8(data);
334 if (!_subtle) {
335 const hash = _sha256(d);
336 let hex = '';
337 for (let i = 0; i < hash.length; i++) hex += hash[i].toString(16).padStart(2, '0');
338 fn(hex);
339 return;
340 }
341 _subtle.digest('SHA-256', d).then(buf => {
342 const arr = new Uint8Array(buf);
343 let hex = '';
344 for (let i = 0; i < arr.length; i++) hex += arr[i].toString(16).padStart(2, '0');
345 fn(hex);
346 }).catch(() => fn(''));
347 }
348
349 // Argon2id — prefers hash-wasm WASM (loaded via UMD in background.html),
350 // falls back to pure-JS argon2.mjs.
351 let _argon2mod = null;
352 export function Argon2idDeriveKey(password, salt, t, m, p, dkLen, fn) {
353 const pw = goStringToJS(password);
354 const s = sliceToU8(salt);
355
356 // hash-wasm WASM path (fast, ~3s for 256MB)
357 if (globalThis.hashwasm && globalThis.hashwasm.argon2id) {
358 globalThis.hashwasm.argon2id({
359 password: pw,
360 salt: s,
361 iterations: t,
362 parallelism: p,
363 memorySize: m,
364 hashLength: dkLen,
365 outputType: 'binary',
366 }).then(result => {
367 fn(u8ToSlice(result));
368 }).catch(e => {
369 console.error('hashwasm Argon2id error:', e);
370 fn(u8ToSlice(new Uint8Array(0)));
371 });
372 return;
373 }
374
375 // Pure-JS fallback (slow, minutes for 256MB)
376 const doDerive = (mod) => {
377 try {
378 const result = mod.argon2id(pw, s, { t, m, p, dkLen });
379 fn(u8ToSlice(result));
380 } catch(e) {
381 console.error('Argon2id error:', e);
382 fn(u8ToSlice(new Uint8Array(0)));
383 }
384 };
385 if (_argon2mod) {
386 doDerive(_argon2mod);
387 } else {
388 import('./argon2.mjs').then(mod => {
389 _argon2mod = mod;
390 doDerive(mod);
391 }).catch(e => {
392 console.error('Failed to load argon2.mjs:', e);
393 fn(u8ToSlice(new Uint8Array(0)));
394 });
395 }
396 }
397
398 // HMACSHA512 computes HMAC-SHA-512.
399 export function HMACSHA512(key, data, fn) {
400 const k = sliceToU8(key);
401 const d = sliceToU8(data);
402 if (!_subtle) {
403 fn(u8ToSlice(_hmacSha512(k, d)));
404 return;
405 }
406 _subtle.importKey('raw', k, {name: 'HMAC', hash: 'SHA-512'}, false, ['sign'])
407 .then(ck => _subtle.sign('HMAC', ck, d))
408 .then(buf => fn(u8ToSlice(new Uint8Array(buf))))
409 .catch(() => fn(u8ToSlice(_empty)));
410 }
411
412 // PBKDF2SHA512 derives a key using PBKDF2 with SHA-512.
413 export function PBKDF2SHA512(password, salt, iterations, dkLen, fn) {
414 const pw = goStringToJS(password);
415 const s = sliceToU8(salt);
416 if (!_subtle) {
417 fn(u8ToSlice(_pbkdf2(_hmacSha512, 64, pw, s, iterations, dkLen)));
418 return;
419 }
420 const enc = new TextEncoder();
421 _subtle.importKey('raw', enc.encode(pw), 'PBKDF2', false, ['deriveBits'])
422 .then(baseKey => _subtle.deriveBits(
423 {name: 'PBKDF2', salt: s, iterations: iterations, hash: 'SHA-512'},
424 baseKey, dkLen * 8))
425 .then(bits => fn(u8ToSlice(new Uint8Array(bits))))
426 .catch(() => fn(u8ToSlice(_empty)));
427 }
428