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