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