UNPKG

avify-client

Version:
1,311 lines 461 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.string_to_bytes = exports.hex_to_bytes = exports.bytes_to_string = exports.bytes_to_hex = exports.bytes_to_base64 = exports.base64_to_bytes = exports.Sha512 = exports.Sha256 = exports.Sha1 = exports.SecurityError = exports.RSA_PSS = exports.RSA_PKCS1_v1_5 = exports.RSA_OAEP = exports.RSA = exports.Pbkdf2HmacSha512 = exports.Pbkdf2HmacSha256 = exports.Pbkdf2HmacSha1 = exports.Modulus = exports.IllegalStateError = exports.IllegalArgumentError = exports.HmacSha512 = exports.HmacSha256 = exports.HmacSha1 = exports.BigNumber = exports.AES_OFB = exports.AES_GCM = exports.AES_ECB = exports.AES_CTR = exports.AES_CMAC = exports.AES_CFB = exports.AES_CCM = exports.AES_CBC = void 0; const local_atob = typeof atob === 'undefined' ? (str) => Buffer.from(str, 'base64').toString('binary') : atob; const local_btoa = typeof btoa === 'undefined' ? (str) => Buffer.from(str, 'binary').toString('base64') : btoa; function string_to_bytes(str, utf8 = false) { var len = str.length, bytes = new Uint8Array(utf8 ? 4 * len : len); for (var i = 0, j = 0; i < len; i++) { var c = str.charCodeAt(i); if (utf8 && 0xd800 <= c && c <= 0xdbff) { if (++i >= len) throw new Error('Malformed string, low surrogate expected at position ' + i); c = ((c ^ 0xd800) << 10) | 0x10000 | (str.charCodeAt(i) ^ 0xdc00); } else if (!utf8 && c >>> 8) { throw new Error('Wide characters are not allowed.'); } if (!utf8 || c <= 0x7f) { bytes[j++] = c; } else if (c <= 0x7ff) { bytes[j++] = 0xc0 | (c >> 6); bytes[j++] = 0x80 | (c & 0x3f); } else if (c <= 0xffff) { bytes[j++] = 0xe0 | (c >> 12); bytes[j++] = 0x80 | ((c >> 6) & 0x3f); bytes[j++] = 0x80 | (c & 0x3f); } else { bytes[j++] = 0xf0 | (c >> 18); bytes[j++] = 0x80 | ((c >> 12) & 0x3f); bytes[j++] = 0x80 | ((c >> 6) & 0x3f); bytes[j++] = 0x80 | (c & 0x3f); } } return bytes.subarray(0, j); } exports.string_to_bytes = string_to_bytes; function hex_to_bytes(str) { var len = str.length; if (len & 1) { str = '0' + str; len++; } var bytes = new Uint8Array(len >> 1); for (var i = 0; i < len; i += 2) { bytes[i >> 1] = parseInt(str.substr(i, 2), 16); } return bytes; } exports.hex_to_bytes = hex_to_bytes; function base64_to_bytes(str) { return string_to_bytes(local_atob(str)); } exports.base64_to_bytes = base64_to_bytes; function bytes_to_string(bytes, utf8 = false) { var len = bytes.length, chars = new Array(len); for (var i = 0, j = 0; i < len; i++) { var b = bytes[i]; if (!utf8 || b < 128) { chars[j++] = b; } else if (b >= 192 && b < 224 && i + 1 < len) { chars[j++] = ((b & 0x1f) << 6) | (bytes[++i] & 0x3f); } else if (b >= 224 && b < 240 && i + 2 < len) { chars[j++] = ((b & 0xf) << 12) | ((bytes[++i] & 0x3f) << 6) | (bytes[++i] & 0x3f); } else if (b >= 240 && b < 248 && i + 3 < len) { var c = ((b & 7) << 18) | ((bytes[++i] & 0x3f) << 12) | ((bytes[++i] & 0x3f) << 6) | (bytes[++i] & 0x3f); if (c <= 0xffff) { chars[j++] = c; } else { c ^= 0x10000; chars[j++] = 0xd800 | (c >> 10); chars[j++] = 0xdc00 | (c & 0x3ff); } } else { throw new Error('Malformed UTF8 character at byte offset ' + i); } } var str = '', bs = 16384; for (var i = 0; i < j; i += bs) { str += String.fromCharCode.apply(String, chars.slice(i, i + bs <= j ? i + bs : j)); } return str; } exports.bytes_to_string = bytes_to_string; function bytes_to_hex(arr) { var str = ''; for (var i = 0; i < arr.length; i++) { var h = (arr[i] & 0xff).toString(16); if (h.length < 2) str += '0'; str += h; } return str; } exports.bytes_to_hex = bytes_to_hex; function bytes_to_base64(arr) { return local_btoa(bytes_to_string(arr)); } exports.bytes_to_base64 = bytes_to_base64; function is_bytes(a) { return a instanceof Uint8Array; } function _heap_init(heap, heapSize) { const size = heap ? heap.byteLength : heapSize || 65536; if (size & 0xfff || size <= 0) throw new Error('heap size must be a positive integer and a multiple of 4096'); heap = heap || new Uint8Array(new ArrayBuffer(size)); return heap; } function _heap_write(heap, hpos, data, dpos, dlen) { const hlen = heap.length - hpos; const wlen = hlen < dlen ? hlen : dlen; heap.set(data.subarray(dpos, dpos + wlen), hpos); return wlen; } function joinBytes(...arg) { const totalLenght = arg.reduce((sum, curr) => sum + curr.length, 0); const ret = new Uint8Array(totalLenght); let cursor = 0; for (let i = 0; i < arg.length; i++) { ret.set(arg[i], cursor); cursor += arg[i].length; } return ret; } class IllegalStateError extends Error { constructor(...args) { super(...args); } } exports.IllegalStateError = IllegalStateError; class IllegalArgumentError extends Error { constructor(...args) { super(...args); } } exports.IllegalArgumentError = IllegalArgumentError; class SecurityError extends Error { constructor(...args) { super(...args); } } exports.SecurityError = SecurityError; /** * @file {@link http://asmjs.org Asm.js} implementation of the {@link https://en.wikipedia.org/wiki/Advanced_Encryption_Standard Advanced Encryption Standard}. * @author Artem S Vybornov <vybornov@gmail.com> * @license MIT */ var AES_asm = function () { /** * Galois Field stuff init flag */ var ginit_done = false; /** * Galois Field exponentiation and logarithm tables for 3 (the generator) */ var gexp3, glog3; /** * Init Galois Field tables */ function ginit() { gexp3 = [], glog3 = []; var a = 1, c, d; for (c = 0; c < 255; c++) { gexp3[c] = a; // Multiply by three d = a & 0x80, a <<= 1, a &= 255; if (d === 0x80) a ^= 0x1b; a ^= gexp3[c]; // Set the log table value glog3[gexp3[c]] = c; } gexp3[255] = gexp3[0]; glog3[0] = 0; ginit_done = true; } /** * Galois Field multiplication * @param {number} a * @param {number} b * @return {number} */ function gmul(a, b) { var c = gexp3[(glog3[a] + glog3[b]) % 255]; if (a === 0 || b === 0) c = 0; return c; } /** * Galois Field reciprocal * @param {number} a * @return {number} */ function ginv(a) { var i = gexp3[255 - glog3[a]]; if (a === 0) i = 0; return i; } /** * AES stuff init flag */ var aes_init_done = false; /** * Encryption, Decryption, S-Box and KeyTransform tables * * @type {number[]} */ var aes_sbox; /** * @type {number[]} */ var aes_sinv; /** * @type {number[][]} */ var aes_enc; /** * @type {number[][]} */ var aes_dec; /** * Init AES tables */ function aes_init() { if (!ginit_done) ginit(); // Calculates AES S-Box value function _s(a) { var c, s, x; s = x = ginv(a); for (c = 0; c < 4; c++) { s = ((s << 1) | (s >>> 7)) & 255; x ^= s; } x ^= 99; return x; } // Tables aes_sbox = [], aes_sinv = [], aes_enc = [[], [], [], []], aes_dec = [[], [], [], []]; for (var i = 0; i < 256; i++) { var s = _s(i); // S-Box and its inverse aes_sbox[i] = s; aes_sinv[s] = i; // Ecryption and Decryption tables aes_enc[0][i] = (gmul(2, s) << 24) | (s << 16) | (s << 8) | gmul(3, s); aes_dec[0][s] = (gmul(14, i) << 24) | (gmul(9, i) << 16) | (gmul(13, i) << 8) | gmul(11, i); // Rotate tables for (var t = 1; t < 4; t++) { aes_enc[t][i] = (aes_enc[t - 1][i] >>> 8) | (aes_enc[t - 1][i] << 24); aes_dec[t][s] = (aes_dec[t - 1][s] >>> 8) | (aes_dec[t - 1][s] << 24); } } aes_init_done = true; } /** * Asm.js module constructor. * * <p> * Heap buffer layout by offset: * <pre> * 0x0000 encryption key schedule * 0x0400 decryption key schedule * 0x0800 sbox * 0x0c00 inv sbox * 0x1000 encryption tables * 0x2000 decryption tables * 0x3000 reserved (future GCM multiplication lookup table) * 0x4000 data * </pre> * Don't touch anything before <code>0x400</code>. * </p> * * @alias AES_asm * @class * @param foreign - <i>ignored</i> * @param buffer - heap buffer to link with */ var wrapper = function (foreign, buffer) { // Init AES stuff for the first time if (!aes_init_done) aes_init(); // Fill up AES tables var heap = new Uint32Array(buffer); heap.set(aes_sbox, 0x0800 >> 2); heap.set(aes_sinv, 0x0c00 >> 2); for (var i = 0; i < 4; i++) { heap.set(aes_enc[i], (0x1000 + 0x400 * i) >> 2); heap.set(aes_dec[i], (0x2000 + 0x400 * i) >> 2); } /** * Calculate AES key schedules. * @instance * @memberof AES_asm * @param {number} ks - key size, 4/6/8 (for 128/192/256-bit key correspondingly) * @param {number} k0 - key vector components * @param {number} k1 - key vector components * @param {number} k2 - key vector components * @param {number} k3 - key vector components * @param {number} k4 - key vector components * @param {number} k5 - key vector components * @param {number} k6 - key vector components * @param {number} k7 - key vector components */ function set_key(ks, k0, k1, k2, k3, k4, k5, k6, k7) { var ekeys = heap.subarray(0x000, 60), dkeys = heap.subarray(0x100, 0x100 + 60); // Encryption key schedule ekeys.set([k0, k1, k2, k3, k4, k5, k6, k7]); for (var i = ks, rcon = 1; i < 4 * ks + 28; i++) { var k = ekeys[i - 1]; if ((i % ks === 0) || (ks === 8 && i % ks === 4)) { k = aes_sbox[k >>> 24] << 24 ^ aes_sbox[k >>> 16 & 255] << 16 ^ aes_sbox[k >>> 8 & 255] << 8 ^ aes_sbox[k & 255]; } if (i % ks === 0) { k = (k << 8) ^ (k >>> 24) ^ (rcon << 24); rcon = (rcon << 1) ^ ((rcon & 0x80) ? 0x1b : 0); } ekeys[i] = ekeys[i - ks] ^ k; } // Decryption key schedule for (var j = 0; j < i; j += 4) { for (var jj = 0; jj < 4; jj++) { var k = ekeys[i - (4 + j) + (4 - jj) % 4]; if (j < 4 || j >= i - 4) { dkeys[j + jj] = k; } else { dkeys[j + jj] = aes_dec[0][aes_sbox[k >>> 24]] ^ aes_dec[1][aes_sbox[k >>> 16 & 255]] ^ aes_dec[2][aes_sbox[k >>> 8 & 255]] ^ aes_dec[3][aes_sbox[k & 255]]; } } } // Set rounds number asm.set_rounds(ks + 5); } // create library object with necessary properties var stdlib = { Uint8Array: Uint8Array, Uint32Array: Uint32Array }; var asm = function (stdlib, foreign, buffer) { "use asm"; var S0 = 0, S1 = 0, S2 = 0, S3 = 0, I0 = 0, I1 = 0, I2 = 0, I3 = 0, N0 = 0, N1 = 0, N2 = 0, N3 = 0, M0 = 0, M1 = 0, M2 = 0, M3 = 0, H0 = 0, H1 = 0, H2 = 0, H3 = 0, R = 0; var HEAP = new stdlib.Uint32Array(buffer), DATA = new stdlib.Uint8Array(buffer); /** * AES core * @param {number} k - precomputed key schedule offset * @param {number} s - precomputed sbox table offset * @param {number} t - precomputed round table offset * @param {number} r - number of inner rounds to perform * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _core(k, s, t, r, x0, x1, x2, x3) { k = k | 0; s = s | 0; t = t | 0; r = r | 0; x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; var t1 = 0, t2 = 0, t3 = 0, y0 = 0, y1 = 0, y2 = 0, y3 = 0, i = 0; t1 = t | 0x400, t2 = t | 0x800, t3 = t | 0xc00; // round 0 x0 = x0 ^ HEAP[(k | 0) >> 2], x1 = x1 ^ HEAP[(k | 4) >> 2], x2 = x2 ^ HEAP[(k | 8) >> 2], x3 = x3 ^ HEAP[(k | 12) >> 2]; // round 1..r for (i = 16; (i | 0) <= (r << 4); i = (i + 16) | 0) { y0 = HEAP[(t | x0 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x1 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x2 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x3 << 2 & 1020) >> 2] ^ HEAP[(k | i | 0) >> 2], y1 = HEAP[(t | x1 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x2 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x3 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x0 << 2 & 1020) >> 2] ^ HEAP[(k | i | 4) >> 2], y2 = HEAP[(t | x2 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x3 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x0 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x1 << 2 & 1020) >> 2] ^ HEAP[(k | i | 8) >> 2], y3 = HEAP[(t | x3 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x0 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x1 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x2 << 2 & 1020) >> 2] ^ HEAP[(k | i | 12) >> 2]; x0 = y0, x1 = y1, x2 = y2, x3 = y3; } // final round S0 = HEAP[(s | x0 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x1 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x2 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x3 << 2 & 1020) >> 2] ^ HEAP[(k | i | 0) >> 2], S1 = HEAP[(s | x1 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x2 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x3 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x0 << 2 & 1020) >> 2] ^ HEAP[(k | i | 4) >> 2], S2 = HEAP[(s | x2 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x3 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x0 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x1 << 2 & 1020) >> 2] ^ HEAP[(k | i | 8) >> 2], S3 = HEAP[(s | x3 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x0 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x1 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x2 << 2 & 1020) >> 2] ^ HEAP[(k | i | 12) >> 2]; } /** * ECB mode encryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _ecb_enc(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, x0, x1, x2, x3); } /** * ECB mode decryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _ecb_dec(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; var t = 0; _core(0x0400, 0x0c00, 0x2000, R, x0, x3, x2, x1); t = S1, S1 = S3, S3 = t; } /** * CBC mode encryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _cbc_enc(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, I0 ^ x0, I1 ^ x1, I2 ^ x2, I3 ^ x3); I0 = S0, I1 = S1, I2 = S2, I3 = S3; } /** * CBC mode decryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _cbc_dec(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; var t = 0; _core(0x0400, 0x0c00, 0x2000, R, x0, x3, x2, x1); t = S1, S1 = S3, S3 = t; S0 = S0 ^ I0, S1 = S1 ^ I1, S2 = S2 ^ I2, S3 = S3 ^ I3; I0 = x0, I1 = x1, I2 = x2, I3 = x3; } /** * CFB mode encryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _cfb_enc(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3); I0 = S0 = S0 ^ x0, I1 = S1 = S1 ^ x1, I2 = S2 = S2 ^ x2, I3 = S3 = S3 ^ x3; } /** * CFB mode decryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _cfb_dec(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3); S0 = S0 ^ x0, S1 = S1 ^ x1, S2 = S2 ^ x2, S3 = S3 ^ x3; I0 = x0, I1 = x1, I2 = x2, I3 = x3; } /** * OFB mode encryption / decryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _ofb(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3); I0 = S0, I1 = S1, I2 = S2, I3 = S3; S0 = S0 ^ x0, S1 = S1 ^ x1, S2 = S2 ^ x2, S3 = S3 ^ x3; } /** * CTR mode encryption / decryption * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _ctr(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; _core(0x0000, 0x0800, 0x1000, R, N0, N1, N2, N3); N3 = (~M3 & N3) | M3 & (N3 + 1); N2 = (~M2 & N2) | M2 & (N2 + ((N3 | 0) == 0)); N1 = (~M1 & N1) | M1 & (N1 + ((N2 | 0) == 0)); N0 = (~M0 & N0) | M0 & (N0 + ((N1 | 0) == 0)); S0 = S0 ^ x0; S1 = S1 ^ x1; S2 = S2 ^ x2; S3 = S3 ^ x3; } /** * GCM mode MAC calculation * @param {number} x0 - 128-bit input block vector * @param {number} x1 - 128-bit input block vector * @param {number} x2 - 128-bit input block vector * @param {number} x3 - 128-bit input block vector */ function _gcm_mac(x0, x1, x2, x3) { x0 = x0 | 0; x1 = x1 | 0; x2 = x2 | 0; x3 = x3 | 0; var y0 = 0, y1 = 0, y2 = 0, y3 = 0, z0 = 0, z1 = 0, z2 = 0, z3 = 0, i = 0, c = 0; x0 = x0 ^ I0, x1 = x1 ^ I1, x2 = x2 ^ I2, x3 = x3 ^ I3; y0 = H0 | 0, y1 = H1 | 0, y2 = H2 | 0, y3 = H3 | 0; for (; (i | 0) < 128; i = (i + 1) | 0) { if (y0 >>> 31) { z0 = z0 ^ x0, z1 = z1 ^ x1, z2 = z2 ^ x2, z3 = z3 ^ x3; } y0 = (y0 << 1) | (y1 >>> 31), y1 = (y1 << 1) | (y2 >>> 31), y2 = (y2 << 1) | (y3 >>> 31), y3 = (y3 << 1); c = x3 & 1; x3 = (x3 >>> 1) | (x2 << 31), x2 = (x2 >>> 1) | (x1 << 31), x1 = (x1 >>> 1) | (x0 << 31), x0 = (x0 >>> 1); if (c) x0 = x0 ^ 0xe1000000; } I0 = z0, I1 = z1, I2 = z2, I3 = z3; } /** * Set the internal rounds number. * @instance * @memberof AES_asm * @param {number} r - number if inner AES rounds */ function set_rounds(r) { r = r | 0; R = r; } /** * Populate the internal state of the module. * @instance * @memberof AES_asm * @param {number} s0 - state vector * @param {number} s1 - state vector * @param {number} s2 - state vector * @param {number} s3 - state vector */ function set_state(s0, s1, s2, s3) { s0 = s0 | 0; s1 = s1 | 0; s2 = s2 | 0; s3 = s3 | 0; S0 = s0, S1 = s1, S2 = s2, S3 = s3; } /** * Populate the internal iv of the module. * @instance * @memberof AES_asm * @param {number} i0 - iv vector * @param {number} i1 - iv vector * @param {number} i2 - iv vector * @param {number} i3 - iv vector */ function set_iv(i0, i1, i2, i3) { i0 = i0 | 0; i1 = i1 | 0; i2 = i2 | 0; i3 = i3 | 0; I0 = i0, I1 = i1, I2 = i2, I3 = i3; } /** * Set nonce for CTR-family modes. * @instance * @memberof AES_asm * @param {number} n0 - nonce vector * @param {number} n1 - nonce vector * @param {number} n2 - nonce vector * @param {number} n3 - nonce vector */ function set_nonce(n0, n1, n2, n3) { n0 = n0 | 0; n1 = n1 | 0; n2 = n2 | 0; n3 = n3 | 0; N0 = n0, N1 = n1, N2 = n2, N3 = n3; } /** * Set counter mask for CTR-family modes. * @instance * @memberof AES_asm * @param {number} m0 - counter mask vector * @param {number} m1 - counter mask vector * @param {number} m2 - counter mask vector * @param {number} m3 - counter mask vector */ function set_mask(m0, m1, m2, m3) { m0 = m0 | 0; m1 = m1 | 0; m2 = m2 | 0; m3 = m3 | 0; M0 = m0, M1 = m1, M2 = m2, M3 = m3; } /** * Set counter for CTR-family modes. * @instance * @memberof AES_asm * @param {number} c0 - counter vector * @param {number} c1 - counter vector * @param {number} c2 - counter vector * @param {number} c3 - counter vector */ function set_counter(c0, c1, c2, c3) { c0 = c0 | 0; c1 = c1 | 0; c2 = c2 | 0; c3 = c3 | 0; N3 = (~M3 & N3) | M3 & c3, N2 = (~M2 & N2) | M2 & c2, N1 = (~M1 & N1) | M1 & c1, N0 = (~M0 & N0) | M0 & c0; } /** * Store the internal state vector into the heap. * @instance * @memberof AES_asm * @param {number} pos - offset where to put the data * @return {number} The number of bytes have been written into the heap, always 16. */ function get_state(pos) { pos = pos | 0; if (pos & 15) return -1; DATA[pos | 0] = S0 >>> 24, DATA[pos | 1] = S0 >>> 16 & 255, DATA[pos | 2] = S0 >>> 8 & 255, DATA[pos | 3] = S0 & 255, DATA[pos | 4] = S1 >>> 24, DATA[pos | 5] = S1 >>> 16 & 255, DATA[pos | 6] = S1 >>> 8 & 255, DATA[pos | 7] = S1 & 255, DATA[pos | 8] = S2 >>> 24, DATA[pos | 9] = S2 >>> 16 & 255, DATA[pos | 10] = S2 >>> 8 & 255, DATA[pos | 11] = S2 & 255, DATA[pos | 12] = S3 >>> 24, DATA[pos | 13] = S3 >>> 16 & 255, DATA[pos | 14] = S3 >>> 8 & 255, DATA[pos | 15] = S3 & 255; return 16; } /** * Store the internal iv vector into the heap. * @instance * @memberof AES_asm * @param {number} pos - offset where to put the data * @return {number} The number of bytes have been written into the heap, always 16. */ function get_iv(pos) { pos = pos | 0; if (pos & 15) return -1; DATA[pos | 0] = I0 >>> 24, DATA[pos | 1] = I0 >>> 16 & 255, DATA[pos | 2] = I0 >>> 8 & 255, DATA[pos | 3] = I0 & 255, DATA[pos | 4] = I1 >>> 24, DATA[pos | 5] = I1 >>> 16 & 255, DATA[pos | 6] = I1 >>> 8 & 255, DATA[pos | 7] = I1 & 255, DATA[pos | 8] = I2 >>> 24, DATA[pos | 9] = I2 >>> 16 & 255, DATA[pos | 10] = I2 >>> 8 & 255, DATA[pos | 11] = I2 & 255, DATA[pos | 12] = I3 >>> 24, DATA[pos | 13] = I3 >>> 16 & 255, DATA[pos | 14] = I3 >>> 8 & 255, DATA[pos | 15] = I3 & 255; return 16; } /** * GCM initialization. * @instance * @memberof AES_asm */ function gcm_init() { _ecb_enc(0, 0, 0, 0); H0 = S0, H1 = S1, H2 = S2, H3 = S3; } /** * Perform ciphering operation on the supplied data. * @instance * @memberof AES_asm * @param {number} mode - block cipher mode (see {@link AES_asm} mode constants) * @param {number} pos - offset of the data being processed * @param {number} len - length of the data being processed * @return {number} Actual amount of data have been processed. */ function cipher(mode, pos, len) { mode = mode | 0; pos = pos | 0; len = len | 0; var ret = 0; if (pos & 15) return -1; while ((len | 0) >= 16) { _cipher_modes[mode & 7](DATA[pos | 0] << 24 | DATA[pos | 1] << 16 | DATA[pos | 2] << 8 | DATA[pos | 3], DATA[pos | 4] << 24 | DATA[pos | 5] << 16 | DATA[pos | 6] << 8 | DATA[pos | 7], DATA[pos | 8] << 24 | DATA[pos | 9] << 16 | DATA[pos | 10] << 8 | DATA[pos | 11], DATA[pos | 12] << 24 | DATA[pos | 13] << 16 | DATA[pos | 14] << 8 | DATA[pos | 15]); DATA[pos | 0] = S0 >>> 24, DATA[pos | 1] = S0 >>> 16 & 255, DATA[pos | 2] = S0 >>> 8 & 255, DATA[pos | 3] = S0 & 255, DATA[pos | 4] = S1 >>> 24, DATA[pos | 5] = S1 >>> 16 & 255, DATA[pos | 6] = S1 >>> 8 & 255, DATA[pos | 7] = S1 & 255, DATA[pos | 8] = S2 >>> 24, DATA[pos | 9] = S2 >>> 16 & 255, DATA[pos | 10] = S2 >>> 8 & 255, DATA[pos | 11] = S2 & 255, DATA[pos | 12] = S3 >>> 24, DATA[pos | 13] = S3 >>> 16 & 255, DATA[pos | 14] = S3 >>> 8 & 255, DATA[pos | 15] = S3 & 255; ret = (ret + 16) | 0, pos = (pos + 16) | 0, len = (len - 16) | 0; } return ret | 0; } /** * Calculates MAC of the supplied data. * @instance * @memberof AES_asm * @param {number} mode - block cipher mode (see {@link AES_asm} mode constants) * @param {number} pos - offset of the data being processed * @param {number} len - length of the data being processed * @return {number} Actual amount of data have been processed. */ function mac(mode, pos, len) { mode = mode | 0; pos = pos | 0; len = len | 0; var ret = 0; if (pos & 15) return -1; while ((len | 0) >= 16) { _mac_modes[mode & 1](DATA[pos | 0] << 24 | DATA[pos | 1] << 16 | DATA[pos | 2] << 8 | DATA[pos | 3], DATA[pos | 4] << 24 | DATA[pos | 5] << 16 | DATA[pos | 6] << 8 | DATA[pos | 7], DATA[pos | 8] << 24 | DATA[pos | 9] << 16 | DATA[pos | 10] << 8 | DATA[pos | 11], DATA[pos | 12] << 24 | DATA[pos | 13] << 16 | DATA[pos | 14] << 8 | DATA[pos | 15]); ret = (ret + 16) | 0, pos = (pos + 16) | 0, len = (len - 16) | 0; } return ret | 0; } /** * AES cipher modes table (virual methods) */ var _cipher_modes = [_ecb_enc, _ecb_dec, _cbc_enc, _cbc_dec, _cfb_enc, _cfb_dec, _ofb, _ctr]; /** * AES MAC modes table (virual methods) */ var _mac_modes = [_cbc_enc, _gcm_mac]; /** * Asm.js module exports */ return { set_rounds: set_rounds, set_state: set_state, set_iv: set_iv, set_nonce: set_nonce, set_mask: set_mask, set_counter: set_counter, get_state: get_state, get_iv: get_iv, gcm_init: gcm_init, cipher: cipher, mac: mac, }; }(stdlib, foreign, buffer); asm.set_key = set_key; return asm; }; /** * AES enciphering mode constants * @enum {number} * @const */ wrapper.ENC = { ECB: 0, CBC: 2, CFB: 4, OFB: 6, CTR: 7, }, /** * AES deciphering mode constants * @enum {number} * @const */ wrapper.DEC = { ECB: 1, CBC: 3, CFB: 5, OFB: 6, CTR: 7, }, /** * AES MAC mode constants * @enum {number} * @const */ wrapper.MAC = { CBC: 0, GCM: 1, }; /** * Heap data offset * @type {number} * @const */ wrapper.HEAP_DATA = 0x4000; return wrapper; }(); class AES { constructor(key, iv, padding = true, mode, heap, asm) { this.pos = 0; this.len = 0; this.mode = mode; // The AES "worker" this.heap = heap ? heap : _heap_init().subarray(AES_asm.HEAP_DATA); this.asm = asm ? asm : new AES_asm(null, this.heap.buffer); // The AES object state this.pos = 0; this.len = 0; // Key const keylen = key.length; if (keylen !== 16 && keylen !== 24 && keylen !== 32) throw new IllegalArgumentError('illegal key size'); const keyview = new DataView(key.buffer, key.byteOffset, key.byteLength); this.asm.set_key(keylen >> 2, keyview.getUint32(0), keyview.getUint32(4), keyview.getUint32(8), keyview.getUint32(12), keylen > 16 ? keyview.getUint32(16) : 0, keylen > 16 ? keyview.getUint32(20) : 0, keylen > 24 ? keyview.getUint32(24) : 0, keylen > 24 ? keyview.getUint32(28) : 0); // IV if (iv !== undefined) { if (iv.length !== 16) throw new IllegalArgumentError('illegal iv size'); let ivview = new DataView(iv.buffer, iv.byteOffset, iv.byteLength); this.asm.set_iv(ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12)); } else { this.asm.set_iv(0, 0, 0, 0); } this.padding = padding; } AES_Encrypt_process(data) { if (!is_bytes(data)) throw new TypeError("data isn't of expected type"); let asm = this.asm; let heap = this.heap; let amode = AES_asm.ENC[this.mode]; let hpos = AES_asm.HEAP_DATA; let pos = this.pos; let len = this.len; let dpos = 0; let dlen = data.length || 0; let rpos = 0; let rlen = (len + dlen) & -16; let wlen = 0; let result = new Uint8Array(rlen); while (dlen > 0) { wlen = _heap_write(heap, pos + len, data, dpos, dlen); len += wlen; dpos += wlen; dlen -= wlen; wlen = asm.cipher(amode, hpos + pos, len); if (wlen) result.set(heap.subarray(pos, pos + wlen), rpos); rpos += wlen; if (wlen < len) { pos += wlen; len -= wlen; } else { pos = 0; len = 0; } } this.pos = pos; this.len = len; return result; } AES_Encrypt_finish() { let asm = this.asm; let heap = this.heap; let amode = AES_asm.ENC[this.mode]; let hpos = AES_asm.HEAP_DATA; let pos = this.pos; let len = this.len; let plen = 16 - (len % 16); let rlen = len; if (this.hasOwnProperty('padding')) { if (this.padding) { for (let p = 0; p < plen; ++p) { heap[pos + len + p] = plen; } len += plen; rlen = len; } else if (len % 16) { throw new IllegalArgumentError('data length must be a multiple of the block size'); } } else { len += plen; } const result = new Uint8Array(rlen); if (len) asm.cipher(amode, hpos + pos, len); if (rlen) result.set(heap.subarray(pos, pos + rlen)); this.pos = 0; this.len = 0; return result; } AES_Decrypt_process(data) { if (!is_bytes(data)) throw new TypeError("data isn't of expected type"); let asm = this.asm; let heap = this.heap; let amode = AES_asm.DEC[this.mode]; let hpos = AES_asm.HEAP_DATA; let pos = this.pos; let len = this.len; let dpos = 0; let dlen = data.length || 0; let rpos = 0; let rlen = (len + dlen) & -16; let plen = 0; let wlen = 0; if (this.padding) { plen = len + dlen - rlen || 16; rlen -= plen; } const result = new Uint8Array(rlen); while (dlen > 0) { wlen = _heap_write(heap, pos + len, data, dpos, dlen); len += wlen; dpos += wlen; dlen -= wlen; wlen = asm.cipher(amode, hpos + pos, len - (!dlen ? plen : 0)); if (wlen) result.set(heap.subarray(pos, pos + wlen), rpos); rpos += wlen; if (wlen < len) { pos += wlen; len -= wlen; } else { pos = 0; len = 0; } } this.pos = pos; this.len = len; return result; } AES_Decrypt_finish() { let asm = this.asm; let heap = this.heap; let amode = AES_asm.DEC[this.mode]; let hpos = AES_asm.HEAP_DATA; let pos = this.pos; let len = this.len; let rlen = len; if (len > 0) { if (len % 16) { if (this.hasOwnProperty('padding')) { throw new IllegalArgumentError('data length must be a multiple of the block size'); } else { len += 16 - (len % 16); } } asm.cipher(amode, hpos + pos, len); if (this.hasOwnProperty('padding') && this.padding) { let pad = heap[pos + rlen - 1]; if (pad < 1 || pad > 16 || pad > rlen) throw new SecurityError('bad padding'); let pcheck = 0; for (let i = pad; i > 1; i--) pcheck |= pad ^ heap[pos + rlen - i]; if (pcheck) throw new SecurityError('bad padding'); rlen -= pad; } } const result = new Uint8Array(rlen); if (rlen > 0) { result.set(heap.subarray(pos, pos + rlen)); } this.pos = 0; this.len = 0; return result; } } class AES_CBC { static encrypt(data, key, padding = true, iv) { return new AES_CBC(key, iv, padding).encrypt(data); } static decrypt(data, key, padding = true, iv) { return new AES_CBC(key, iv, padding).decrypt(data); } constructor(key, iv, padding = true, aes) { this.aes = aes ? aes : new AES(key, iv, padding, 'CBC'); } encrypt(data) { const r1 = this.aes.AES_Encrypt_process(data); const r2 = this.aes.AES_Encrypt_finish(); return joinBytes(r1, r2); } decrypt(data) { const r1 = this.aes.AES_Decrypt_process(data); const r2 = this.aes.AES_Decrypt_finish(); return joinBytes(r1, r2); } } exports.AES_CBC = AES_CBC; /** * Counter with CBC-MAC (CCM) * * Due to JS limitations (52 bits of Number precision) maximum encrypted message length * is limited to ~4 PiB ( 2^52 - 16 ) per `nonce`-`key` pair. * That also limits `lengthSize` parameter maximum value to 7 (not 8 as described in RFC3610). * * Additional authenticated data `adata` maximum length is chosen to be no more than 65279 bytes ( 2^16 - 2^8 ), * which is considered enough for the most of use-cases. * * And one more important thing: in case of progressive ciphering of a data stream (in other * words when data can't be held in-memory at a whole and are ciphered chunk-by-chunk) * you have to know the `dataLength` in advance and pass that value to the cipher options. */ const _AES_CCM_adata_maxLength = 65279; // 2^16 - 2^8 const _AES_CCM_data_maxLength = 4503599627370480; // 2^52 - 2^4 class AES_CCM { constructor(key, nonce, adata, tagSize = 16, dataLength, aes) { this.counter = 1; this.dataLength = -1; this.aes = aes ? aes : new AES(key, undefined, undefined, 'CCM'); // Tag size if (tagSize < 4 || tagSize > 16 || tagSize & 1) throw new IllegalArgumentError('illegal tagSize value'); this.tagSize = tagSize; // Nonce this.nonce = nonce; if (nonce.length < 8 || nonce.length > 13) throw new IllegalArgumentError('illegal nonce length'); this.lengthSize = 15 - nonce.length; nonce = new Uint8Array(nonce.length + 1); nonce[0] = this.lengthSize - 1; nonce.set(this.nonce, 1); if (dataLength < 0 || dataLength > _AES_CCM_data_maxLength || dataLength > Math.pow(2, 8 * this.lengthSize) - 16) throw new IllegalArgumentError('illegal dataLength value'); if (adata !== undefined) { if (adata.length > _AES_CCM_adata_maxLength) throw new IllegalArgumentError('illegal adata length'); this.adata = adata.length ? adata : undefined; } this.dataLength = dataLength; this.counter = 1; this.AES_CCM_calculate_iv(); this.AES_CTR_set_options(nonce, this.counter, 8 * this.lengthSize); } static encrypt(clear, key, nonce, adata, tagsize = 16) { return new AES_CCM(key, nonce, adata, tagsize, clear.length).encrypt(clear); } static decrypt(cipher, key, nonce, adata, tagsize = 16) { return new AES_CCM(key, nonce, adata, tagsize, cipher.length - tagsize).decrypt(cipher); } encrypt(data) { this.dataLength = data.length || 0; const result1 = this.AES_CCM_Encrypt_process(data); const result2 = this.AES_CCM_Encrypt_finish(); const result = new Uint8Array(result1.length + result2.length); if (result1.length) result.set(result1); if (result2.length) result.set(result2, result1.length); return result; } decrypt(data) { this.dataLength = data.length || 0; const result1 = this.AES_CCM_Decrypt_process(data); const result2 = this.AES_CCM_Decrypt_finish(); const result = new Uint8Array(result1.length + result2.length); if (result1.length) result.set(result1); if (result2.length) result.set(result2, result1.length); return result; } AES_CCM_calculate_iv() { const nonce = this.nonce; const adata = this.adata; const tagSize = this.tagSize; const lengthSize = this.lengthSize; const dataLength = this.dataLength; const data = new Uint8Array(16 + (adata ? 2 + adata.length : 0)); // B0: flags(adata?, M', L'), nonce, len(data) data[0] = (adata ? 64 : 0) | ((tagSize - 2) << 2) | (lengthSize - 1); data.set(nonce, 1); if (lengthSize > 6) data[9] = ((dataLength / 0x100000000) >>> 16) & 15; if (lengthSize > 5) data[10] = ((dataLength / 0x100000000) >>> 8) & 255; if (lengthSize > 4) data[11] = (dataLength / 0x100000000) & 255; if (lengthSize > 3) data[12] = dataLength >>> 24; if (lengthSize > 2) data[13] = (dataLength >>> 16) & 255; data[14] = (dataLength >>> 8) & 255; data[15] = dataLength & 255; // B*: len(adata), adata if (adata) { data[16] = (adata.length >>> 8) & 255; data[17] = adata.length & 255; data.set(adata, 18); } this._cbc_mac_process(data); this.aes.asm.get_state(AES_asm.HEAP_DATA); const iv = new Uint8Array(this.aes.heap.subarray(0, 16)); const ivview = new DataView(iv.buffer, iv.byteOffset, iv.byteLength); this.aes.asm.set_iv(ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12)); } _cbc_mac_process(data) { const heap = this.aes.heap; const asm = this.aes.asm; let dpos = 0; let dlen = data.length || 0; let wlen = 0; while (dlen > 0) { wlen = _heap_write(heap, 0, data, dpos, dlen); while (wlen & 15) heap[wlen++] = 0; dpos += wlen; dlen -= wlen; asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA, wlen); } } AES_CCM_Encrypt_process(data) { const asm = this.aes.asm; const heap = this.aes.heap; let dpos = 0; let dlen = data.length || 0; let counter = this.counter; let pos = this.aes.pos; let len = this.aes.len; const rlen = (len + dlen) & -16; let rpos = 0; let wlen = 0; if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength) // ??? should check against lengthSize throw new RangeError('counter overflow'); const result = new Uint8Array(rlen); while (dlen > 0) { wlen = _heap_write(heap, pos + len, data, dpos, dlen); len += wlen; dpos += wlen; dlen -= wlen; wlen = asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, len); wlen = asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, wlen); if (wlen) result.set(heap.subarray(pos, pos + wlen), rpos); counter += wlen >>> 4; rpos += wlen; if (wlen < len) { pos += wlen; len -= wlen; } else { pos = 0; len = 0; } } this.counter = counter; this.aes.pos = pos; this.aes.len = len; return result; } AES_CCM_Encrypt_finish() { const asm = this.aes.asm; const heap = this.aes.heap; const tagSize = this.tagSize; const pos = this.aes.pos; const len = this.aes.len; const result = new Uint8Array(len + tagSize); let i = len; for (; i & 15; i++) heap[pos + i] = 0; asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, i); asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, i); if (len) result.set(heap.subarray(pos, pos + len)); asm.set_counter(0, 0, 0, 0); asm.get_iv(AES_asm.HEAP_DATA); asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16); result.set(heap.subarray(0, tagSize), len); this.counter = 1; this.aes.pos = 0; this.aes.len = 0; return result; } AES_CCM_Decrypt_process(data) { let dpos = 0; let dlen = data.length || 0; const asm = this.aes.asm; const heap = this.aes.heap; let counter = this.counter; const tagSize = this.tagSize; let pos = this.aes.pos; let len = this.aes.len; let rpos = 0; const rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0; const tlen = len + dlen - rlen; let wlen = 0; if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength) throw new RangeError('counter overflow'); const result = new Uint8Array(rlen); while (dlen > tlen) { wlen = _heap_write(heap, pos + len, data, dpos, dlen - t