argon2.mjs raw

   1  // Argon2id from RFC 9106 — self-contained ES module
   2  // Ported from @noble/hashes (MIT License, Paul Miller)
   3  // All dependencies (blake2b, u64 helpers, utils) inlined.
   4  
   5  import { Slice } from './builtin.mjs';
   6  
   7  // ============================================================
   8  // Go slice conversion helpers (same pattern as subtle.mjs)
   9  // ============================================================
  10  
  11  function sliceToU8(s) {
  12    if (s instanceof Uint8Array) return s;
  13    const u = new Uint8Array(s.$length);
  14    for (let i = 0; i < s.$length; i++) u[i] = s.$array[s.$offset + i];
  15    return u;
  16  }
  17  
  18  function u8ToSlice(u8arr) {
  19    const arr = new Array(u8arr.length);
  20    for (let i = 0; i < u8arr.length; i++) arr[i] = u8arr[i];
  21    return new Slice(arr, 0, u8arr.length, u8arr.length);
  22  }
  23  
  24  // ============================================================
  25  // utils.ts — only functions used by argon2 + blake2b
  26  // ============================================================
  27  
  28  function isBytes(a) {
  29    return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
  30  }
  31  
  32  function anumber(n) {
  33    if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n);
  34  }
  35  
  36  function abytes(b, ...lengths) {
  37    if (!isBytes(b)) throw new Error('Uint8Array expected');
  38    if (lengths.length > 0 && !lengths.includes(b.length))
  39      throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
  40  }
  41  
  42  function aexists(instance, checkFinished = true) {
  43    if (instance.destroyed) throw new Error('Hash instance has been destroyed');
  44    if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');
  45  }
  46  
  47  function aoutput(out, instance) {
  48    abytes(out);
  49    const min = instance.outputLen;
  50    if (out.length < min) {
  51      throw new Error('digestInto() expects output buffer of length at least ' + min);
  52    }
  53  }
  54  
  55  function u8(arr) {
  56    return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
  57  }
  58  
  59  function u32(arr) {
  60    return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
  61  }
  62  
  63  function clean(...arrays) {
  64    for (let i = 0; i < arrays.length; i++) {
  65      arrays[i].fill(0);
  66    }
  67  }
  68  
  69  const isLE = (() =>
  70    new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
  71  
  72  function byteSwap(word) {
  73    return (
  74      ((word << 24) & 0xff000000) |
  75      ((word << 8) & 0xff0000) |
  76      ((word >>> 8) & 0xff00) |
  77      ((word >>> 24) & 0xff)
  78    );
  79  }
  80  
  81  const swap8IfBE = isLE ? (n) => n : (n) => byteSwap(n);
  82  
  83  function byteSwap32(arr) {
  84    for (let i = 0; i < arr.length; i++) {
  85      arr[i] = byteSwap(arr[i]);
  86    }
  87    return arr;
  88  }
  89  
  90  const swap32IfBE = isLE ? (u) => u : byteSwap32;
  91  
  92  function utf8ToBytes(str) {
  93    if (typeof str !== 'string') throw new Error('string expected');
  94    return new Uint8Array(new TextEncoder().encode(str));
  95  }
  96  
  97  function toBytes(data) {
  98    if (typeof data === 'string') data = utf8ToBytes(data);
  99    abytes(data);
 100    return data;
 101  }
 102  
 103  function kdfInputToBytes(data) {
 104    if (typeof data === 'string') data = utf8ToBytes(data);
 105    abytes(data);
 106    return data;
 107  }
 108  
 109  // ============================================================
 110  // _u64.ts — 64-bit helpers
 111  // ============================================================
 112  
 113  const U32_MASK64 = BigInt(2 ** 32 - 1);
 114  const _32n = BigInt(32);
 115  
 116  function fromBig(n, le = false) {
 117    if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
 118    return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
 119  }
 120  
 121  const rotrSH = (h, l, s) => (h >>> s) | (l << (32 - s));
 122  const rotrSL = (h, l, s) => (h << (32 - s)) | (l >>> s);
 123  const rotrBH = (h, l, s) => (h << (64 - s)) | (l >>> (s - 32));
 124  const rotrBL = (h, l, s) => (h >>> (s - 32)) | (l << (64 - s));
 125  const rotr32H = (_h, l) => l;
 126  const rotr32L = (h, _l) => h;
 127  
 128  function u64add(Ah, Al, Bh, Bl) {
 129    const l = (Al >>> 0) + (Bl >>> 0);
 130    return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };
 131  }
 132  
 133  const add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
 134  const add3H = (low, Ah, Bh, Ch) => (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;
 135  
 136  // ============================================================
 137  // _blake.ts — BSIGMA constant
 138  // ============================================================
 139  
 140  const BSIGMA = Uint8Array.from([
 141    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
 142    14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
 143    11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
 144    7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
 145    9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
 146    2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
 147    12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
 148    13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
 149    6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
 150    10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
 151    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
 152    14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
 153  ]);
 154  
 155  // ============================================================
 156  // blake2.ts — BLAKE2b only
 157  // ============================================================
 158  
 159  const B2B_IV = Uint32Array.from([
 160    0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
 161    0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19,
 162  ]);
 163  
 164  const BBUF = new Uint32Array(32);
 165  
 166  function G1b(a, b, c, d, msg, x) {
 167    const Xl = msg[x], Xh = msg[x + 1];
 168    let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1];
 169    let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1];
 170    let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1];
 171    let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1];
 172    let ll = add3L(Al, Bl, Xl);
 173    Ah = add3H(ll, Ah, Bh, Xh);
 174    Al = ll | 0;
 175    ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
 176    ({ Dh, Dl } = { Dh: rotr32H(Dh, Dl), Dl: rotr32L(Dh, Dl) });
 177    ({ h: Ch, l: Cl } = u64add(Ch, Cl, Dh, Dl));
 178    ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
 179    ({ Bh, Bl } = { Bh: rotrSH(Bh, Bl, 24), Bl: rotrSL(Bh, Bl, 24) });
 180    (BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah);
 181    (BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh);
 182    (BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch);
 183    (BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh);
 184  }
 185  
 186  function G2b(a, b, c, d, msg, x) {
 187    const Xl = msg[x], Xh = msg[x + 1];
 188    let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1];
 189    let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1];
 190    let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1];
 191    let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1];
 192    let ll = add3L(Al, Bl, Xl);
 193    Ah = add3H(ll, Ah, Bh, Xh);
 194    Al = ll | 0;
 195    ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
 196    ({ Dh, Dl } = { Dh: rotrSH(Dh, Dl, 16), Dl: rotrSL(Dh, Dl, 16) });
 197    ({ h: Ch, l: Cl } = u64add(Ch, Cl, Dh, Dl));
 198    ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
 199    ({ Bh, Bl } = { Bh: rotrBH(Bh, Bl, 63), Bl: rotrBL(Bh, Bl, 63) });
 200    (BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah);
 201    (BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh);
 202    (BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch);
 203    (BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh);
 204  }
 205  
 206  function checkBlake2Opts(outputLen, opts = {}, keyLen, saltLen, persLen) {
 207    anumber(keyLen);
 208    if (outputLen < 0 || outputLen > keyLen) throw new Error('outputLen bigger than keyLen');
 209    const { key, salt, personalization } = opts;
 210    if (key !== undefined && (key.length < 1 || key.length > keyLen))
 211      throw new Error('key length must be undefined or 1..' + keyLen);
 212    if (salt !== undefined && salt.length !== saltLen)
 213      throw new Error('salt must be undefined or ' + saltLen);
 214    if (personalization !== undefined && personalization.length !== persLen)
 215      throw new Error('personalization must be undefined or ' + persLen);
 216  }
 217  
 218  class BLAKE2b {
 219    constructor(opts = {}) {
 220      const olen = opts.dkLen === undefined ? 64 : opts.dkLen;
 221      this.blockLen = 128;
 222      this.outputLen = olen;
 223      this.buffer = new Uint8Array(128);
 224      this.buffer32 = u32(this.buffer);
 225      this.finished = false;
 226      this.destroyed = false;
 227      this.length = 0;
 228      this.pos = 0;
 229  
 230      this.v0l = B2B_IV[0] | 0;
 231      this.v0h = B2B_IV[1] | 0;
 232      this.v1l = B2B_IV[2] | 0;
 233      this.v1h = B2B_IV[3] | 0;
 234      this.v2l = B2B_IV[4] | 0;
 235      this.v2h = B2B_IV[5] | 0;
 236      this.v3l = B2B_IV[6] | 0;
 237      this.v3h = B2B_IV[7] | 0;
 238      this.v4l = B2B_IV[8] | 0;
 239      this.v4h = B2B_IV[9] | 0;
 240      this.v5l = B2B_IV[10] | 0;
 241      this.v5h = B2B_IV[11] | 0;
 242      this.v6l = B2B_IV[12] | 0;
 243      this.v6h = B2B_IV[13] | 0;
 244      this.v7l = B2B_IV[14] | 0;
 245      this.v7h = B2B_IV[15] | 0;
 246  
 247      checkBlake2Opts(olen, opts, 64, 16, 16);
 248      let { key, personalization, salt } = opts;
 249      let keyLength = 0;
 250      if (key !== undefined) {
 251        key = toBytes(key);
 252        keyLength = key.length;
 253      }
 254      this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
 255      if (salt !== undefined) {
 256        salt = toBytes(salt);
 257        const slt = u32(salt);
 258        this.v4l ^= swap8IfBE(slt[0]);
 259        this.v4h ^= swap8IfBE(slt[1]);
 260        this.v5l ^= swap8IfBE(slt[2]);
 261        this.v5h ^= swap8IfBE(slt[3]);
 262      }
 263      if (personalization !== undefined) {
 264        personalization = toBytes(personalization);
 265        const pers = u32(personalization);
 266        this.v6l ^= swap8IfBE(pers[0]);
 267        this.v6h ^= swap8IfBE(pers[1]);
 268        this.v7l ^= swap8IfBE(pers[2]);
 269        this.v7h ^= swap8IfBE(pers[3]);
 270      }
 271      if (key !== undefined) {
 272        const tmp = new Uint8Array(this.blockLen);
 273        tmp.set(key);
 274        this.update(tmp);
 275      }
 276    }
 277  
 278    _get() {
 279      const { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this;
 280      return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];
 281    }
 282  
 283    _set(v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h) {
 284      this.v0l = v0l | 0;
 285      this.v0h = v0h | 0;
 286      this.v1l = v1l | 0;
 287      this.v1h = v1h | 0;
 288      this.v2l = v2l | 0;
 289      this.v2h = v2h | 0;
 290      this.v3l = v3l | 0;
 291      this.v3h = v3h | 0;
 292      this.v4l = v4l | 0;
 293      this.v4h = v4h | 0;
 294      this.v5l = v5l | 0;
 295      this.v5h = v5h | 0;
 296      this.v6l = v6l | 0;
 297      this.v6h = v6h | 0;
 298      this.v7l = v7l | 0;
 299      this.v7h = v7h | 0;
 300    }
 301  
 302    _compress(msg, offset, isLast) {
 303      this._get().forEach((v, i) => (BBUF[i] = v));
 304      BBUF.set(B2B_IV, 16);
 305      let { h, l } = fromBig(BigInt(this.length));
 306      BBUF[24] = B2B_IV[8] ^ l;
 307      BBUF[25] = B2B_IV[9] ^ h;
 308      if (isLast) {
 309        BBUF[28] = ~BBUF[28];
 310        BBUF[29] = ~BBUF[29];
 311      }
 312      let j = 0;
 313      const s = BSIGMA;
 314      for (let i = 0; i < 12; i++) {
 315        G1b(0, 4, 8, 12, msg, offset + 2 * s[j++]);
 316        G2b(0, 4, 8, 12, msg, offset + 2 * s[j++]);
 317        G1b(1, 5, 9, 13, msg, offset + 2 * s[j++]);
 318        G2b(1, 5, 9, 13, msg, offset + 2 * s[j++]);
 319        G1b(2, 6, 10, 14, msg, offset + 2 * s[j++]);
 320        G2b(2, 6, 10, 14, msg, offset + 2 * s[j++]);
 321        G1b(3, 7, 11, 15, msg, offset + 2 * s[j++]);
 322        G2b(3, 7, 11, 15, msg, offset + 2 * s[j++]);
 323        G1b(0, 5, 10, 15, msg, offset + 2 * s[j++]);
 324        G2b(0, 5, 10, 15, msg, offset + 2 * s[j++]);
 325        G1b(1, 6, 11, 12, msg, offset + 2 * s[j++]);
 326        G2b(1, 6, 11, 12, msg, offset + 2 * s[j++]);
 327        G1b(2, 7, 8, 13, msg, offset + 2 * s[j++]);
 328        G2b(2, 7, 8, 13, msg, offset + 2 * s[j++]);
 329        G1b(3, 4, 9, 14, msg, offset + 2 * s[j++]);
 330        G2b(3, 4, 9, 14, msg, offset + 2 * s[j++]);
 331      }
 332      this.v0l ^= BBUF[0] ^ BBUF[16];
 333      this.v0h ^= BBUF[1] ^ BBUF[17];
 334      this.v1l ^= BBUF[2] ^ BBUF[18];
 335      this.v1h ^= BBUF[3] ^ BBUF[19];
 336      this.v2l ^= BBUF[4] ^ BBUF[20];
 337      this.v2h ^= BBUF[5] ^ BBUF[21];
 338      this.v3l ^= BBUF[6] ^ BBUF[22];
 339      this.v3h ^= BBUF[7] ^ BBUF[23];
 340      this.v4l ^= BBUF[8] ^ BBUF[24];
 341      this.v4h ^= BBUF[9] ^ BBUF[25];
 342      this.v5l ^= BBUF[10] ^ BBUF[26];
 343      this.v5h ^= BBUF[11] ^ BBUF[27];
 344      this.v6l ^= BBUF[12] ^ BBUF[28];
 345      this.v6h ^= BBUF[13] ^ BBUF[29];
 346      this.v7l ^= BBUF[14] ^ BBUF[30];
 347      this.v7h ^= BBUF[15] ^ BBUF[31];
 348      clean(BBUF);
 349    }
 350  
 351    update(data) {
 352      aexists(this);
 353      data = toBytes(data);
 354      abytes(data);
 355      const { blockLen, buffer, buffer32 } = this;
 356      const len = data.length;
 357      const offset = data.byteOffset;
 358      const buf = data.buffer;
 359      for (let pos = 0; pos < len; ) {
 360        if (this.pos === blockLen) {
 361          swap32IfBE(buffer32);
 362          this._compress(buffer32, 0, false);
 363          swap32IfBE(buffer32);
 364          this.pos = 0;
 365        }
 366        const take = Math.min(blockLen - this.pos, len - pos);
 367        const dataOffset = offset + pos;
 368        if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
 369          const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));
 370          swap32IfBE(data32);
 371          for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
 372            this.length += blockLen;
 373            this._compress(data32, pos32, false);
 374          }
 375          swap32IfBE(data32);
 376          continue;
 377        }
 378        buffer.set(data.subarray(pos, pos + take), this.pos);
 379        this.pos += take;
 380        this.length += take;
 381        pos += take;
 382      }
 383      return this;
 384    }
 385  
 386    digestInto(out) {
 387      aexists(this);
 388      aoutput(out, this);
 389      const { pos, buffer32 } = this;
 390      this.finished = true;
 391      clean(this.buffer.subarray(pos));
 392      swap32IfBE(buffer32);
 393      this._compress(buffer32, 0, true);
 394      swap32IfBE(buffer32);
 395      const out32 = u32(out);
 396      this._get().forEach((v, i) => (out32[i] = swap8IfBE(v)));
 397    }
 398  
 399    digest() {
 400      const { buffer, outputLen } = this;
 401      this.digestInto(buffer);
 402      const res = buffer.slice(0, outputLen);
 403      this.destroy();
 404      return res;
 405    }
 406  
 407    _cloneInto(to) {
 408      const { buffer, length, finished, destroyed, outputLen, pos } = this;
 409      to = to || new BLAKE2b({ dkLen: outputLen });
 410      to._set(...this._get());
 411      to.buffer.set(buffer);
 412      to.destroyed = destroyed;
 413      to.finished = finished;
 414      to.length = length;
 415      to.pos = pos;
 416      to.outputLen = outputLen;
 417      return to;
 418    }
 419  
 420    clone() {
 421      return this._cloneInto();
 422    }
 423  
 424    destroy() {
 425      this.destroyed = true;
 426      clean(this.buffer32);
 427      this._set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 428    }
 429  }
 430  
 431  // blake2b wrapper matching noble-hashes createOptHasher interface
 432  const blake2b = function (msg, opts) {
 433    return new BLAKE2b(opts).update(toBytes(msg)).digest();
 434  };
 435  blake2b.create = (opts) => new BLAKE2b(opts);
 436  blake2b.outputLen = 64;
 437  blake2b.blockLen = 128;
 438  
 439  // ============================================================
 440  // argon2.ts — full implementation
 441  // ============================================================
 442  
 443  const AT = { Argond2d: 0, Argon2i: 1, Argon2id: 2 };
 444  
 445  const ARGON2_SYNC_POINTS = 4;
 446  
 447  const abytesOrZero = (buf) => {
 448    if (buf === undefined) return Uint8Array.of();
 449    return kdfInputToBytes(buf);
 450  };
 451  
 452  function mul(a, b) {
 453    const aL = a & 0xffff;
 454    const aH = a >>> 16;
 455    const bL = b & 0xffff;
 456    const bH = b >>> 16;
 457    const ll = Math.imul(aL, bL);
 458    const hl = Math.imul(aH, bL);
 459    const lh = Math.imul(aL, bH);
 460    const hh = Math.imul(aH, bH);
 461    const carry = (ll >>> 16) + (hl & 0xffff) + lh;
 462    const high = (hh + (hl >>> 16) + (carry >>> 16)) | 0;
 463    const low = (carry << 16) | (ll & 0xffff);
 464    return { h: high, l: low };
 465  }
 466  
 467  function mul2(a, b) {
 468    const { h, l } = mul(a, b);
 469    return { h: ((h << 1) | (l >>> 31)) & 0xffff_ffff, l: (l << 1) & 0xffff_ffff };
 470  }
 471  
 472  function blamka(Ah, Al, Bh, Bl) {
 473    const { h: Ch, l: Cl } = mul2(Al, Bl);
 474    const Rll = add3L(Al, Bl, Cl);
 475    return { h: add3H(Rll, Ah, Bh, Ch), l: Rll | 0 };
 476  }
 477  
 478  const A2_BUF = new Uint32Array(256);
 479  
 480  function G(a, b, c, d) {
 481    let Al = A2_BUF[2*a], Ah = A2_BUF[2*a + 1];
 482    let Bl = A2_BUF[2*b], Bh = A2_BUF[2*b + 1];
 483    let Cl = A2_BUF[2*c], Ch = A2_BUF[2*c + 1];
 484    let Dl = A2_BUF[2*d], Dh = A2_BUF[2*d + 1];
 485  
 486    ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
 487    ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
 488    ({ Dh, Dl } = { Dh: rotr32H(Dh, Dl), Dl: rotr32L(Dh, Dl) });
 489  
 490    ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
 491    ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
 492    ({ Bh, Bl } = { Bh: rotrSH(Bh, Bl, 24), Bl: rotrSL(Bh, Bl, 24) });
 493  
 494    ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
 495    ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
 496    ({ Dh, Dl } = { Dh: rotrSH(Dh, Dl, 16), Dl: rotrSL(Dh, Dl, 16) });
 497  
 498    ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
 499    ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
 500    ({ Bh, Bl } = { Bh: rotrBH(Bh, Bl, 63), Bl: rotrBL(Bh, Bl, 63) });
 501  
 502    (A2_BUF[2 * a] = Al), (A2_BUF[2 * a + 1] = Ah);
 503    (A2_BUF[2 * b] = Bl), (A2_BUF[2 * b + 1] = Bh);
 504    (A2_BUF[2 * c] = Cl), (A2_BUF[2 * c + 1] = Ch);
 505    (A2_BUF[2 * d] = Dl), (A2_BUF[2 * d + 1] = Dh);
 506  }
 507  
 508  function P(
 509    v00, v01, v02, v03, v04, v05, v06, v07,
 510    v08, v09, v10, v11, v12, v13, v14, v15,
 511  ) {
 512    G(v00, v04, v08, v12);
 513    G(v01, v05, v09, v13);
 514    G(v02, v06, v10, v14);
 515    G(v03, v07, v11, v15);
 516    G(v00, v05, v10, v15);
 517    G(v01, v06, v11, v12);
 518    G(v02, v07, v08, v13);
 519    G(v03, v04, v09, v14);
 520  }
 521  
 522  function block(x, xPos, yPos, outPos, needXor) {
 523    for (let i = 0; i < 256; i++) A2_BUF[i] = x[xPos + i] ^ x[yPos + i];
 524    for (let i = 0; i < 128; i += 16) {
 525      P(
 526        i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7,
 527        i + 8, i + 9, i + 10, i + 11, i + 12, i + 13, i + 14, i + 15
 528      );
 529    }
 530    for (let i = 0; i < 16; i += 2) {
 531      P(
 532        i, i + 1, i + 16, i + 17, i + 32, i + 33, i + 48, i + 49,
 533        i + 64, i + 65, i + 80, i + 81, i + 96, i + 97, i + 112, i + 113
 534      );
 535    }
 536  
 537    if (needXor) for (let i = 0; i < 256; i++) x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
 538    else for (let i = 0; i < 256; i++) x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
 539    clean(A2_BUF);
 540  }
 541  
 542  function Hp(A, dkLen) {
 543    const A8 = u8(A);
 544    const T = new Uint32Array(1);
 545    const T8 = u8(T);
 546    T[0] = dkLen;
 547    if (dkLen <= 64) return blake2b.create({ dkLen }).update(T8).update(A8).digest();
 548    const out = new Uint8Array(dkLen);
 549    let V = blake2b.create({}).update(T8).update(A8).digest();
 550    let pos = 0;
 551    out.set(V.subarray(0, 32));
 552    pos += 32;
 553    for (; dkLen - pos > 64; pos += 32) {
 554      const Vh = blake2b.create({}).update(V);
 555      Vh.digestInto(V);
 556      Vh.destroy();
 557      out.set(V.subarray(0, 32), pos);
 558    }
 559    out.set(blake2b(V, { dkLen: dkLen - pos }), pos);
 560    clean(V, T);
 561    return u32(out);
 562  }
 563  
 564  function indexAlpha(r, s, laneLen, segmentLen, index, randL, sameLane = false) {
 565    let area;
 566    if (r === 0) {
 567      if (s === 0) area = index - 1;
 568      else if (sameLane) area = s * segmentLen + index - 1;
 569      else area = s * segmentLen + (index == 0 ? -1 : 0);
 570    } else if (sameLane) area = laneLen - segmentLen + index - 1;
 571    else area = laneLen - segmentLen + (index == 0 ? -1 : 0);
 572    const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
 573    const rel = area - 1 - mul(area, mul(randL, randL).h).h;
 574    return (startPos + rel) % laneLen;
 575  }
 576  
 577  const maxUint32 = Math.pow(2, 32);
 578  function isU32(num) {
 579    return Number.isSafeInteger(num) && num >= 0 && num < maxUint32;
 580  }
 581  
 582  function argon2Opts(opts) {
 583    const merged = {
 584      version: 0x13,
 585      dkLen: 32,
 586      maxmem: maxUint32 - 1,
 587      asyncTick: 10,
 588    };
 589    for (let [k, v] of Object.entries(opts)) if (v != null) merged[k] = v;
 590  
 591    const { dkLen, p, m, t, version, onProgress } = merged;
 592    if (!isU32(dkLen) || dkLen < 4) throw new Error('dkLen should be at least 4 bytes');
 593    if (!isU32(p) || p < 1 || p >= Math.pow(2, 24)) throw new Error('p should be 1 <= p < 2^24');
 594    if (!isU32(m)) throw new Error('m should be 0 <= m < 2^32');
 595    if (!isU32(t) || t < 1) throw new Error('t (iterations) should be 1 <= t < 2^32');
 596    if (onProgress !== undefined && typeof onProgress !== 'function')
 597      throw new Error('progressCb should be function');
 598    if (!isU32(m) || m < 8 * p) throw new Error('memory should be at least 8*p bytes');
 599    if (version !== 0x10 && version !== 0x13) throw new Error('unknown version=' + version);
 600    return merged;
 601  }
 602  
 603  function argon2Init(password, salt, type, opts) {
 604    password = kdfInputToBytes(password);
 605    salt = kdfInputToBytes(salt);
 606    abytes(password);
 607    abytes(salt);
 608    if (!isU32(password.length)) throw new Error('password should be less than 4 GB');
 609    if (!isU32(salt.length) || salt.length < 8)
 610      throw new Error('salt should be at least 8 bytes and less than 4 GB');
 611    if (!Object.values(AT).includes(type)) throw new Error('invalid type');
 612    let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress, asyncTick } =
 613      argon2Opts(opts);
 614  
 615    key = abytesOrZero(key);
 616    personalization = abytesOrZero(personalization);
 617  
 618    const h = blake2b.create({});
 619    const BUF = new Uint32Array(1);
 620    const BUF8 = u8(BUF);
 621    for (let item of [p, dkLen, m, t, version, type]) {
 622      BUF[0] = item;
 623      h.update(BUF8);
 624    }
 625    for (let i of [password, salt, key, personalization]) {
 626      BUF[0] = i.length;
 627      h.update(BUF8).update(i);
 628    }
 629    const H0 = new Uint32Array(18);
 630    const H0_8 = u8(H0);
 631    h.digestInto(H0_8);
 632  
 633    const lanes = p;
 634    const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
 635    const laneLen = Math.floor(mP / p);
 636    const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
 637    const memUsed = mP * 256;
 638    if (!isU32(maxmem) || memUsed > maxmem)
 639      throw new Error(
 640        'mem should be less than 2**32, got: maxmem=' + maxmem + ', memused=' + memUsed
 641      );
 642    const B = new Uint32Array(memUsed);
 643    for (let l = 0; l < p; l++) {
 644      const i = 256 * laneLen * l;
 645      H0[17] = l;
 646      H0[16] = 0;
 647      B.set(Hp(H0, 1024), i);
 648      H0[16] = 1;
 649      B.set(Hp(H0, 1024), i + 256);
 650    }
 651    let perBlock = () => {};
 652    if (onProgress) {
 653      const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen;
 654      const callbackPer = Math.max(Math.floor(totalBlock / 10000), 1);
 655      let blockCnt = 0;
 656      perBlock = () => {
 657        blockCnt++;
 658        if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
 659          onProgress(blockCnt / totalBlock);
 660      };
 661    }
 662    clean(BUF, H0);
 663    return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick };
 664  }
 665  
 666  function argon2Output(B, p, laneLen, dkLen) {
 667    const B_final = new Uint32Array(256);
 668    for (let l = 0; l < p; l++)
 669      for (let j = 0; j < 256; j++) B_final[j] ^= B[256 * (laneLen * l + laneLen - 1) + j];
 670    const res = u8(Hp(B_final, dkLen));
 671    clean(B_final);
 672    return res;
 673  }
 674  
 675  function processBlock(
 676    B, address, l, r, s, index,
 677    laneLen, segmentLen, lanes, offset, prev,
 678    dataIndependent, needXor
 679  ) {
 680    if (offset % laneLen) prev = offset - 1;
 681    let randL, randH;
 682    if (dataIndependent) {
 683      let i128 = index % 128;
 684      if (i128 === 0) {
 685        address[256 + 12]++;
 686        block(address, 256, 2 * 256, 0, false);
 687        block(address, 0, 2 * 256, 0, false);
 688      }
 689      randL = address[2 * i128];
 690      randH = address[2 * i128 + 1];
 691    } else {
 692      const T = 256 * prev;
 693      randL = B[T];
 694      randH = B[T + 1];
 695    }
 696    const refLane = r === 0 && s === 0 ? l : randH % lanes;
 697    const refPos = indexAlpha(r, s, laneLen, segmentLen, index, randL, refLane == l);
 698    const refBlock = laneLen * refLane + refPos;
 699    block(B, 256 * prev, 256 * refBlock, offset * 256, needXor);
 700  }
 701  
 702  function argon2(type, password, salt, opts) {
 703    const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock } = argon2Init(
 704      password,
 705      salt,
 706      type,
 707      opts
 708    );
 709    const address = new Uint32Array(3 * 256);
 710    address[256 + 6] = mP;
 711    address[256 + 8] = t;
 712    address[256 + 10] = type;
 713    for (let r = 0; r < t; r++) {
 714      const needXor = r !== 0 && version === 0x13;
 715      address[256 + 0] = r;
 716      for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
 717        address[256 + 4] = s;
 718        const dataIndependent = type == AT.Argon2i || (type == AT.Argon2id && r === 0 && s < 2);
 719        for (let l = 0; l < p; l++) {
 720          address[256 + 2] = l;
 721          address[256 + 12] = 0;
 722          let startPos = 0;
 723          if (r === 0 && s === 0) {
 724            startPos = 2;
 725            if (dataIndependent) {
 726              address[256 + 12]++;
 727              block(address, 256, 2 * 256, 0, false);
 728              block(address, 0, 2 * 256, 0, false);
 729            }
 730          }
 731          let offset = l * laneLen + s * segmentLen + startPos;
 732          let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
 733          for (let index = startPos; index < segmentLen; index++, offset++, prev++) {
 734            perBlock();
 735            processBlock(
 736              B, address, l, r, s, index,
 737              laneLen, segmentLen, lanes, offset, prev,
 738              dataIndependent, needXor
 739            );
 740          }
 741        }
 742      }
 743    }
 744    clean(address);
 745    return argon2Output(B, p, laneLen, dkLen);
 746  }
 747  
 748  function _argon2id(password, salt, opts) {
 749    return argon2(AT.Argon2id, password, salt, opts);
 750  }
 751  
 752  // ============================================================
 753  // Export: Go slice bridge
 754  // ============================================================
 755  
 756  // Called from Go jsbridge — parameters are Go slices.
 757  export function Argon2id(password, salt, t, m, p, dkLen, fn) {
 758    const pw = sliceToU8(password);
 759    const sl = sliceToU8(salt);
 760    const result = _argon2id(pw, sl, { t, m, p, dkLen });
 761    fn(u8ToSlice(result));
 762  }
 763  
 764  // Called from JS (subtle.mjs) — password is JS string, salt is Uint8Array.
 765  export function argon2id(password, salt, opts) {
 766    return _argon2id(password, salt, opts);
 767  }
 768