diff --git a/packages/chacha20poly1305/src/chacha/_arx.ts b/packages/chacha20poly1305/src/chacha/_arx.ts index e037611320..ebde12f0dd 100644 --- a/packages/chacha20poly1305/src/chacha/_arx.ts +++ b/packages/chacha20poly1305/src/chacha/_arx.ts @@ -52,11 +52,9 @@ import { checkOpts, clean, copyBytes, - type PRG, u32, type XorStream, } from "./utils.ts"; -import { randomBytes } from "./webcrypto.ts"; // Can't use similar utils.utf8ToBytes, because it uses `TextEncoder` - not available in all envs const _utf8ToBytes = (str: string) => @@ -116,6 +114,7 @@ const BLOCK_LEN32 = 16; const MAX_COUNTER = 2 ** 32 - 1; const U32_EMPTY = Uint32Array.of(); + function runCipher( core: CipherCoreFn, sigma: Uint32Array, @@ -256,109 +255,3 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream { return output; }; } - -/** Internal class which wraps chacha20 or chacha8 to create CSPRNG. */ -export class _XorStreamPRG implements PRG { - readonly blockLen: number; - readonly keyLen: number; - readonly nonceLen: number; - private state: Uint8Array; - private buf: Uint8Array; - private key: Uint8Array; - private nonce: Uint8Array; - private pos: number; - private ctr: number; - private cipher: XorStream; - constructor( - cipher: XorStream, - blockLen: number, - keyLen: number, - nonceLen: number, - seed: Uint8Array, - ) { - this.cipher = cipher; - this.blockLen = blockLen; - this.keyLen = keyLen; - this.nonceLen = nonceLen; - this.state = new Uint8Array(this.keyLen + this.nonceLen); - this.reseed(seed); - this.ctr = 0; - this.pos = this.blockLen; - this.buf = new Uint8Array(this.blockLen); - this.key = this.state.subarray(0, this.keyLen); - this.nonce = this.state.subarray(this.keyLen); - } - private reseed(seed: Uint8Array) { - abytes(seed); - if (!seed || seed.length === 0) throw new Error("entropy required"); - for (let i = 0; i < seed.length; i++) { - this.state[i % this.state.length] ^= seed[i]; - } - this.ctr = 0; - this.pos = this.blockLen; - } - addEntropy(seed: Uint8Array): void { - this.state.set(this.randomBytes(this.state.length)); - this.reseed(seed); - } - randomBytes(len: number): Uint8Array { - anumber(len); - if (len === 0) return new Uint8Array(0); - const out = new Uint8Array(len); - let outPos = 0; - // Leftovers - if (this.pos < this.blockLen) { - const take = Math.min(len, this.blockLen - this.pos); - out.set(this.buf.subarray(this.pos, this.pos + take), 0); - this.pos += take; - outPos += take; - if (outPos === len) return out; // fast path - } - // Full blocks directly to out - const blocks = Math.floor((len - outPos) / this.blockLen); - if (blocks > 0) { - const blockBytes = blocks * this.blockLen; - const b = out.subarray(outPos, outPos + blockBytes); - this.cipher(this.key, this.nonce, b, b, this.ctr); - this.ctr += blocks; - outPos += blockBytes; - } - // Save leftovers - const left = len - outPos; - if (left > 0) { - this.buf.fill(0); - // NOTE: cipher will handle overflow - this.cipher(this.key, this.nonce, this.buf, this.buf, this.ctr++); - out.set(this.buf.subarray(0, left), outPos); - this.pos = left; - } - return out; - } - clone(): _XorStreamPRG { - return new _XorStreamPRG( - this.cipher, - this.blockLen, - this.keyLen, - this.nonceLen, - this.randomBytes(this.state.length), - ); - } - clean(): void { - this.pos = 0; - this.ctr = 0; - this.buf.fill(0); - this.state.fill(0); - } -} - -export type XorPRG = (seed?: Uint8Array) => _XorStreamPRG; - -export const createPRG = ( - cipher: XorStream, - blockLen: number, - keyLen: number, - nonceLen: number, -): XorPRG => { - return (seed: Uint8Array = randomBytes(32)): _XorStreamPRG => - new _XorStreamPRG(cipher, blockLen, keyLen, nonceLen, seed); -}; diff --git a/packages/chacha20poly1305/src/chacha/chacha.ts b/packages/chacha20poly1305/src/chacha/chacha.ts index 62e5e57db5..797563efcb 100644 --- a/packages/chacha20poly1305/src/chacha/chacha.ts +++ b/packages/chacha20poly1305/src/chacha/chacha.ts @@ -23,7 +23,7 @@ * * @module */ -import { createCipher, createPRG, rotl, type XorPRG } from "./_arx.ts"; +import { createCipher, rotl } from "./_arx.ts"; import { poly1305 } from "./_poly1305.ts"; import { abytes, @@ -43,88 +43,6 @@ import { * 2. Unrolled loop (chachaCore, hchacha) - 4x faster, but larger & harder to read * The specific implementation is selected in `createCipher` below. */ - -// /** quarter-round */ -// // prettier-ignore -// function chachaQR(x: Uint32Array, a: number, b: number, c: number, d: number) { -// x[a] = (x[a] + x[b]) | 0; -// x[d] = rotl(x[d] ^ x[a], 16); -// x[c] = (x[c] + x[d]) | 0; -// x[b] = rotl(x[b] ^ x[c], 12); -// x[a] = (x[a] + x[b]) | 0; -// x[d] = rotl(x[d] ^ x[a], 8); -// x[c] = (x[c] + x[d]) | 0; -// x[b] = rotl(x[b] ^ x[c], 7); -// } - -// /** single round */ -// function chachaRound(x: Uint32Array, rounds = 20) { -// for (let r = 0; r < rounds; r += 2) { -// chachaQR(x, 0, 4, 8, 12); -// chachaQR(x, 1, 5, 9, 13); -// chachaQR(x, 2, 6, 10, 14); -// chachaQR(x, 3, 7, 11, 15); -// chachaQR(x, 0, 5, 10, 15); -// chachaQR(x, 1, 6, 11, 12); -// chachaQR(x, 2, 7, 8, 13); -// chachaQR(x, 3, 4, 9, 14); -// } -// } -// -// const ctmp = /* @__PURE__ */ new Uint32Array(16); - -/** Small version of chacha without loop unrolling. Unused, provided for auditability. */ -// // prettier-ignore -// function chacha( -// s: Uint32Array, -// k: Uint32Array, -// i: Uint32Array, -// out: Uint32Array, -// isHChacha: boolean = true, -// rounds: number = 20, -// ): void { -// // Create initial array using common pattern -// const y = Uint32Array.from([ -// s[0], -// s[1], -// s[2], -// s[3], // "expa" "nd 3" "2-by" "te k" -// k[0], -// k[1], -// k[2], -// k[3], // Key Key Key Key -// k[4], -// k[5], -// k[6], -// k[7], // Key Key Key Key -// i[0], -// i[1], -// i[2], -// i[3], // Counter Counter Nonce Nonce -// ]); -// const x = ctmp; -// x.set(y); -// chachaRound(x, rounds); -// -// // hchacha extracts 8 specific bytes, chacha adds orig to result -// if (isHChacha) { -// const xindexes = [0, 1, 2, 3, 12, 13, 14, 15]; -// for (let i = 0; i < 8; i++) out[i] = x[xindexes[i]]; -// } else { -// for (let i = 0; i < 16; i++) out[i] = (y[i] + x[i]) | 0; -// } -// } - -/** Identical to `chachaCore`. Unused. */ -// // @ts-ignore: to ignore "isHChacha" -// const chachaCore_small: typeof chachaCore = (s, k, n, out, cnt, rounds) => -// chacha(s, k, Uint32Array.from([n[0], n[1], cnt, 0]), out, false, rounds); -/** Identical to `hchacha`. Unused. */ -// // @ts-ignore: to ignore "isHChacha" -// const hchacha_small: typeof hchacha = chacha; - -/** Identical to `chachaCore_small`. Unused. */ -// prettier-ignore function chachaCore( s: Uint32Array, k: Uint32Array, @@ -258,127 +176,7 @@ function chachaCore( out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0; } -// /** -// * hchacha hashes key and nonce into key' and nonce' for xchacha20. -// * Identical to `hchacha_small`. -// * Need to find a way to merge it with `chachaCore` without 25% performance hit. -// */ -// // prettier-ignore -// export function hchacha( -// s: Uint32Array, -// k: Uint32Array, -// i: Uint32Array, -// out: Uint32Array, -// ): void { -// let x00 = s[0], -// x01 = s[1], -// x02 = s[2], -// x03 = s[3], -// x04 = k[0], -// x05 = k[1], -// x06 = k[2], -// x07 = k[3], -// x08 = k[4], -// x09 = k[5], -// x10 = k[6], -// x11 = k[7], -// x12 = i[0], -// x13 = i[1], -// x14 = i[2], -// x15 = i[3]; -// for (let r = 0; r < 20; r += 2) { -// x00 = (x00 + x04) | 0; -// x12 = rotl(x12 ^ x00, 16); -// x08 = (x08 + x12) | 0; -// x04 = rotl(x04 ^ x08, 12); -// x00 = (x00 + x04) | 0; -// x12 = rotl(x12 ^ x00, 8); -// x08 = (x08 + x12) | 0; -// x04 = rotl(x04 ^ x08, 7); -// -// x01 = (x01 + x05) | 0; -// x13 = rotl(x13 ^ x01, 16); -// x09 = (x09 + x13) | 0; -// x05 = rotl(x05 ^ x09, 12); -// x01 = (x01 + x05) | 0; -// x13 = rotl(x13 ^ x01, 8); -// x09 = (x09 + x13) | 0; -// x05 = rotl(x05 ^ x09, 7); -// -// x02 = (x02 + x06) | 0; -// x14 = rotl(x14 ^ x02, 16); -// x10 = (x10 + x14) | 0; -// x06 = rotl(x06 ^ x10, 12); -// x02 = (x02 + x06) | 0; -// x14 = rotl(x14 ^ x02, 8); -// x10 = (x10 + x14) | 0; -// x06 = rotl(x06 ^ x10, 7); -// -// x03 = (x03 + x07) | 0; -// x15 = rotl(x15 ^ x03, 16); -// x11 = (x11 + x15) | 0; -// x07 = rotl(x07 ^ x11, 12); -// x03 = (x03 + x07) | 0; -// x15 = rotl(x15 ^ x03, 8); -// x11 = (x11 + x15) | 0; -// x07 = rotl(x07 ^ x11, 7); -// -// x00 = (x00 + x05) | 0; -// x15 = rotl(x15 ^ x00, 16); -// x10 = (x10 + x15) | 0; -// x05 = rotl(x05 ^ x10, 12); -// x00 = (x00 + x05) | 0; -// x15 = rotl(x15 ^ x00, 8); -// x10 = (x10 + x15) | 0; -// x05 = rotl(x05 ^ x10, 7); -// -// x01 = (x01 + x06) | 0; -// x12 = rotl(x12 ^ x01, 16); -// x11 = (x11 + x12) | 0; -// x06 = rotl(x06 ^ x11, 12); -// x01 = (x01 + x06) | 0; -// x12 = rotl(x12 ^ x01, 8); -// x11 = (x11 + x12) | 0; -// x06 = rotl(x06 ^ x11, 7); -// -// x02 = (x02 + x07) | 0; -// x13 = rotl(x13 ^ x02, 16); -// x08 = (x08 + x13) | 0; -// x07 = rotl(x07 ^ x08, 12); -// x02 = (x02 + x07) | 0; -// x13 = rotl(x13 ^ x02, 8); -// x08 = (x08 + x13) | 0; -// x07 = rotl(x07 ^ x08, 7); -// -// x03 = (x03 + x04) | 0; -// x14 = rotl(x14 ^ x03, 16); -// x09 = (x09 + x14) | 0; -// x04 = rotl(x04 ^ x09, 12); -// x03 = (x03 + x04) | 0; -// x14 = rotl(x14 ^ x03, 8); -// x09 = (x09 + x14) | 0; -// x04 = rotl(x04 ^ x09, 7); -// } -// let oi = 0; -// out[oi++] = x00; -// out[oi++] = x01; -// out[oi++] = x02; -// out[oi++] = x03; -// out[oi++] = x12; -// out[oi++] = x13; -// out[oi++] = x14; -// out[oi++] = x15; -// } -/** Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter. */ -export const chacha20orig: XorStream = /* @__PURE__ */ createCipher( - chachaCore, - { - counterRight: false, - counterLength: 8, - allowShortKeys: true, - }, -); /** * ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter. * With smaller nonce, it's not safe to make it random (CSPRNG), due to collision chance. @@ -389,31 +187,6 @@ export const chacha20: XorStream = /* @__PURE__ */ createCipher(chachaCore, { allowShortKeys: false, }); -// /** -// * XChaCha eXtended-nonce ChaCha. With 24-byte nonce, it's safe to make it random (CSPRNG). -// * See [IRTF draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha). -// */ -// export const xchacha20: XorStream = /* @__PURE__ */ createCipher(chachaCore, { -// counterRight: false, -// counterLength: 8, -// extendNonceFn: hchacha, -// allowShortKeys: false, -// }); - -/** Reduced 8-round chacha, described in original paper. */ -export const chacha8: XorStream = /* @__PURE__ */ createCipher(chachaCore, { - counterRight: false, - counterLength: 4, - rounds: 8, -}); - -/** Reduced 12-round chacha, described in original paper. */ -export const chacha12: XorStream = /* @__PURE__ */ createCipher(chachaCore, { - counterRight: false, - counterLength: 4, - rounds: 12, -}); - const ZEROS16 = /* @__PURE__ */ new Uint8Array(16); // Pad to digest size with zeros const updatePadded = ( @@ -497,37 +270,3 @@ export const chacha20poly1305: ARXCipher = /* @__PURE__ */ wrapCipher( { blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20), ); - -// /** -// * XChaCha20-Poly1305 extended-nonce chacha. -// * -// * Can be safely used with random nonces (CSPRNG). -// * See [IRTF draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha). -// */ -// export const xchacha20poly1305: ARXCipher = /* @__PURE__ */ wrapCipher( -// { blockSize: 64, nonceLength: 24, tagLength: 16 }, -// _poly1305_aead(xchacha20), -// ); - -/** - * Chacha20 CSPRNG (cryptographically secure pseudorandom number generator). - * It's best to limit usage to non-production, non-critical cases: for example, test-only. - * Compatible with libtomcrypt. It does not have a specification, so unclear how secure it is. - */ -export const rngChacha20: XorPRG = /* @__PURE__ */ createPRG( - chacha20orig, - 64, - 32, - 8, -); -/** - * Chacha20/8 CSPRNG (cryptographically secure pseudorandom number generator). - * It's best to limit usage to non-production, non-critical cases: for example, test-only. - * Faster than `rngChacha20`. - */ -export const rngChacha8: XorPRG = /* @__PURE__ */ createPRG( - chacha8, - 64, - 32, - 12, -); diff --git a/packages/chacha20poly1305/src/chacha/webcrypto.ts b/packages/chacha20poly1305/src/chacha/webcrypto.ts deleted file mode 100644 index 1330a40e3e..0000000000 --- a/packages/chacha20poly1305/src/chacha/webcrypto.ts +++ /dev/null @@ -1,230 +0,0 @@ -// deno-lint-ignore-file no-explicit-any -/** - * This file is based on noble-ciphers (https://github.com/paulmillr/noble-ciphers). - * - * noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) - * - * The original file is located at: - * https://github.com/paulmillr/noble-ciphers/blob/749cdf9cd07ebdd19e9b957d0f172f1045179695/src/webcrypto.ts - */ - -// /** -// * WebCrypto-based AES gcm/ctr/cbc, `managedNonce` and `randomBytes`. -// * We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+. -// * @module -// */ -// import { -// abytes, -// anumber, -// type AsyncCipher, -// type Cipher, -// concatBytes, -// } from "./utils.ts"; - -// function getWebcryptoSubtle(): SubtleCrypto { -// const cr = typeof globalThis !== "undefined" && (globalThis as any).crypto; -// if (cr && typeof cr.subtle === "object" && cr.subtle != null) { -// return cr.subtle; -// } -// throw new Error("crypto.subtle must be defined"); -// } - -/** - * Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. - * We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+. - */ -export function randomBytes(bytesLength = 32): Uint8Array { - const cr = typeof globalThis != null && (globalThis as any).crypto; - if (!cr || typeof cr.getRandomValues !== "function") { - throw new Error("crypto.getRandomValues must be defined"); - } - return cr.getRandomValues(new Uint8Array(bytesLength)); -} - -// type RemoveNonceInner = ((...args: T) => Ret) extends ( -// arg0: any, -// arg1: any, -// ...rest: infer R -// ) => any ? (key: Uint8Array, ...args: R) => Ret -// : never; -// -// type RemoveNonce any> = RemoveNonceInner< -// Parameters, -// ReturnType -// >; -// type CipherWithNonce = -// & (( -// key: Uint8Array, -// nonce: Uint8Array, -// ...args: any[] -// ) => Cipher | AsyncCipher) -// & { -// nonceLength: number; -// }; -// -// /** -// * Uses CSPRG for nonce, nonce injected in ciphertext. -// * For `encrypt`, a `nonceBytes`-length buffer is fetched from CSPRNG and -// * prepended to encrypted ciphertext. For `decrypt`, first `nonceBytes` of ciphertext -// * are treated as nonce. -// * -// * NOTE: Under the same key, using random nonces (e.g. `managedNonce`) with AES-GCM and ChaCha -// * should be limited to `2**23` (8M) messages to get a collision chance of `2**-50`. Stretching to * `2**32` (4B) messages, chance would become `2**-33` - still negligible, but creeping up. -// * @example -// * const gcm = managedNonce(aes.gcm); -// * const ciphr = gcm(key).encrypt(data); -// * const plain = gcm(key).decrypt(ciph); -// */ -// export function managedNonce(fn: T): RemoveNonce { -// const { nonceLength } = fn; -// anumber(nonceLength); -// const addNonce = (nonce: Uint8Array, ciphertext: Uint8Array) => { -// const out = concatBytes(nonce, ciphertext); -// ciphertext.fill(0); -// return out; -// }; -// // NOTE: we cannot support DST here, it would be mistake: -// // - we don't know how much dst length cipher requires -// // - nonce may unalign dst and break everything -// // - we create new u8a anyway (concatBytes) -// // - previously we passed all args to cipher, but that was mistake! -// return ((key: Uint8Array, ...args: any[]): any => ({ -// encrypt(plaintext: Uint8Array) { -// abytes(plaintext); -// const nonce = randomBytes(nonceLength); -// const encrypted = fn(key, nonce, ...args).encrypt(plaintext); -// // @ts-ignore: to use encrypted -// if (encrypted instanceof Promise) { -// return encrypted.then((ct) => addNonce(nonce, ct)); -// } -// return addNonce(nonce, encrypted); -// }, -// decrypt(ciphertext: Uint8Array) { -// abytes(ciphertext); -// const nonce = ciphertext.subarray(0, nonceLength); -// const decrypted = ciphertext.subarray(nonceLength); -// return fn(key, nonce, ...args).decrypt(decrypted); -// }, -// })) as RemoveNonce; -// } -// -// /** -// * Internal webcrypto utils. Can be overridden of crypto.subtle is not present, -// * for example in React Native. -// */ -// export const utils: { -// encrypt: (key: Uint8Array, ...all: any[]) => Promise; -// decrypt: (key: Uint8Array, ...all: any[]) => Promise; -// } = { -// async encrypt( -// key: Uint8Array, -// keyParams: any, -// cryptParams: any, -// plaintext: Uint8Array, -// ): Promise { -// const cr = getWebcryptoSubtle(); -// const iKey = await cr.importKey("raw", key, keyParams, true, ["encrypt"]); -// const ciphertext = await cr.encrypt(cryptParams, iKey, plaintext); -// return new Uint8Array(ciphertext); -// }, -// async decrypt( -// key: Uint8Array, -// keyParams: any, -// cryptParams: any, -// ciphertext: Uint8Array, -// ): Promise { -// const cr = getWebcryptoSubtle(); -// const iKey = await cr.importKey("raw", key, keyParams, true, ["decrypt"]); -// const plaintext = await cr.decrypt(cryptParams, iKey, ciphertext); -// return new Uint8Array(plaintext); -// }, -// }; -// -// const mode = { -// CBC: "AES-CBC", -// CTR: "AES-CTR", -// GCM: "AES-GCM", -// } as const; -// type BlockMode = (typeof mode)[keyof typeof mode]; -// -// function getCryptParams(algo: BlockMode, nonce: Uint8Array, AAD?: Uint8Array) { -// if (algo === mode.CBC) return { name: mode.CBC, iv: nonce }; -// if (algo === mode.CTR) return { name: mode.CTR, counter: nonce, length: 64 }; -// if (algo === mode.GCM) { -// if (AAD) return { name: mode.GCM, iv: nonce, additionalData: AAD }; -// else return { name: mode.GCM, iv: nonce }; -// } -// -// throw new Error("unknown aes block mode"); -// } -// -// function generate(algo: BlockMode, nonceLength: number) { -// anumber(nonceLength); -// const res = ( -// key: Uint8Array, -// nonce: Uint8Array, -// AAD?: Uint8Array, -// ): AsyncCipher => { -// abytes(key); -// abytes(nonce); -// const keyParams = { name: algo, length: key.length * 8 }; -// const cryptParams = getCryptParams(algo, nonce, AAD); -// let consumed = false; -// return { -// // keyLength, -// encrypt(plaintext: Uint8Array) { -// abytes(plaintext); -// if (consumed) { -// throw new Error("Cannot encrypt() twice with same key / nonce"); -// } -// consumed = true; -// return utils.encrypt(key, keyParams, cryptParams, plaintext); -// }, -// decrypt(ciphertext: Uint8Array) { -// abytes(ciphertext); -// return utils.decrypt(key, keyParams, cryptParams, ciphertext); -// }, -// }; -// }; -// res.nonceLength = nonceLength; -// res.blockSize = 16; // always for AES -// return res; -// } -// -// /** AES-CBC, native webcrypto version */ -// export const cbc: ((key: Uint8Array, iv: Uint8Array) => AsyncCipher) & { -// blockSize: number; -// nonceLength: number; -// } = /* @__PURE__ */ (() => generate(mode.CBC, 16))(); -// /** AES-CTR, native webcrypto version */ -// export const ctr: ((key: Uint8Array, nonce: Uint8Array) => AsyncCipher) & { -// blockSize: number; -// nonceLength: number; -// } = /* @__PURE__ */ (() => generate(mode.CTR, 16))(); -// /** AES-GCM, native webcrypto version */ -// export const gcm: -// & ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => AsyncCipher) -// & { -// blockSize: number; -// nonceLength: number; -// } = /* @__PURE__ */ (() => generate(mode.GCM, 12))(); - -// // Type tests -// import { siv, gcm, ctr, ecb, cbc } from '../aes.ts'; -// import { xsalsa20poly1305 } from '../salsa.ts'; -// import { chacha20poly1305, xchacha20poly1305 } from '../chacha.ts'; - -// const wsiv = managedNonce(siv); -// const wgcm = managedNonce(gcm); -// const wctr = managedNonce(ctr); -// const wcbc = managedNonce(cbc); -// const wsalsapoly = managedNonce(xsalsa20poly1305); -// const wchacha = managedNonce(chacha20poly1305); -// const wxchacha = managedNonce(xchacha20poly1305); - -// // should fail -// const wcbc2 = managedNonce(managedNonce(cbc)); -// const wctr = managedNonce(ctr); -// import { gcm as gcmSync } from '../aes.ts'; -// const x1 = managedNonce(gcmSync); // const x1: (key: Uint8Array, AAD?: Uint8Array | undefined) => Cipher -// const x2 = managedNonce(gcm); // const x2: (key: Uint8Array, AAD?: Uint8Array | undefined) => AsyncCipher