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
+125
View File
@@ -0,0 +1,125 @@
import { AsyncCipher, 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: Uint8Array, key: Uint8Array): Promise<Uint8Array> {
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: Uint8Array, b: Uint8Array): boolean {
// 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 &&= false;
return flag;
}
// Wrap position so it will be in padded to blockSize
const wrapPos = (pos: number, blockSize: number) => Math.ceil(pos / blockSize) * blockSize;
const limit = (name: string, min: number, max: number) => (value: number) => {
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: Uint8Array, tag: Uint8Array, input: Uint8Array) {
// 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: Uint8Array, nonce: Uint8Array) {
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: Uint8Array,
nonce: Uint8Array,
AAD: Uint8Array
): Promise<AsyncCipher> {
const { encKey, authKey } = await deriveKeys(key, nonce);
const computeTag = async (data: Uint8Array, AAD: Uint8Array) => {
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: Uint8Array) => {
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: Uint8Array) => {
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;
},
};
}