{"ast":null,"code":"// Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.\nimport { number as anumber, bytes as abytes, bool as abool } from './_assert.js';\nimport { checkOpts, u32 } from './utils.js';\n/*\nRFC8439 requires multi-step cipher stream, where\nauthKey starts with counter: 0, actual msg with counter: 1.\n\nFor this, we need a way to re-use nonce / counter:\n\n    const counter = new Uint8Array(4);\n    chacha(..., counter, ...); // counter is now 1\n    chacha(..., counter, ...); // counter is now 2\n\nThis is complicated:\n\n- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB\n- Original papers don't allow mutating counters\n- Counter overflow is undefined [^1]\n- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it\n- Caveat: Cannot be re-used through all cases:\n- * chacha has (counter | nonce)\n- * xchacha has (nonce16 | counter | nonce16)\n- Idea B: separate nonce / counter and provide separate API for counter re-use\n- Caveat: there are different counter sizes depending on an algorithm.\n- salsa & chacha also differ in structures of key & sigma:\n  salsa20:      s[0] | k(4) | s[1] | nonce(2) | ctr(2) | s[2] | k(4) | s[3]\n  chacha:       s(4) | k(8) | ctr(1) | nonce(3)\n  chacha20orig: s(4) | k(8) | ctr(2) | nonce(2)\n- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`\n- Caveat: we can't re-use counter array\n\nxchacha [^2] uses the subkey and remaining 8 byte nonce with ChaCha20 as normal\n(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).\n\n[^1]: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/\n[^2]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2\n*/\n// We can't make top-level var depend on utils.utf8ToBytes\n// because it's not present in all envs. Creating a similar fn here\nconst _utf8ToBytes = str => Uint8Array.from(str.split('').map(c => c.charCodeAt(0)));\nconst sigma16 = _utf8ToBytes('expand 16-byte k');\nconst sigma32 = _utf8ToBytes('expand 32-byte k');\nconst sigma16_32 = u32(sigma16);\nconst sigma32_32 = u32(sigma32);\nexport const sigma = sigma32_32.slice();\nexport function rotl(a, b) {\n  return a << b | a >>> 32 - b;\n}\n// Is byte array aligned to 4 byte offset (u32)?\nfunction isAligned32(b) {\n  return b.byteOffset % 4 === 0;\n}\n// Salsa and Chacha block length is always 512-bit\nconst BLOCK_LEN = 64;\nconst BLOCK_LEN32 = 16;\n// new Uint32Array([2**32])   // => Uint32Array(1) [ 0 ]\n// new Uint32Array([2**32-1]) // => Uint32Array(1) [ 4294967295 ]\nconst MAX_COUNTER = 2 ** 32 - 1;\nconst U32_EMPTY = new Uint32Array();\nfunction runCipher(core, sigma, key, nonce, data, output, counter, rounds) {\n  const len = data.length;\n  const block = new Uint8Array(BLOCK_LEN);\n  const b32 = u32(block);\n  // Make sure that buffers aligned to 4 bytes\n  const isAligned = isAligned32(data) && isAligned32(output);\n  const d32 = isAligned ? u32(data) : U32_EMPTY;\n  const o32 = isAligned ? u32(output) : U32_EMPTY;\n  for (let pos = 0; pos < len; counter++) {\n    core(sigma, key, nonce, b32, counter, rounds);\n    if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n    const take = Math.min(BLOCK_LEN, len - pos);\n    // aligned to 4 bytes\n    if (isAligned && take === BLOCK_LEN) {\n      const pos32 = pos / 4;\n      if (pos % 4 !== 0) throw new Error('arx: invalid block position');\n      for (let j = 0, posj; j < BLOCK_LEN32; j++) {\n        posj = pos32 + j;\n        o32[posj] = d32[posj] ^ b32[j];\n      }\n      pos += BLOCK_LEN;\n      continue;\n    }\n    for (let j = 0, posj; j < take; j++) {\n      posj = pos + j;\n      output[posj] = data[posj] ^ block[j];\n    }\n    pos += take;\n  }\n}\nexport function createCipher(core, opts) {\n  const {\n    allowShortKeys,\n    extendNonceFn,\n    counterLength,\n    counterRight,\n    rounds\n  } = checkOpts({\n    allowShortKeys: false,\n    counterLength: 8,\n    counterRight: false,\n    rounds: 20\n  }, opts);\n  if (typeof core !== 'function') throw new Error('core must be a function');\n  anumber(counterLength);\n  anumber(rounds);\n  abool(counterRight);\n  abool(allowShortKeys);\n  return (key, nonce, data, output, counter = 0) => {\n    abytes(key);\n    abytes(nonce);\n    abytes(data);\n    const len = data.length;\n    if (!output) output = new Uint8Array(len);\n    abytes(output);\n    anumber(counter);\n    if (counter < 0 || counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n    if (output.length < len) throw new Error(`arx: output (${output.length}) is shorter than data (${len})`);\n    const toClean = [];\n    // Key & sigma\n    // key=16 -> sigma16, k=key|key\n    // key=32 -> sigma32, k=key\n    let l = key.length,\n      k,\n      sigma;\n    if (l === 32) {\n      k = key.slice();\n      toClean.push(k);\n      sigma = sigma32_32;\n    } else if (l === 16 && allowShortKeys) {\n      k = new Uint8Array(32);\n      k.set(key);\n      k.set(key, 16);\n      sigma = sigma16_32;\n      toClean.push(k);\n    } else {\n      throw new Error(`arx: invalid 32-byte key, got length=${l}`);\n    }\n    // Nonce\n    // salsa20:      8   (8-byte counter)\n    // chacha20orig: 8   (8-byte counter)\n    // chacha20:     12  (4-byte counter)\n    // xsalsa20:     24  (16 -> hsalsa,  8 -> old nonce)\n    // xchacha20:    24  (16 -> hchacha, 8 -> old nonce)\n    // Align nonce to 4 bytes\n    if (!isAligned32(nonce)) {\n      nonce = nonce.slice();\n      toClean.push(nonce);\n    }\n    const k32 = u32(k);\n    // hsalsa & hchacha: handle extended nonce\n    if (extendNonceFn) {\n      if (nonce.length !== 24) throw new Error(`arx: extended nonce must be 24 bytes`);\n      extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);\n      nonce = nonce.subarray(16);\n    }\n    // Handle nonce counter\n    const nonceNcLen = 16 - counterLength;\n    if (nonceNcLen !== nonce.length) throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);\n    // Pad counter when nonce is 64 bit\n    if (nonceNcLen !== 12) {\n      const nc = new Uint8Array(12);\n      nc.set(nonce, counterRight ? 0 : 12 - nonce.length);\n      nonce = nc;\n      toClean.push(nonce);\n    }\n    const n32 = u32(nonce);\n    runCipher(core, sigma, k32, n32, data, output, counter, rounds);\n    while (toClean.length > 0) toClean.pop().fill(0);\n    return output;\n  };\n}\n//# sourceMappingURL=_arx.js.map","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}