include node_modules so release .zip is deployable

This commit is contained in:
2023-11-24 17:44:25 -05:00
parent 6c86cfe5d2
commit 8b11c41267
8963 changed files with 874175 additions and 1 deletions
+37
View File
@@ -0,0 +1,37 @@
function number(n) {
if (!Number.isSafeInteger(n) || n < 0)
throw new Error(`Wrong positive integer: ${n}`);
}
function bool(b) {
if (typeof b !== 'boolean')
throw new Error(`Expected boolean, not ${b}`);
}
function bytes(b, ...lengths) {
if (!(b instanceof Uint8Array))
throw new Error('Expected Uint8Array');
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
}
function hash(hash) {
if (typeof hash !== 'function' || typeof hash.create !== 'function')
throw new Error('hash must be wrapped by utils.wrapConstructor');
number(hash.outputLen);
number(hash.blockLen);
}
function exists(instance, checkFinished = true) {
if (instance.destroyed)
throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished)
throw new Error('Hash#digest() has already been called');
}
function output(out, instance) {
bytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
}
}
export { number, bool, bytes, hash, exists, output };
const assert = { number, bool, bytes, hash, exists, output };
export default assert;
//# sourceMappingURL=_assert.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"_assert.js","sourceRoot":"","sources":["../src/_assert.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,KAAK,CAAC,CAAyB,EAAE,GAAG,OAAiB;IAC5D,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3F,CAAC;AAQD,SAAS,IAAI,CAAC,IAAU;IACtB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;QACjE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,MAAM,CAAC,QAAa,EAAE,aAAa,GAAG,IAAI;IACjD,IAAI,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC5E,IAAI,aAAa,IAAI,QAAQ,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;AACnG,CAAC;AACD,SAAS,MAAM,CAAC,GAAQ,EAAE,QAAa;IACrC,KAAK,CAAC,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,EAAE,CAAC,CAAC;KACjF;AACH,CAAC;AAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7D,eAAe,MAAM,CAAC"}
+281
View File
@@ -0,0 +1,281 @@
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
// micro-noble-ciphers: more auditable, but slower version of salsa20, chacha & poly1305.
// Implements the same algorithms that are present in other files,
// but without unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
import * as u from './utils.js';
import { salsaBasic } from './_salsa.js';
// Utils
function hexToNumber(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
// Big Endian
return BigInt(hex === '' ? '0' : `0x${hex}`);
}
function bytesToNumberLE(bytes) {
return hexToNumber(u.bytesToHex(Uint8Array.from(bytes).reverse()));
}
function numberToBytesLE(n, len) {
return u.hexToBytes(n.toString(16).padStart(len * 2, '0')).reverse();
}
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
// /Utils
function salsaQR(x, a, b, c, d) {
x[b] ^= rotl((x[a] + x[d]) | 0, 7);
x[c] ^= rotl((x[b] + x[a]) | 0, 9);
x[d] ^= rotl((x[c] + x[b]) | 0, 13);
x[a] ^= rotl((x[d] + x[c]) | 0, 18);
}
// prettier-ignore
function chachaQR(x, a, b, c, d) {
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);
}
function salsaRound(x, rounds = 20) {
for (let i = 0; i < rounds; i += 2) {
salsaQR(x, 0, 4, 8, 12);
salsaQR(x, 5, 9, 13, 1);
salsaQR(x, 10, 14, 2, 6);
salsaQR(x, 15, 3, 7, 11);
salsaQR(x, 0, 1, 2, 3);
salsaQR(x, 5, 6, 7, 4);
salsaQR(x, 10, 11, 8, 9);
salsaQR(x, 15, 12, 13, 14);
}
}
function chachaRound(x, rounds = 20) {
for (let i = 0; i < rounds; i += 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);
}
}
function salsaCore(c, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
c[0], k[0], k[1], k[2],
k[3], c[1], n[0], n[1],
cnt, 0, c[2], k[4],
k[5], k[6], k[7], c[3], // Key Key Key "te k"
]);
const x = y.slice();
salsaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
export function hsalsa(c, key, nonce) {
const k = u.u32(key);
const i = u.u32(nonce);
// prettier-ignore
const x = new Uint32Array([
c[0], k[0], k[1], k[2],
k[3], c[1], i[0], i[1],
i[2], i[3], c[2], k[4],
k[5], k[6], k[7], c[3]
]);
salsaRound(x);
return u.u8(new Uint32Array([x[0], x[5], x[10], x[15], x[6], x[7], x[8], x[9]]));
}
function chachaCore(c, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
c[0], c[1], c[2], c[3],
k[0], k[1], k[2], k[3],
k[4], k[5], k[6], k[7],
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
]);
const x = y.slice();
chachaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
export function hchacha(c, key, nonce) {
const k = u.u32(key);
const i = u.u32(nonce);
// prettier-ignore
const x = new Uint32Array([
c[0], c[1], c[2], c[3],
k[0], k[1], k[2], k[3],
k[4], k[5], k[6], k[7],
i[0], i[1], i[2], i[3],
]);
chachaRound(x);
return u.u8(new Uint32Array([x[0], x[1], x[2], x[3], x[12], x[13], x[14], x[15]]));
}
/**
* salsa20, 12-byte nonce.
*/
export const salsa20 = salsaBasic({ core: salsaCore, counterRight: true });
/**
* xsalsa20, 24-byte nonce.
*/
export const xsalsa20 = salsaBasic({
core: salsaCore,
counterRight: true,
extendNonceFn: hsalsa,
allow128bitKeys: false,
});
/**
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = salsaBasic({ core: chachaCore, counterRight: false, counterLen: 8 });
/**
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
*/
export const chacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
allow128bitKeys: false,
});
/**
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export const xchacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 8,
extendNonceFn: hchacha,
allow128bitKeys: false,
});
/**
* 8-round chacha from the original paper.
*/
export const chacha8 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 8,
});
/**
* 12-round chacha from the original paper.
*/
export const chacha12 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 12,
});
const POW_2_130_5 = 2n ** 130n - 5n;
const POW_2_128_1 = 2n ** (16n * 8n) - 1n;
// Can be speed-up using BigUint64Array, but would be more complicated
export function poly1305(msg, key) {
u.ensureBytes(msg);
u.ensureBytes(key);
let acc = 0n;
const r = bytesToNumberLE(key.subarray(0, 16)) & 0x0ffffffc0ffffffc0ffffffc0fffffffn;
const s = bytesToNumberLE(key.subarray(16));
// Process by 16 byte chunks
for (let i = 0; i < msg.length; i += 16) {
const m = msg.subarray(i, i + 16);
const n = bytesToNumberLE(m) | (1n << BigInt(8 * m.length));
acc = ((acc + n) * r) % POW_2_130_5;
}
const res = (acc + s) & POW_2_128_1;
return numberToBytesLE(res, 16);
}
function computeTag(fn, key, nonce, ciphertext, AAD) {
const res = [];
if (AAD) {
res.push(AAD);
const leftover = AAD.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
}
res.push(ciphertext);
const leftover = ciphertext.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
// Lengths
const num = new Uint8Array(16);
const view = u.createView(num);
u.setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
u.setBigUint64(view, 8, BigInt(ciphertext.length), true);
res.push(num);
const authKey = fn(key, nonce, new Uint8Array(32));
return poly1305(u.concatBytes(...res), authKey);
}
/**
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
*/
export function xsalsa20poly1305(key, nonce) {
u.ensureBytes(key);
u.ensureBytes(nonce);
return {
encrypt: (plaintext) => {
u.ensureBytes(plaintext);
const m = u.concatBytes(new Uint8Array(32), plaintext);
const c = xsalsa20(key, nonce, m);
const authKey = c.subarray(0, 32);
const data = c.subarray(32);
const tag = poly1305(data, authKey);
return u.concatBytes(tag, data);
},
decrypt: (ciphertext) => {
u.ensureBytes(ciphertext);
if (ciphertext.length < 16)
throw new Error('encrypted data must be at least 16 bytes');
const c = u.concatBytes(new Uint8Array(16), ciphertext);
const authKey = xsalsa20(key, nonce, new Uint8Array(32));
const tag = poly1305(c.subarray(32), authKey);
if (!u.equalBytes(c.subarray(16, 32), tag))
throw new Error('invalid poly1305 tag');
return xsalsa20(key, nonce, c).subarray(32);
},
};
}
/**
* Alias to xsalsa20-poly1305
*/
export function secretbox(key, nonce) {
u.ensureBytes(key);
u.ensureBytes(nonce);
const xs = xsalsa20poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}
export const _poly1305_aead = (fn) => (key, nonce, AAD) => {
const tagLength = 16;
const keyLength = 32;
u.ensureBytes(key, keyLength);
u.ensureBytes(nonce);
return {
tagLength,
encrypt: (plaintext) => {
u.ensureBytes(plaintext);
const res = fn(key, nonce, plaintext, undefined, 1);
const tag = computeTag(fn, key, nonce, res, AAD);
return u.concatBytes(res, tag);
},
decrypt: (ciphertext) => {
u.ensureBytes(ciphertext);
if (ciphertext.length < tagLength)
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
const passedTag = ciphertext.subarray(-tagLength);
const data = ciphertext.subarray(0, -tagLength);
const tag = computeTag(fn, key, nonce, data, AAD);
if (!u.equalBytes(passedTag, tag))
throw new Error('invalid poly1305 tag');
return fn(key, nonce, data, undefined, 1);
},
};
};
/**
* chacha20-poly1305 12-byte-nonce chacha.
*/
export const chacha20poly1305 = _poly1305_aead(chacha20);
/**
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xchacha20poly1305 = _poly1305_aead(xchacha20);
//# sourceMappingURL=_micro.js.map
File diff suppressed because one or more lines are too long
+264
View File
@@ -0,0 +1,264 @@
import { toBytes, ensureBytes } from './utils.js';
import assert from './_assert.js';
// Poly1305 is a fast and parallel secret-key message-authentication code.
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
// https://datatracker.ietf.org/doc/html/rfc8439
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
class Poly1305 {
constructor(key) {
this.blockLen = 16;
this.outputLen = 16;
this.buffer = new Uint8Array(16);
this.r = new Uint16Array(10);
this.h = new Uint16Array(10);
this.pad = new Uint16Array(8);
this.pos = 0;
this.finished = false;
key = toBytes(key);
ensureBytes(key, 32);
const t0 = u8to16(key, 0);
const t1 = u8to16(key, 2);
const t2 = u8to16(key, 4);
const t3 = u8to16(key, 6);
const t4 = u8to16(key, 8);
const t5 = u8to16(key, 10);
const t6 = u8to16(key, 12);
const t7 = u8to16(key, 14);
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
this.r[0] = t0 & 0x1fff;
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
this.r[5] = (t4 >>> 1) & 0x1ffe;
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
this.r[9] = (t7 >>> 5) & 0x007f;
for (let i = 0; i < 8; i++)
this.pad[i] = u8to16(key, 16 + 2 * i);
}
process(data, offset, isLast = false) {
const hibit = isLast ? 0 : 1 << 11;
const { h, r } = this;
const r0 = r[0];
const r1 = r[1];
const r2 = r[2];
const r3 = r[3];
const r4 = r[4];
const r5 = r[5];
const r6 = r[6];
const r7 = r[7];
const r8 = r[8];
const r9 = r[9];
const t0 = u8to16(data, offset + 0);
const t1 = u8to16(data, offset + 2);
const t2 = u8to16(data, offset + 4);
const t3 = u8to16(data, offset + 6);
const t4 = u8to16(data, offset + 8);
const t5 = u8to16(data, offset + 10);
const t6 = u8to16(data, offset + 12);
const t7 = u8to16(data, offset + 14);
let h0 = h[0] + (t0 & 0x1fff);
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
let h9 = h[9] + ((t7 >>> 5) | hibit);
let c = 0;
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
c = d0 >>> 13;
d0 &= 0x1fff;
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
c += d0 >>> 13;
d0 &= 0x1fff;
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
c = d1 >>> 13;
d1 &= 0x1fff;
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
c += d1 >>> 13;
d1 &= 0x1fff;
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
c = d2 >>> 13;
d2 &= 0x1fff;
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
c += d2 >>> 13;
d2 &= 0x1fff;
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
c = d3 >>> 13;
d3 &= 0x1fff;
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
c += d3 >>> 13;
d3 &= 0x1fff;
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
c = d4 >>> 13;
d4 &= 0x1fff;
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
c += d4 >>> 13;
d4 &= 0x1fff;
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
c = d5 >>> 13;
d5 &= 0x1fff;
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
c += d5 >>> 13;
d5 &= 0x1fff;
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
c = d6 >>> 13;
d6 &= 0x1fff;
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
c += d6 >>> 13;
d6 &= 0x1fff;
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
c = d7 >>> 13;
d7 &= 0x1fff;
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
c += d7 >>> 13;
d7 &= 0x1fff;
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
c = d8 >>> 13;
d8 &= 0x1fff;
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
c += d8 >>> 13;
d8 &= 0x1fff;
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
c = d9 >>> 13;
d9 &= 0x1fff;
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
c += d9 >>> 13;
d9 &= 0x1fff;
c = ((c << 2) + c) | 0;
c = (c + d0) | 0;
d0 = c & 0x1fff;
c = c >>> 13;
d1 += c;
h[0] = d0;
h[1] = d1;
h[2] = d2;
h[3] = d3;
h[4] = d4;
h[5] = d5;
h[6] = d6;
h[7] = d7;
h[8] = d8;
h[9] = d9;
}
finalize() {
const { h, pad } = this;
const g = new Uint16Array(10);
let c = h[1] >>> 13;
h[1] &= 0x1fff;
for (let i = 2; i < 10; i++) {
h[i] += c;
c = h[i] >>> 13;
h[i] &= 0x1fff;
}
h[0] += c * 5;
c = h[0] >>> 13;
h[0] &= 0x1fff;
h[1] += c;
c = h[1] >>> 13;
h[1] &= 0x1fff;
h[2] += c;
g[0] = h[0] + 5;
c = g[0] >>> 13;
g[0] &= 0x1fff;
for (let i = 1; i < 10; i++) {
g[i] = h[i] + c;
c = g[i] >>> 13;
g[i] &= 0x1fff;
}
g[9] -= 1 << 13;
let mask = (c ^ 1) - 1;
for (let i = 0; i < 10; i++)
g[i] &= mask;
mask = ~mask;
for (let i = 0; i < 10; i++)
h[i] = (h[i] & mask) | g[i];
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
let f = h[0] + pad[0];
h[0] = f & 0xffff;
for (let i = 1; i < 8; i++) {
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
h[i] = f & 0xffff;
}
}
update(data) {
assert.exists(this);
const { buffer, blockLen } = this;
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len;) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input
if (take === blockLen) {
for (; blockLen <= len - pos; pos += blockLen)
this.process(data, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(buffer, 0, false);
this.pos = 0;
}
}
return this;
}
destroy() {
this.h.fill(0);
this.r.fill(0);
this.buffer.fill(0);
this.pad.fill(0);
}
digestInto(out) {
assert.exists(this);
assert.output(out, this);
this.finished = true;
const { buffer, h } = this;
let { pos } = this;
if (pos) {
buffer[pos++] = 1;
// buffer.subarray(pos).fill(0);
for (; pos < 16; pos++)
buffer[pos] = 0;
this.process(buffer, 0, true);
}
this.finalize();
let opos = 0;
for (let i = 0; i < 8; i++) {
out[opos++] = h[i] >>> 0;
out[opos++] = h[i] >>> 8;
}
return out;
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
}
export function wrapConstructorWithKey(hashCons) {
const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
const tmp = hashCons(new Uint8Array(32));
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (key) => hashCons(key);
return hashC;
}
export const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
//# sourceMappingURL=_poly1305.js.map
File diff suppressed because one or more lines are too long
+100
View File
@@ -0,0 +1,100 @@
import { u8, u32, ensureBytes } from './utils.js';
// AES-SIV polyval, little-endian "mirror image" of AES-GCM GHash
// polynomial hash function. Defined in RFC 8452.
// Reverse bits in u32, constant-time, precompute will be faster, but non-constant time
function rev32(x) {
x = ((x & 1431655765) << 1) | ((x >>> 1) & 1431655765);
x = ((x & 858993459) << 2) | ((x >>> 2) & 858993459);
x = ((x & 252645135) << 4) | ((x >>> 4) & 252645135);
x = ((x & 16711935) << 8) | ((x >>> 8) & 16711935);
return (x << 16) | (x >>> 16);
}
// wrapped 32 bit multiplication
const wrapMul = (a, b) => Math.imul(a, b) >>> 0;
// https://timtaubert.de/blog/2017/06/verified-binary-multiplication-for-ghash/
function bmul32(x, y) {
const x0 = x & 286331153;
const x1 = x & 572662306;
const x2 = x & 1145324612;
const x3 = x & 2290649224;
const y0 = y & 286331153;
const y1 = y & 572662306;
const y2 = y & 1145324612;
const y3 = y & 2290649224;
let res = (wrapMul(x0, y0) ^ wrapMul(x1, y3) ^ wrapMul(x2, y2) ^ wrapMul(x3, y1)) & 286331153;
res |= (wrapMul(x0, y1) ^ wrapMul(x1, y0) ^ wrapMul(x2, y3) ^ wrapMul(x3, y2)) & 572662306;
res |= (wrapMul(x0, y2) ^ wrapMul(x1, y1) ^ wrapMul(x2, y0) ^ wrapMul(x3, y3)) & 1145324612;
res |= (wrapMul(x0, y3) ^ wrapMul(x1, y2) ^ wrapMul(x2, y1) ^ wrapMul(x3, y0)) & 2290649224;
return res >>> 0;
}
function mulPart(arr) {
const a = new Uint32Array(18);
a[0] = arr[0];
a[1] = arr[1];
a[2] = arr[2];
a[3] = arr[3];
a[4] = a[0] ^ a[1];
a[5] = a[2] ^ a[3];
a[6] = a[0] ^ a[2];
a[7] = a[1] ^ a[3];
a[8] = a[6] ^ a[7];
a[9] = rev32(arr[0]);
a[10] = rev32(arr[1]);
a[11] = rev32(arr[2]);
a[12] = rev32(arr[3]);
a[13] = a[9] ^ a[10];
a[14] = a[11] ^ a[12];
a[15] = a[9] ^ a[11];
a[16] = a[10] ^ a[12];
a[17] = a[15] ^ a[16];
return a;
}
export function polyval(h, data) {
ensureBytes(h);
ensureBytes(data);
const s = new Uint32Array(4);
// Precompute for multiplication
const a = mulPart(u32(h));
if (data.length % 16)
throw new Error('polyval: data must be padded to 16 bytes');
const data32 = u32(data);
for (let i = 0; i < data32.length; i += 4) {
// Xor
s[0] ^= data32[i + 0];
s[1] ^= data32[i + 1];
s[2] ^= data32[i + 2];
s[3] ^= data32[i + 3];
// Dot via Karatsuba multiplication, based on MIT-licensed
// https://bearssl.org/gitweb/?p=BearSSL;a=blob;f=src/hash/ghash_ctmul32.c;hb=4b6046412
const b = mulPart(s);
const c = new Uint32Array(18);
for (let i = 0; i < 18; i++)
c[i] = bmul32(a[i], b[i]);
c[4] ^= c[0] ^ c[1];
c[5] ^= c[2] ^ c[3];
c[8] ^= c[6] ^ c[7];
c[13] ^= c[9] ^ c[10];
c[14] ^= c[11] ^ c[12];
c[17] ^= c[15] ^ c[16];
const zw = new Uint32Array(8);
zw[0] = c[0];
zw[1] = c[4] ^ (rev32(c[9]) >>> 1);
zw[2] = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >>> 1);
zw[3] = c[4] ^ c[5] ^ c[8] ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >>> 1);
zw[4] = c[2] ^ c[1] ^ c[3] ^ c[7] ^ (rev32(c[13] ^ c[14] ^ c[17]) >>> 1);
zw[5] = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >>> 1);
zw[6] = c[3] ^ (rev32(c[14]) >>> 1);
zw[7] = rev32(c[12]) >>> 1;
for (let i = 0; i < 4; i++) {
const lw = zw[i];
zw[i + 4] ^= lw ^ (lw >>> 1) ^ (lw >>> 2) ^ (lw >>> 7);
zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
}
s[0] = zw[4];
s[1] = zw[5];
s[2] = zw[6];
s[3] = zw[7];
}
return u8(s);
}
//# sourceMappingURL=_polyval.js.map
File diff suppressed because one or more lines are too long
+154
View File
@@ -0,0 +1,154 @@
// Basic utils for salsa-like ciphers
// Check out _micro.ts for descriptive documentation.
import assert from './_assert.js';
import { u32, utf8ToBytes, checkOpts } from './utils.js';
/*
RFC8439 requires multi-step cipher stream, where
authKey starts with counter: 0, actual msg with counter: 1.
For this, we need a way to re-use nonce / counter:
const counter = new Uint8Array(4);
chacha(..., counter, ...); // counter is now 1
chacha(..., counter, ...); // counter is now 2
This is complicated:
- Original papers don't allow mutating counters
- Counter overflow is undefined: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
- 3rd-party library stablelib implementation uses an approach where you can provide
nonce and counter instead of just nonce - and it will re-use it
- We could have did something similar, but ChaCha has different counter position
(counter | nonce), which is not composable with XChaCha, because full counter
is (nonce16 | counter | nonce16). Stablelib doesn't support in-place counter for XChaCha.
- We could separate nonce & counter and provide separate API for counter re-use, but
there are different counter sizes depending on an algorithm.
- Salsa & ChaCha also differ in structures of key / sigma:
salsa: c0 | k(4) | c1 | nonce(2) | ctr(2) | c2 | k(4) | c4
chacha: c(4) | k(8) | ctr(1) | nonce(3)
chachaDJB: c(4) | k(8) | ctr(2) | nonce(2)
- Creating function such as `setSalsaState(key, nonce, sigma, data)` won't work,
because we can't re-use counter array
- 32-bit nonce is `2 ** 32 * 64` = 256GB with 32-bit counter
- JS does not allow UintArrays bigger than 4GB, so supporting 64-bit counters doesn't matter
Structure is as following:
key=16 -> sigma16, k=key|key
key=32 -> sigma32, k=key
nonces:
salsa20: 8 (8-byte counter)
chacha20djb: 8 (8-byte counter)
chacha20tls: 12 (4-byte counter)
xsalsa: 24 (16 -> hsalsa, 8 -> old nonce)
xchacha: 24 (16 -> hchacha, 8 -> old nonce)
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
Use the subkey and remaining 8 byte nonce with ChaCha20 as normal
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
*/
const sigma16 = utf8ToBytes('expand 16-byte k');
const sigma32 = utf8ToBytes('expand 32-byte k');
const sigma16_32 = u32(sigma16);
const sigma32_32 = u32(sigma32);
// Is byte array aligned to 4 byte offset (u32)?
const isAligned32 = (b) => !(b.byteOffset % 4);
export const salsaBasic = (opts) => {
const { core, rounds, counterRight, counterLen, allow128bitKeys, extendNonceFn, blockLen } = checkOpts({ rounds: 20, counterRight: false, counterLen: 8, allow128bitKeys: true, blockLen: 64 }, opts);
assert.number(counterLen);
assert.number(rounds);
assert.number(blockLen);
assert.bool(counterRight);
assert.bool(allow128bitKeys);
const blockLen32 = blockLen / 4;
if (blockLen % 4 !== 0)
throw new Error('Salsa/ChaCha: blockLen must be aligned to 4 bytes');
return (key, nonce, data, output, counter = 0) => {
assert.bytes(key);
assert.bytes(nonce);
assert.bytes(data);
if (!output)
output = new Uint8Array(data.length);
assert.bytes(output);
assert.number(counter);
// > new Uint32Array([2**32])
// Uint32Array(1) [ 0 ]
// > new Uint32Array([2**32-1])
// Uint32Array(1) [ 4294967295 ]
if (counter < 0 || counter >= 2 ** 32 - 1)
throw new Error('Salsa/ChaCha: counter overflow');
if (output.length < data.length) {
throw new Error(`Salsa/ChaCha: output (${output.length}) is shorter than data (${data.length})`);
}
const toClean = [];
let k, sigma;
// Handle 128 byte keys
if (key.length === 32) {
k = key;
sigma = sigma32_32;
}
else if (key.length === 16 && allow128bitKeys) {
k = new Uint8Array(32);
k.set(key);
k.set(key, 16);
sigma = sigma16_32;
toClean.push(k);
}
else
throw new Error(`Salsa/ChaCha: invalid 32-byte key, got length=${key.length}`);
// Handle extended nonce (HChaCha/HSalsa)
if (extendNonceFn) {
if (nonce.length <= 16)
throw new Error(`Salsa/ChaCha: extended nonce must be bigger than 16 bytes`);
k = extendNonceFn(sigma, k, nonce.subarray(0, 16), new Uint8Array(32));
toClean.push(k);
nonce = nonce.subarray(16);
}
// Handle nonce counter
const nonceLen = 16 - counterLen;
if (nonce.length !== nonceLen)
throw new Error(`Salsa/ChaCha: nonce must be ${nonceLen} or 16 bytes`);
// Pad counter when nonce is 64 bit
if (nonceLen !== 12) {
const nc = new Uint8Array(12);
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
toClean.push((nonce = nc));
}
// Counter positions
const block = new Uint8Array(blockLen);
// Cast to Uint32Array for speed
const b32 = u32(block);
const k32 = u32(k);
const n32 = u32(nonce);
// Make sure that buffers aligned to 4 bytes
const d32 = isAligned32(data) && u32(data);
const o32 = isAligned32(output) && u32(output);
toClean.push(b32);
const len = data.length;
for (let pos = 0, ctr = counter; pos < len; ctr++) {
core(sigma, k32, n32, b32, ctr, rounds);
if (ctr >= 2 ** 32 - 1)
throw new Error('Salsa/ChaCha: counter overflow');
const take = Math.min(blockLen, len - pos);
// full block && aligned to 4 bytes
if (take === blockLen && o32 && d32) {
const pos32 = pos / 4;
if (pos % 4 !== 0)
throw new Error('Salsa/ChaCha: invalid block position');
for (let j = 0; j < blockLen32; j++)
o32[pos32 + j] = d32[pos32 + j] ^ b32[j];
pos += blockLen;
continue;
}
for (let j = 0; j < take; j++)
output[pos + j] = data[pos + j] ^ block[j];
pos += take;
}
for (let i = 0; i < toClean.length; i++)
toClean[i].fill(0);
return output;
};
};
//# sourceMappingURL=_salsa.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"_salsa.js","sourceRoot":"","sources":["../src/_salsa.ts"],"names":[],"mappings":"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"}
+329
View File
@@ -0,0 +1,329 @@
import { createView, ensureBytes, equalBytes, setBigUint64, u32 } from './utils.js';
import { poly1305 } from './_poly1305.js';
import { salsaBasic } from './_salsa.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
// Left rotate for uint32
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
/**
* ChaCha core function.
*/
// prettier-ignore
function chachaCore(c, k, n, out, cnt, rounds = 20) {
let y00 = c[0], y01 = c[1], y02 = c[2], y03 = c[3]; // "expa" "nd 3" "2-by" "te k"
let y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3]; // Key Key Key Key
let y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7]; // Key Key Key Key
let 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;
// Main loop
for (let i = 0; i < rounds; i += 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);
}
// 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
export function hchacha(c, key, src, out) {
const k32 = u32(key);
const i32 = u32(src);
const o32 = u32(out);
let x00 = c[0], x01 = c[1], x02 = c[2], x03 = c[3];
let x04 = k32[0], x05 = k32[1], x06 = k32[2], x07 = k32[3];
let x08 = k32[4], x09 = k32[5], x10 = k32[6], x11 = k32[7];
let x12 = i32[0], x13 = i32[1], x14 = i32[2], x15 = i32[3];
for (let i = 0; i < 20; i += 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);
}
o32[0] = x00;
o32[1] = x01;
o32[2] = x02;
o32[3] = x03;
o32[4] = x12;
o32[5] = x13;
o32[6] = x14;
o32[7] = x15;
return out;
}
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = /* @__PURE__ */ salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 8,
});
/**
* 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.
*/
export const chacha20 = /* @__PURE__ */ salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
allow128bitKeys: 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
*/
export const xchacha20 = /* @__PURE__ */ salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 8,
extendNonceFn: hchacha,
allow128bitKeys: false,
});
/**
* Reduced 8-round chacha, described in original paper.
*/
export const chacha8 = /* @__PURE__ */ salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 8,
});
/**
* Reduced 12-round chacha, described in original paper.
*/
export const chacha12 = /* @__PURE__ */ salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 12,
});
const ZERO = /* @__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(ZERO.subarray(left));
};
const computeTag = (fn, key, nonce, data, AAD) => {
const authKey = fn(key, nonce, new Uint8Array(32));
const h = poly1305.create(authKey);
if (AAD)
updatePadded(h, AAD);
updatePadded(h, data);
const num = new Uint8Array(16);
const view = createView(num);
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
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.
*/
export const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
const tagLength = 16;
ensureBytes(key, 32);
ensureBytes(nonce);
return {
tagLength,
encrypt: (plaintext, output) => {
const plength = plaintext.length;
const clength = plength + tagLength;
if (output) {
ensureBytes(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) {
ensureBytes(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 (!equalBytes(passedTag, tag))
throw new Error('invalid tag');
xorStream(key, nonce, data, output, 1);
return output;
},
};
};
/**
* ChaCha20-Poly1305 from RFC 8439.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const chacha20poly1305 = /* @__PURE__ */ _poly1305_aead(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).
*/
export const xchacha20poly1305 = /* @__PURE__ */ _poly1305_aead(xchacha20);
//# sourceMappingURL=chacha.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
"use strict";
throw new Error('noble-ciphers have no entry-point: consult README for usage');
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC"}
+9
View File
@@ -0,0 +1,9 @@
{
"type": "module",
"browser": {
"node:crypto": false
},
"node": {
"./crypto": "./esm/cryptoNode.js"
}
}
+207
View File
@@ -0,0 +1,207 @@
import { ensureBytes, u32, equalBytes } from './utils.js';
import { salsaBasic } from './_salsa.js';
import { poly1305 } from './_poly1305.js';
// Salsa20 stream cipher was released in 2005.
// Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
// which are hard to implement in a constant-time manner.
// https://cr.yp.to/snuffle.html, https://cr.yp.to/snuffle/salsafamily-20071225.pdf
// Left rotate for uint32
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
/**
* Salsa20 core function.
*/
// prettier-ignore
function salsaCore(c, k, i, out, cnt, rounds = 20) {
// Based on https://cr.yp.to/salsa20.html
let y00 = c[0], y01 = k[0], y02 = k[1], y03 = k[2]; // "expa" Key Key Key
let y04 = k[3], y05 = c[1], y06 = i[0], y07 = i[1]; // Key "nd 3" Nonce Nonce
let y08 = cnt, y09 = 0, y10 = c[2], y11 = k[4]; // Pos. Pos. "2-by" Key
let y12 = k[5], y13 = k[6], y14 = k[7], y15 = c[3]; // Key Key Key "te k"
// 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;
// Main loop
for (let i = 0; i < rounds; i += 2) {
x04 ^= rotl(x00 + x12 | 0, 7);
x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13);
x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7);
x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13);
x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7);
x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13);
x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7);
x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13);
x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7);
x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13);
x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7);
x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13);
x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7);
x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13);
x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7);
x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13);
x15 ^= rotl(x14 + x13 | 0, 18);
}
// 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;
}
/**
* hsalsa hashing function, used primarily in xsalsa, to hash
* key and nonce into key' and nonce'.
* Same as salsaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
export function hsalsa(c, key, nonce, out) {
const k32 = u32(key);
const i32 = u32(nonce);
const o32 = u32(out);
let x00 = c[0], x01 = k32[0], x02 = k32[1], x03 = k32[2], x04 = k32[3];
let x05 = c[1], x06 = i32[0], x07 = i32[1], x08 = i32[2], x09 = i32[3];
let x10 = c[2], x11 = k32[4], x12 = k32[5], x13 = k32[6], x14 = k32[7];
let x15 = c[3];
// Main loop
for (let i = 0; i < 20; i += 2) {
x04 ^= rotl(x00 + x12 | 0, 7);
x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13);
x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7);
x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13);
x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7);
x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13);
x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7);
x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13);
x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7);
x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13);
x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7);
x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13);
x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7);
x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13);
x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7);
x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13);
x15 ^= rotl(x14 + x13 | 0, 18);
}
o32[0] = x00;
o32[1] = x05;
o32[2] = x10;
o32[3] = x15;
o32[4] = x06;
o32[5] = x07;
o32[6] = x08;
o32[7] = x09;
return out;
}
/**
* Salsa20 from original paper.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const salsa20 = /* @__PURE__ */ salsaBasic({ core: salsaCore, counterRight: true });
/**
* xsalsa20 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xsalsa20 = /* @__PURE__ */ salsaBasic({
core: salsaCore,
counterRight: true,
extendNonceFn: hsalsa,
allow128bitKeys: false,
});
/**
* xsalsa20-poly1305 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* Also known as secretbox from libsodium / nacl.
*/
export const xsalsa20poly1305 = (key, nonce) => {
const tagLength = 16;
ensureBytes(key, 32);
ensureBytes(nonce, 24);
return {
tagLength,
encrypt: (plaintext, output) => {
ensureBytes(plaintext);
// This is small optimization (calculate auth key with same call as encryption itself) makes it hard
// to separate tag calculation and encryption itself, since 32 byte is half-block of salsa (64 byte)
const clength = plaintext.length + 32;
if (output) {
ensureBytes(output, clength);
}
else {
output = new Uint8Array(clength);
}
output.set(plaintext, 32);
xsalsa20(key, nonce, output, output);
const authKey = output.subarray(0, 32);
const tag = poly1305(output.subarray(32), authKey);
// Clean auth key, even though JS provides no guarantees about memory cleaning
output.set(tag, tagLength);
output.subarray(0, tagLength).fill(0);
return output.subarray(tagLength);
},
decrypt: (ciphertext) => {
ensureBytes(ciphertext);
const clength = ciphertext.length;
if (clength < tagLength)
throw new Error('encrypted data should be at least 16 bytes');
// Create new ciphertext array:
// auth tag auth tag from ciphertext ciphertext
// [bytes 0..16] [bytes 16..32] [bytes 32..]
// 16 instead of 32, because we already have 16 byte tag
const ciphertext_ = new Uint8Array(clength + tagLength); // alloc
ciphertext_.set(ciphertext, tagLength);
// Each xsalsa20 calls to hsalsa to calculate key, but seems not much perf difference
// Separate call to calculate authkey, since first bytes contains tag
const authKey = xsalsa20(key, nonce, new Uint8Array(32)); // alloc(32)
const tag = poly1305(ciphertext_.subarray(32), authKey);
if (!equalBytes(ciphertext_.subarray(16, 32), tag))
throw new Error('invalid tag');
const plaintext = xsalsa20(key, nonce, ciphertext_); // alloc
// Clean auth key, even though JS provides no guarantees about memory cleaning
plaintext.subarray(0, 32).fill(0);
authKey.fill(0);
return plaintext.subarray(32);
},
};
};
//# sourceMappingURL=salsa.js.map
+1
View File
File diff suppressed because one or more lines are too long
+58
View File
@@ -0,0 +1,58 @@
import { xchacha20poly1305 } from './chacha.js';
import { xsalsa20poly1305 } from './salsa.js';
import { concatBytes, ensureBytes, utf8ToBytes } from './utils.js';
import { aes_256_gcm } from './webcrypto/aes.js';
import { randomBytes } from './webcrypto/utils.js';
export { utf8ToBytes };
/**
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
*/
export function secretbox(key, nonce) {
ensureBytes(key);
ensureBytes(nonce);
const xs = xsalsa20poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}
export function randomKey() {
return randomBytes(32);
}
/**
* Encrypt plaintext under key with random nonce, using xchacha20poly1305.
* User never touches nonce: it is prepended to ciphertext.
*/
export function encrypt(key, plaintext) {
ensureBytes(key);
const nonce = randomBytes(24);
const ciphertext = xchacha20poly1305(key, nonce).encrypt(plaintext);
return concatBytes(nonce, ciphertext);
}
/**
* Decrypt plaintext under key with random nonce, using xchacha20poly1305.
* User never touches nonce: it is prepended to ciphertext.
*/
export function decrypt(key, ciphertext) {
const nonceLength = 24;
ensureBytes(ciphertext);
if (ciphertext.length <= nonceLength)
throw new Error('invalid ciphertext length');
const nonce = ciphertext.subarray(0, nonceLength);
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
return xchacha20poly1305(key, nonce).decrypt(ciphertextWithoutNonce);
}
export async function aes_encrypt(key, plaintext) {
const nonceLength = 12;
ensureBytes(key);
const nonce = randomBytes(nonceLength);
const ciphertext = await aes_256_gcm(key, nonce).encrypt(plaintext);
return concatBytes(nonce, ciphertext);
}
export async function aes_decrypt(key, ciphertext) {
const nonceLength = 12;
ensureBytes(ciphertext);
if (ciphertext.length <= nonceLength)
throw new Error('invalid ciphertext length');
const nonce = ciphertext.subarray(0, nonceLength);
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
return aes_256_gcm(key, nonce).decrypt(ciphertextWithoutNonce);
}
//# sourceMappingURL=simple.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"simple.js","sourceRoot":"","sources":["../src/simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAe,EAAE,KAAiB;IAC1D,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,WAAW,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC;AACD;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAe,EAAE,SAAqB;IAC5D,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAe,EAAE,UAAsB;IAC7D,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,SAAqB;IACtE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,UAAsB;IACvE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACjE,CAAC"}
+143
View File
@@ -0,0 +1,143 @@
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
const u8a = (a) => a instanceof Uint8Array;
// Cast array to different type
export const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
export const u16 = (arr) => new Uint16Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 2));
export const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
// Cast array to view
export const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
// early-throw an error because we don't support BE yet.
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
if (!isLE)
throw new Error('Non little-endian hardware is not supported');
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
/**
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export function bytesToHex(bytes) {
if (!u8a(bytes))
throw new Error('Uint8Array expected');
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
/**
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
export function hexToBytes(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
const len = hex.length;
if (len % 2)
throw new Error('padded hex string expected, got unpadded hex of length ' + len);
const array = new Uint8Array(len / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const hexByte = hex.slice(j, j + 2);
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte) || byte < 0)
throw new Error('Invalid byte sequence');
array[i] = byte;
}
return array;
}
// There is no setImmediate in browser and setTimeout is slow.
// call of async fn will return Promise, which will be fullfiled only on
// next scheduler queue processing step and this is exactly what we need.
export const nextTick = async () => { };
// Returns control to thread each 'tick' ms to avoid blocking
export async function asyncLoop(iters, tick, cb) {
let ts = Date.now();
for (let i = 0; i < iters; i++) {
cb(i);
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
const diff = Date.now() - ts;
if (diff >= 0 && diff < tick)
continue;
await nextTick();
ts += diff;
}
}
/**
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
*/
export function utf8ToBytes(str) {
if (typeof str !== 'string')
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
}
export function bytesToUtf8(bytes) {
return new TextDecoder().decode(bytes);
}
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/
export function toBytes(data) {
if (typeof data === 'string')
data = utf8ToBytes(data);
if (!u8a(data))
throw new Error(`expected Uint8Array, got ${typeof data}`);
return data;
}
/**
* Copies several Uint8Arrays into one.
*/
export function concatBytes(...arrays) {
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
let pad = 0; // walk through each item, ensure they have proper type
arrays.forEach((a) => {
if (!u8a(a))
throw new Error('Uint8Array expected');
r.set(a, pad);
pad += a.length;
});
return r;
}
// Check if object doens't have custom constructor (like Uint8Array/Array)
const isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
export function checkOpts(defaults, opts) {
if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts)))
throw new Error('options must be object or undefined');
const merged = Object.assign(defaults, opts);
return merged;
}
export function ensureBytes(b, len) {
if (!(b instanceof Uint8Array))
throw new Error('Uint8Array expected');
if (typeof len === 'number')
if (b.length !== len)
throw new Error(`Uint8Array length ${len} expected`);
}
// Constant-time equality
export function equalBytes(a, b) {
// Should not happen
if (a.length !== b.length)
throw new Error('equalBytes: Different size of Uint8Arrays');
let isSame = true;
for (let i = 0; i < a.length; i++)
isSame && (isSame = a[i] === b[i]); // Lets hope JIT won't optimize away.
return isSame;
}
// For runtime check if class implements interface
export class Hash {
}
// Polyfill for Safari 14
export function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === 'function')
return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
//# sourceMappingURL=utils.js.map
+1
View File
File diff suppressed because one or more lines are too long
+42
View File
@@ -0,0 +1,42 @@
import { ensureBytes } from '../utils.js';
import { getWebcryptoSubtle } from './utils.js';
function generate(algo, length) {
const keyLength = length / 8;
const keyParams = { name: algo, length };
const cryptParams = { name: algo };
// const params: Record<string, any> = ({ e: algo, i: { name: algo, length } });
return (key, nonce) => {
ensureBytes(key, keyLength);
if (algo === 'AES-CTR') {
cryptParams.counter = nonce;
cryptParams.length = 64;
}
else {
cryptParams.iv = nonce;
}
return {
keyLength,
async encrypt(plaintext) {
ensureBytes(plaintext);
const cr = getWebcryptoSubtle();
const iKey = await cr.importKey('raw', key, keyParams, true, ['encrypt']);
const cipher = await cr.encrypt(cryptParams, iKey, plaintext);
return new Uint8Array(cipher);
},
async decrypt(ciphertext) {
ensureBytes(ciphertext);
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);
},
};
};
}
export const aes_128_ctr = generate('AES-CTR', 128);
export const aes_256_ctr = generate('AES-CTR', 256);
export const aes_128_cbc = generate('AES-CBC', 128);
export const aes_256_cbc = generate('AES-CBC', 256);
export const aes_128_gcm = generate('AES-GCM', 128);
export const aes_256_gcm = generate('AES-GCM', 256);
//# sourceMappingURL=aes.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"aes.js","sourceRoot":"","sources":["../../src/webcrypto/aes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,MAAM,WAAW,GAAwB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,gFAAgF;IAEhF,OAAO,CAAC,GAAe,EAAE,KAAiB,EAAE,EAAE;QAC5C,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC;SACzB;aAAM;YACL,WAAW,CAAC,EAAE,GAAG,KAAK,CAAC;SACxB;QAED,OAAO;YACL,SAAS;YAET,KAAK,CAAC,OAAO,CAAC,SAAqB;gBACjC,WAAW,CAAC,SAAS,CAAC,CAAC;gBACvB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC9D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAED,KAAK,CAAC,OAAO,CAAC,UAAsB;gBAClC,WAAW,CAAC,UAAU,CAAC,CAAC;gBACxB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;gBAClE,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
export const crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
//# sourceMappingURL=crypto.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/webcrypto/crypto.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,MAAM,GACjB,OAAO,UAAU,KAAK,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC"}
+7
View File
@@ -0,0 +1,7 @@
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// See utils.ts for details.
// The file will throw on node.js 14 and earlier.
// @ts-ignore
import * as nc from 'node:crypto';
export const crypto = nc && typeof nc === 'object' && 'webcrypto' in nc ? nc.webcrypto : undefined;
//# sourceMappingURL=cryptoNode.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cryptoNode.js","sourceRoot":"","sources":["../../src/webcrypto/cryptoNode.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,4BAA4B;AAC5B,iDAAiD;AACjD,aAAa;AACb,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,MAAM,CAAC,MAAM,MAAM,GACjB,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,WAAW,IAAI,EAAE,CAAC,CAAC,CAAE,EAAE,CAAC,SAAiB,CAAC,CAAC,CAAC,SAAS,CAAC"}
+171
View File
@@ -0,0 +1,171 @@
import { getWebcryptoSubtle } from './utils.js';
// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
// Utils
function toBytesBE(num, length) {
let hex = num.toString(16);
hex = hex.length & 1 ? `0${hex}` : hex;
if (length)
hex = hex.padStart(length * 2, '00');
const len = hex.length / 2;
const u8 = new Uint8Array(len);
for (let j = 0, i = 0; i < hex.length && i < len * 2; i += 2, j++)
u8[j] = parseInt(hex[i] + hex[i + 1], 16);
return u8;
}
function fromBytesBE(bytes) {
let value = 0n;
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++)
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j));
return value;
}
function mod(a, b) {
const result = a % b;
return result >= 0 ? result : b + result;
}
// AES stuff
const BLOCK_LEN = 16;
const IV = new Uint8Array(BLOCK_LEN);
export async function encryptBlock(msg, key) {
if (key.length !== 16 && key.length !== 32)
throw new Error('Invalid key length');
const cr = getWebcryptoSubtle();
const mode = { name: `AES-CBC`, length: key.length * 8 };
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
return new Uint8Array(cipher).subarray(0, 16);
}
function NUMradix(radix, data) {
let res = 0n;
for (let i of data)
res = res * BigInt(radix) + BigInt(i);
return res;
}
async function getRound(radix, key, tweak, x) {
if (radix > 2 ** 16 - 1)
throw new Error(`Invalid radix: ${radix}`);
// radix**minlen ≥ 100
const minLen = Math.ceil(Math.log(100) / Math.log(radix));
const maxLen = 2 ** 32 - 1;
// 2 ≤ minlen ≤ maxlen < 2**32
if (2 > minLen || minLen > maxLen || maxLen >= 2 ** 32)
throw new Error('Invalid radix: 2 ≤ minlen ≤ maxlen < 2**32');
if (x.length < minLen || x.length > maxLen)
throw new Error('X is outside minLen..maxLen bounds');
const u = Math.floor(x.length / 2);
const v = x.length - u;
const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
const d = 4 * Math.ceil(b / 4) + 4;
const padding = mod(-tweak.length - b - 1, 16);
// P = [1]1 || [2]1 || [1]1 || [radix]3 || [10]1 || [u mod 256]1 || [n]4 || [t]4.
const P = new Uint8Array([1, 2, 1, 0, 0, 0, 10, u, 0, 0, 0, 0, 0, 0, 0, 0]);
const view = new DataView(P.buffer);
view.setUint16(4, radix, false);
view.setUint32(8, x.length, false);
view.setUint32(12, tweak.length, false);
// Q = T || [0](tb1) mod 16 || [i]1 || [NUMradix(B)]b.
const PQ = new Uint8Array(P.length + tweak.length + padding + 1 + b);
PQ.set(P);
P.fill(0);
PQ.set(tweak, P.length);
const round = async (A, B, i, decrypt = false) => {
// Q = ... || [i]1 || [NUMradix(B)]b.
PQ[PQ.length - b - 1] = i;
if (b)
PQ.set(toBytesBE(NUMradix(radix, B), b), PQ.length - b);
// PRF
let r = new Uint8Array(16);
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
for (let i = 0; i < BLOCK_LEN; i++)
r[i] ^= PQ[j * BLOCK_LEN + i];
r.set(await encryptBlock(r, key));
}
// Let S be the first d bytes of the following string of ⎡d/16⎤ blocks:
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ 1]16).
let s = Array.from(r);
for (let j = 1; s.length < d; j++) {
const block = toBytesBE(BigInt(j), 16);
for (let k = 0; k < BLOCK_LEN; k++)
block[k] ^= r[k];
s.push(...Array.from(await encryptBlock(block, key)));
}
let y = fromBytesBE(Uint8Array.from(s.slice(0, d)));
s.fill(0);
if (decrypt)
y = -y;
const m = i % 2 === 0 ? u : v;
let c = mod(NUMradix(radix, A) + y, BigInt(radix) ** BigInt(m));
// STR(radix, m, c)
const C = Array(m).fill(0);
for (let i = 0; i < m; i++, c /= BigInt(radix))
C[m - 1 - i] = Number(c % BigInt(radix));
A.fill(0);
A = B;
B = C;
return [A, B];
};
const destroy = () => PQ.fill(0);
return { u, round, destroy };
}
const EMPTY_BUF = new Uint8Array([]);
export function FF1(radix, key, tweak = EMPTY_BUF) {
const PQ = getRound.bind(null, radix, key, tweak);
return {
async encrypt(x) {
const { u, round, destroy } = await PQ(x);
let [A, B] = [x.slice(0, u), x.slice(u)];
for (let i = 0; i < 10; i++)
[A, B] = await round(A, B, i);
destroy();
const res = A.concat(B);
A.fill(0);
B.fill(0);
return res;
},
async decrypt(x) {
const { u, round, destroy } = await PQ(x);
// The FF1.Decrypt algorithm is similar to the FF1.Encrypt algorithm;
// the differences are in Step 6, where:
// 1) the order of the indices is reversed,
// 2) the roles of A and B are swapped
// 3) modular addition is replaced by modular subtraction, in Step 6vi.
let [B, A] = [x.slice(0, u), x.slice(u)];
for (let i = 9; i >= 0; i--)
[A, B] = await round(A, B, i, true);
destroy();
const res = B.concat(A);
A.fill(0);
B.fill(0);
return res;
},
};
}
// Binary string which encodes each byte in little-endian byte order
const binLE = {
encode(bytes) {
const x = [];
for (let i = 0; i < bytes.length; i++) {
for (let j = 0, tmp = bytes[i]; j < 8; j++, tmp >>= 1)
x.push(tmp & 1);
}
return x;
},
decode(b) {
if (b.length % 8)
throw new Error('Invalid binary string');
const res = new Uint8Array(b.length / 8);
for (let i = 0, j = 0; i < res.length; i++) {
res[i] = b[j++] | (b[j++] << 1) | (b[j++] << 2) | (b[j++] << 3);
res[i] |= (b[j++] << 4) | (b[j++] << 5) | (b[j++] << 6) | (b[j++] << 7);
}
return res;
},
};
export function BinaryFF1(key, tweak = EMPTY_BUF) {
const ff1 = FF1(2, key, tweak);
return {
encrypt: async (x) => binLE.decode(await ff1.encrypt(binLE.encode(x))),
decrypt: async (x) => binLE.decode(await ff1.decrypt(binLE.encode(x))),
};
}
//# sourceMappingURL=ff1.js.map
File diff suppressed because one or more lines are too long
+122
View File
@@ -0,0 +1,122 @@
import { createView, setBigUint64 } from '../utils.js';
import { polyval } from '../_polyval.js';
import { getWebcryptoSubtle } from './utils.js';
/**
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
*/
// AES stuff (same as ff1)
const BLOCK_LEN = 16;
const IV = new Uint8Array(BLOCK_LEN);
async function encryptBlock(msg, key) {
if (key.length !== 16 && key.length !== 32)
throw new Error('Invalid key length');
const mode = { name: `AES-CBC`, length: key.length * 8 };
const cr = getWebcryptoSubtle();
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
return new Uint8Array(cipher).subarray(0, 16);
}
// Kinda constant-time equality
function equalBytes(a, b) {
// Should not happen
if (a.length !== b.length)
throw new Error('equalBytes: Different size of Uint8Arrays');
let flag = true;
for (let i = 0; i < a.length; i++)
if (a[i] !== b[i])
flag && (flag = false);
return flag;
}
// Wrap position so it will be in padded to blockSize
const wrapPos = (pos, blockSize) => Math.ceil(pos / blockSize) * blockSize;
const limit = (name, min, max) => (value) => {
if (!Number.isSafeInteger(value) || min > value || value > max)
throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
};
// From RFC 8452: Section 6
const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
const PLAIN_LIMIT = limit('Plaintext', 0, 2 ** 36);
const NONCE_LIMIT = limit('Nonce', 12, 12);
const CIPHER_LIMIT = limit('Ciphertext', 16, 2 ** 36 + 16);
// nodejs api doesn't support 32bit counters, browser does
async function ctr(key, tag, input) {
// The initial counter block is the tag with the most significant bit of the last byte set to one.
let block = tag.slice();
block[15] |= 0x80;
let view = createView(block);
let output = new Uint8Array(input.length);
for (let pos = 0; pos < input.length;) {
const encryptedBlock = await encryptBlock(block, key);
view.setUint32(0, view.getUint32(0, true) + 1, true);
const take = Math.min(input.length, encryptedBlock.length);
for (let j = 0; j < take; j++, pos++)
output[pos] = encryptedBlock[j] ^ input[pos];
}
return new Uint8Array(output);
}
export async function deriveKeys(key, nonce) {
NONCE_LIMIT(nonce.length);
const len = key.length;
if (len !== 16 && len !== 32)
throw new Error(`key length must be 16 or 32 bytes, got: ${len} bytes`);
const encKey = new Uint8Array(len);
const authKey = new Uint8Array(16);
let counter = 0;
const deriveBlock = new Uint8Array(nonce.length + 4);
deriveBlock.set(nonce, 4);
const view = createView(deriveBlock);
for (const derivedKey of [authKey, encKey]) {
for (let i = 0; i < derivedKey.length; i += 8) {
view.setUint32(0, counter++, true);
const block = await encryptBlock(deriveBlock, key);
derivedKey.set(block.subarray(0, 8), i);
}
}
return { authKey, encKey };
}
export async function aes_256_gcm_siv(key, nonce, AAD) {
const { encKey, authKey } = await deriveKeys(key, nonce);
const computeTag = async (data, AAD) => {
const dataPos = wrapPos(AAD.length, 16);
const lenPos = wrapPos(dataPos + data.length, 16);
const block = new Uint8Array(lenPos + 16);
const view = createView(block);
block.set(AAD);
block.set(data, dataPos);
setBigUint64(view, lenPos, BigInt(AAD.length * 8), true);
setBigUint64(view, lenPos + 8, BigInt(data.length * 8), true);
// Compute the expected tag by XORing S_s and the nonce, clearing the
// most significant bit of the last byte and encrypting with the
// message-encryption key.
const tag = polyval(authKey, block);
for (let i = 0; i < 12; i++)
tag[i] ^= nonce[i];
// Clear the highest bit
tag[15] &= 0x7f;
return await encryptBlock(tag, encKey);
};
return {
// computeTag,
encrypt: async (plaintext) => {
AAD_LIMIT(AAD.length);
PLAIN_LIMIT(plaintext.length);
const tag = await computeTag(plaintext, AAD);
const out = new Uint8Array(plaintext.length + 16);
out.set(tag, plaintext.length);
out.set(await ctr(encKey, tag, plaintext));
return out;
},
decrypt: async (ciphertext) => {
CIPHER_LIMIT(ciphertext.length);
AAD_LIMIT(AAD.length);
const tag = ciphertext.subarray(-16);
const plaintext = await ctr(encKey, tag, ciphertext.subarray(0, -16));
const expectedTag = await computeTag(plaintext, AAD);
if (!equalBytes(tag, expectedTag))
throw new Error('invalid poly1305 tag');
return plaintext;
},
};
}
//# sourceMappingURL=siv.js.map
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// node.js versions earlier than v19 don't declare it in global scope.
// For node.js, package.js on#exports field mapping rewrites import
// from `crypto` to `cryptoNode`, which imports native module.
// Makes the utils un-importable in browsers without a bundler.
// Once node.js 18 is deprecated, we can just drop the import.
import { crypto } from '@noble/ciphers/webcrypto/crypto';
/**
* Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
*/
export function randomBytes(bytesLength = 32) {
if (crypto && typeof crypto.getRandomValues === 'function') {
return crypto.getRandomValues(new Uint8Array(bytesLength));
}
throw new Error('crypto.getRandomValues must be defined');
}
export function getWebcryptoSubtle() {
if (crypto && typeof crypto.subtle === 'object' && crypto.subtle != null) {
return crypto.subtle;
}
throw new Error('crypto.subtle must be defined');
}
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/webcrypto/utils.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,sEAAsE;AACtE,mEAAmE;AACnE,8DAA8D;AAC9D,+DAA+D;AAC/D,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,eAAe,KAAK,UAAU,EAAE;QAC1D,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;KAC5D;IACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE;QACxE,OAAO,MAAM,CAAC,MAAM,CAAC;KACtB;IACD,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC"}