e2179aa684e29d532c82c9cc28f42644e846379a124716f5762157e83a75ddd4.json raw
1 {"ast":null,"code":"// prettier-ignore\nimport { wrapCipher, createView, equalBytes, setBigUint64 } from './utils.js';\nimport { poly1305 } from './_poly1305.js';\nimport { createCipher, rotl } from './_arx.js';\nimport { bytes as abytes } from './_assert.js';\n// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase\n// the diffusion per round, but had slightly less cryptanalysis.\n// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf\n/**\n * ChaCha core function.\n */\n// prettier-ignore\nfunction chachaCore(s, k, n, out, cnt, rounds = 20) {\n let y00 = s[0],\n y01 = s[1],\n y02 = s[2],\n y03 = s[3],\n // \"expa\" \"nd 3\" \"2-by\" \"te k\"\n y04 = k[0],\n y05 = k[1],\n y06 = k[2],\n y07 = k[3],\n // Key Key Key Key\n y08 = k[4],\n y09 = k[5],\n y10 = k[6],\n y11 = k[7],\n // Key Key Key Key\n y12 = cnt,\n y13 = n[0],\n y14 = n[1],\n y15 = n[2]; // Counter Counter\tNonce Nonce\n // Save state to temporary variables\n let x00 = y00,\n x01 = y01,\n x02 = y02,\n x03 = y03,\n x04 = y04,\n x05 = y05,\n x06 = y06,\n x07 = y07,\n x08 = y08,\n x09 = y09,\n x10 = y10,\n x11 = y11,\n x12 = y12,\n x13 = y13,\n x14 = y14,\n x15 = y15;\n for (let r = 0; r < rounds; r += 2) {\n x00 = x00 + x04 | 0;\n x12 = rotl(x12 ^ x00, 16);\n x08 = x08 + x12 | 0;\n x04 = rotl(x04 ^ x08, 12);\n x00 = x00 + x04 | 0;\n x12 = rotl(x12 ^ x00, 8);\n x08 = x08 + x12 | 0;\n x04 = rotl(x04 ^ x08, 7);\n x01 = x01 + x05 | 0;\n x13 = rotl(x13 ^ x01, 16);\n x09 = x09 + x13 | 0;\n x05 = rotl(x05 ^ x09, 12);\n x01 = x01 + x05 | 0;\n x13 = rotl(x13 ^ x01, 8);\n x09 = x09 + x13 | 0;\n x05 = rotl(x05 ^ x09, 7);\n x02 = x02 + x06 | 0;\n x14 = rotl(x14 ^ x02, 16);\n x10 = x10 + x14 | 0;\n x06 = rotl(x06 ^ x10, 12);\n x02 = x02 + x06 | 0;\n x14 = rotl(x14 ^ x02, 8);\n x10 = x10 + x14 | 0;\n x06 = rotl(x06 ^ x10, 7);\n x03 = x03 + x07 | 0;\n x15 = rotl(x15 ^ x03, 16);\n x11 = x11 + x15 | 0;\n x07 = rotl(x07 ^ x11, 12);\n x03 = x03 + x07 | 0;\n x15 = rotl(x15 ^ x03, 8);\n x11 = x11 + x15 | 0;\n x07 = rotl(x07 ^ x11, 7);\n x00 = x00 + x05 | 0;\n x15 = rotl(x15 ^ x00, 16);\n x10 = x10 + x15 | 0;\n x05 = rotl(x05 ^ x10, 12);\n x00 = x00 + x05 | 0;\n x15 = rotl(x15 ^ x00, 8);\n x10 = x10 + x15 | 0;\n x05 = rotl(x05 ^ x10, 7);\n x01 = x01 + x06 | 0;\n x12 = rotl(x12 ^ x01, 16);\n x11 = x11 + x12 | 0;\n x06 = rotl(x06 ^ x11, 12);\n x01 = x01 + x06 | 0;\n x12 = rotl(x12 ^ x01, 8);\n x11 = x11 + x12 | 0;\n x06 = rotl(x06 ^ x11, 7);\n x02 = x02 + x07 | 0;\n x13 = rotl(x13 ^ x02, 16);\n x08 = x08 + x13 | 0;\n x07 = rotl(x07 ^ x08, 12);\n x02 = x02 + x07 | 0;\n x13 = rotl(x13 ^ x02, 8);\n x08 = x08 + x13 | 0;\n x07 = rotl(x07 ^ x08, 7);\n x03 = x03 + x04 | 0;\n x14 = rotl(x14 ^ x03, 16);\n x09 = x09 + x14 | 0;\n x04 = rotl(x04 ^ x09, 12);\n x03 = x03 + x04 | 0;\n x14 = rotl(x14 ^ x03, 8);\n x09 = x09 + x14 | 0;\n x04 = rotl(x04 ^ x09, 7);\n }\n // Write output\n let oi = 0;\n out[oi++] = y00 + x00 | 0;\n out[oi++] = y01 + x01 | 0;\n out[oi++] = y02 + x02 | 0;\n out[oi++] = y03 + x03 | 0;\n out[oi++] = y04 + x04 | 0;\n out[oi++] = y05 + x05 | 0;\n out[oi++] = y06 + x06 | 0;\n out[oi++] = y07 + x07 | 0;\n out[oi++] = y08 + x08 | 0;\n out[oi++] = y09 + x09 | 0;\n out[oi++] = y10 + x10 | 0;\n out[oi++] = y11 + x11 | 0;\n out[oi++] = y12 + x12 | 0;\n out[oi++] = y13 + x13 | 0;\n out[oi++] = y14 + x14 | 0;\n out[oi++] = y15 + x15 | 0;\n}\n/**\n * hchacha helper method, used primarily in xchacha, to hash\n * key and nonce into key' and nonce'.\n * Same as chachaCore, but there doesn't seem to be a way to move the block\n * out without 25% performance hit.\n */\n// prettier-ignore\nexport function hchacha(s, k, i, o32) {\n let x00 = s[0],\n x01 = s[1],\n x02 = s[2],\n x03 = s[3],\n x04 = k[0],\n x05 = k[1],\n x06 = k[2],\n x07 = k[3],\n x08 = k[4],\n x09 = k[5],\n x10 = k[6],\n x11 = k[7],\n x12 = i[0],\n x13 = i[1],\n x14 = i[2],\n x15 = i[3];\n for (let r = 0; r < 20; r += 2) {\n x00 = x00 + x04 | 0;\n x12 = rotl(x12 ^ x00, 16);\n x08 = x08 + x12 | 0;\n x04 = rotl(x04 ^ x08, 12);\n x00 = x00 + x04 | 0;\n x12 = rotl(x12 ^ x00, 8);\n x08 = x08 + x12 | 0;\n x04 = rotl(x04 ^ x08, 7);\n x01 = x01 + x05 | 0;\n x13 = rotl(x13 ^ x01, 16);\n x09 = x09 + x13 | 0;\n x05 = rotl(x05 ^ x09, 12);\n x01 = x01 + x05 | 0;\n x13 = rotl(x13 ^ x01, 8);\n x09 = x09 + x13 | 0;\n x05 = rotl(x05 ^ x09, 7);\n x02 = x02 + x06 | 0;\n x14 = rotl(x14 ^ x02, 16);\n x10 = x10 + x14 | 0;\n x06 = rotl(x06 ^ x10, 12);\n x02 = x02 + x06 | 0;\n x14 = rotl(x14 ^ x02, 8);\n x10 = x10 + x14 | 0;\n x06 = rotl(x06 ^ x10, 7);\n x03 = x03 + x07 | 0;\n x15 = rotl(x15 ^ x03, 16);\n x11 = x11 + x15 | 0;\n x07 = rotl(x07 ^ x11, 12);\n x03 = x03 + x07 | 0;\n x15 = rotl(x15 ^ x03, 8);\n x11 = x11 + x15 | 0;\n x07 = rotl(x07 ^ x11, 7);\n x00 = x00 + x05 | 0;\n x15 = rotl(x15 ^ x00, 16);\n x10 = x10 + x15 | 0;\n x05 = rotl(x05 ^ x10, 12);\n x00 = x00 + x05 | 0;\n x15 = rotl(x15 ^ x00, 8);\n x10 = x10 + x15 | 0;\n x05 = rotl(x05 ^ x10, 7);\n x01 = x01 + x06 | 0;\n x12 = rotl(x12 ^ x01, 16);\n x11 = x11 + x12 | 0;\n x06 = rotl(x06 ^ x11, 12);\n x01 = x01 + x06 | 0;\n x12 = rotl(x12 ^ x01, 8);\n x11 = x11 + x12 | 0;\n x06 = rotl(x06 ^ x11, 7);\n x02 = x02 + x07 | 0;\n x13 = rotl(x13 ^ x02, 16);\n x08 = x08 + x13 | 0;\n x07 = rotl(x07 ^ x08, 12);\n x02 = x02 + x07 | 0;\n x13 = rotl(x13 ^ x02, 8);\n x08 = x08 + x13 | 0;\n x07 = rotl(x07 ^ x08, 7);\n x03 = x03 + x04 | 0;\n x14 = rotl(x14 ^ x03, 16);\n x09 = x09 + x14 | 0;\n x04 = rotl(x04 ^ x09, 12);\n x03 = x03 + x04 | 0;\n x14 = rotl(x14 ^ x03, 8);\n x09 = x09 + x14 | 0;\n x04 = rotl(x04 ^ x09, 7);\n }\n let oi = 0;\n o32[oi++] = x00;\n o32[oi++] = x01;\n o32[oi++] = x02;\n o32[oi++] = x03;\n o32[oi++] = x12;\n o32[oi++] = x13;\n o32[oi++] = x14;\n o32[oi++] = x15;\n}\n/**\n * Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.\n */\nexport const chacha20orig = /* @__PURE__ */createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n allowShortKeys: true\n});\n/**\n * ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.\n * With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.\n */\nexport const chacha20 = /* @__PURE__ */createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n allowShortKeys: false\n});\n/**\n * XChaCha eXtended-nonce ChaCha. 24-byte nonce.\n * With 24-byte nonce, it's safe to use fill it with random (CSPRNG).\n * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha\n */\nexport const xchacha20 = /* @__PURE__ */createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n extendNonceFn: hchacha,\n allowShortKeys: false\n});\n/**\n * Reduced 8-round chacha, described in original paper.\n */\nexport const chacha8 = /* @__PURE__ */createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 8\n});\n/**\n * Reduced 12-round chacha, described in original paper.\n */\nexport const chacha12 = /* @__PURE__ */createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 12\n});\nconst ZEROS16 = /* @__PURE__ */new Uint8Array(16);\n// Pad to digest size with zeros\nconst updatePadded = (h, msg) => {\n h.update(msg);\n const left = msg.length % 16;\n if (left) h.update(ZEROS16.subarray(left));\n};\nconst ZEROS32 = /* @__PURE__ */new Uint8Array(32);\nfunction computeTag(fn, key, nonce, data, AAD) {\n const authKey = fn(key, nonce, ZEROS32);\n const h = poly1305.create(authKey);\n if (AAD) updatePadded(h, AAD);\n updatePadded(h, data);\n const num = new Uint8Array(16);\n const view = createView(num);\n setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);\n setBigUint64(view, 8, BigInt(data.length), true);\n h.update(num);\n const res = h.digest();\n authKey.fill(0);\n return res;\n}\n/**\n * AEAD algorithm from RFC 8439.\n * Salsa20 and chacha (RFC 8439) use poly1305 differently.\n * We could have composed them similar to:\n * https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250\n * But it's hard because of authKey:\n * In salsa20, authKey changes position in salsa stream.\n * In chacha, authKey can't be computed inside computeTag, it modifies the counter.\n */\nexport const _poly1305_aead = xorStream => (key, nonce, AAD) => {\n const tagLength = 16;\n abytes(key, 32);\n abytes(nonce);\n return {\n encrypt: (plaintext, output) => {\n const plength = plaintext.length;\n const clength = plength + tagLength;\n if (output) {\n abytes(output, clength);\n } else {\n output = new Uint8Array(clength);\n }\n xorStream(key, nonce, plaintext, output, 1);\n const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);\n output.set(tag, plength); // append tag\n return output;\n },\n decrypt: (ciphertext, output) => {\n const clength = ciphertext.length;\n const plength = clength - tagLength;\n if (clength < tagLength) throw new Error(`encrypted data must be at least ${tagLength} bytes`);\n if (output) {\n abytes(output, plength);\n } else {\n output = new Uint8Array(plength);\n }\n const data = ciphertext.subarray(0, -tagLength);\n const passedTag = ciphertext.subarray(-tagLength);\n const tag = computeTag(xorStream, key, nonce, data, AAD);\n if (!equalBytes(passedTag, tag)) throw new Error('invalid tag');\n xorStream(key, nonce, data, output, 1);\n return output;\n }\n };\n};\n/**\n * ChaCha20-Poly1305 from RFC 8439.\n * With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.\n */\nexport const chacha20poly1305 = /* @__PURE__ */wrapCipher({\n blockSize: 64,\n nonceLength: 12,\n tagLength: 16\n}, _poly1305_aead(chacha20));\n/**\n * XChaCha20-Poly1305 extended-nonce chacha.\n * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha\n * With 24-byte nonce, it's safe to use fill it with random (CSPRNG).\n */\nexport const xchacha20poly1305 = /* @__PURE__ */wrapCipher({\n blockSize: 64,\n nonceLength: 24,\n tagLength: 16\n}, _poly1305_aead(xchacha20));\n//# sourceMappingURL=chacha.js.map","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}