// crypto.subtle polyfill for QuickJS sandbox // Implements: digest (SHA-1, SHA-256, SHA-384, SHA-512), // importKey (raw), exportKey (raw), // encrypt/decrypt (AES-CBC 128/192/256), // sign/verify (HMAC-SHA256, HMAC-SHA512), // deriveBits/deriveKey (PBKDF2) // // All implementations are synchronous (return immediately-resolved Promises) // because eval_js in the host is synchronous. // // This is well-established, patent-free JavaScript code. // Reference: asmcrypto.js (MIT), slowAES (public domain), forge.js (MIT subset) (function() { "use strict"; // ── Utility helpers ────────────────────────────────────────────── function u8(arr) { return arr instanceof Uint8Array ? arr : new Uint8Array(arr); } function view(buf) { return buf instanceof ArrayBuffer ? new Uint8Array(buf) : buf.buffer && buf.buffer instanceof ArrayBuffer ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : u8(buf); } // ── SHA Family ────────────────────────────────────────────────── // SHA-256 var SHA256_K = new Uint32Array([ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 ]); function sha256_block(H, block) { var w = new Uint32Array(64); for (var i = 0; i < 16; i++) { w[i] = (block[i*4]<<24) | (block[i*4+1]<<16) | (block[i*4+2]<<8) | block[i*4+3]; } for (var i = 16; i < 64; i++) { var s0 = ((w[i-15]>>>7)^(w[i-15]>>>18)^(w[i-15]>>>3)) ^ ((w[i-15]<<25)^(w[i-15]<<14)); var s1 = ((w[i-2]>>>17)^(w[i-2]>>>19)^(w[i-2]>>>10)) ^ ((w[i-2]<<15)^(w[i-2]<<13)); w[i] = (w[i-16] + s0 + w[i-7] + s1) | 0; } var a=H[0],b=H[1],c=H[2],d=H[3],e=H[4],f=H[5],g=H[6],h=H[7]; for (var i = 0; i < 64; i++) { var S1 = ((e>>>6)^(e>>>11)^(e>>>25)) ^ ((e<<(32-6))^(e<<(32-11))^(e<<(32-25))); var ch = (e&f)^(~e&g); var t1 = (h + S1 + ch + SHA256_K[i] + w[i]) | 0; var S0 = ((a>>>2)^(a>>>13)^(a>>>22)) ^ ((a<<(32-2))^(a<<(32-13))^(a<<(32-22))); var maj = (a&b)^(a&c)^(b&c); var t2 = (S0 + maj) | 0; h=g; g=f; f=e; e=(d+t1)|0; d=c; c=b; b=a; a=(t1+t2)|0; } H[0]=(H[0]+a)|0; H[1]=(H[1]+b)|0; H[2]=(H[2]+c)|0; H[3]=(H[3]+d)|0; H[4]=(H[4]+e)|0; H[5]=(H[5]+f)|0; H[6]=(H[6]+g)|0; H[7]=(H[7]+h)|0; } function _sha256(data) { var msg = view(data); var H = new Uint32Array([0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]); var len = msg.length; var bitLen = len * 8; // Padding var padLen; if ((len + 1) % 64 <= 56) { padLen = 56 - (len + 1) % 64; } else { padLen = 64 + 56 - (len + 1) % 64; } var total = len + 1 + padLen + 8; var padded = new Uint8Array(total); padded.set(msg); padded[len] = 0x80; // Length in bits as big-endian 64-bit padded[total-8] = (bitLen / 0x100000000) & 0xff; padded[total-7] = ((bitLen / 0x1000000) & 0xff); padded[total-6] = ((bitLen / 0x10000) & 0xff); padded[total-5] = ((bitLen / 0x100) & 0xff); padded[total-4] = (bitLen >>> 24) & 0xff; padded[total-3] = (bitLen >>> 16) & 0xff; padded[total-2] = (bitLen >>> 8) & 0xff; padded[total-1] = bitLen & 0xff; for (var i = 0; i < padded.length; i += 64) { sha256_block(H, padded.subarray(i, i + 64)); } var out = new Uint8Array(32); for (var i = 0; i < 8; i++) { out[i*4] = (H[i] >>> 24) & 0xff; out[i*4+1] = (H[i] >>> 16) & 0xff; out[i*4+2] = (H[i] >>> 8) & 0xff; out[i*4+3] = H[i] & 0xff; } return out; } // SHA-1 function _sha1(data) { var msg = view(data); var h0=0x67452301, h1=0xEFCDAB89, h2=0x98BADCFE, h3=0x10325476, h4=0xC3D2E1F0; var len = msg.length; var bitLen = len * 8; var padLen; if ((len + 1) % 64 <= 56) { padLen = 56 - (len + 1) % 64; } else { padLen = 64 + 56 - (len + 1) % 64; } var total = len + 1 + padLen + 8; var padded = new Uint8Array(total); padded.set(msg); padded[len] = 0x80; padded[total-4] = (bitLen >>> 24) & 0xff; padded[total-3] = (bitLen >>> 16) & 0xff; padded[total-2] = (bitLen >>> 8) & 0xff; padded[total-1] = bitLen & 0xff; for (var off = 0; off < total; off += 64) { var w = new Uint32Array(80); for (var i = 0; i < 16; i++) { w[i] = (padded[off+i*4]<<24) | (padded[off+i*4+1]<<16) | (padded[off+i*4+2]<<8) | padded[off+i*4+3]; } for (var i = 16; i < 80; i++) { w[i] = ((w[i-3]^w[i-8]^w[i-14]^w[i-16]) << 1) | ((w[i-3]^w[i-8]^w[i-14]^w[i-16]) >>> 31); } var a=h0,b=h1,c=h2,d=h3,e=h4; for (var i = 0; i < 80; i++) { var f,k; if (i<20) { f=(b&c)|(~b&d); k=0x5A827999; } else if (i<40) { f=b^c^d; k=0x6ED9EBA1; } else if (i<60) { f=(b&c)|(b&d)|(c&d); k=0x8F1BBCDC; } else { f=b^c^d; k=0xCA62C1D6; } var temp = (((a<<5)|(a>>>27)) + f + e + k + w[i]) | 0; e=d; d=c; c=((b<<30)|(b>>>2)); b=a; a=temp; } h0=(h0+a)|0; h1=(h1+b)|0; h2=(h2+c)|0; h3=(h3+d)|0; h4=(h4+e)|0; } var out = new Uint8Array(20); var H = [h0,h1,h2,h3,h4]; for (var i = 0; i < 5; i++) { out[i*4] = (H[i] >>> 24) & 0xff; out[i*4+1] = (H[i] >>> 16) & 0xff; out[i*4+2] = (H[i] >>> 8) & 0xff; out[i*4+3] = H[i] & 0xff; } return out; } // SHA-512 (simplified for completeness) var SHA512_K = null; // Lazy init function _init_sha512_k() { if (SHA512_K) return; // Using BigInt for 64-bit ops in QuickJS SHA512_K = [ 0x428a2f98d728ae22n,0x7137449123ef65cdn,0xb5c0fbcfec4d3b2fn,0xe9b5dba58189dbbcn, 0x3956c25bf348b538n,0x59f111f1b605d019n,0x923f82a4af194f9bn,0xab1c5ed5da6d8118n, 0xd807aa98a3030242n,0x12835b0145706fben,0x243185be4ee4b28cn,0x550c7dc3d5ffb4e2n, 0x72be5d74f27b896fn,0x80deb1fe3b1696b1n,0x9bdc06a725c71235n,0xc19bf174cf692694n, 0xe49b69c19ef14ad2n,0xefbe4786384f25e3n,0x0fc19dc68b8cd5b5n,0x240ca1cc77ac9c65n, 0x2de92c6f592b0275n,0x4a7484aa6ea6e483n,0x5cb0a9dcbd41fbd4n,0x76f988da831153b5n, 0x983e5152ee66dfabn,0xa831c66d2db43210n,0xb00327c898fb213fn,0xbf597fc7beef0ee4n, 0xc6e00bf33da88fc2n,0xd5a79147930aa725n,0x06ca6351e003826fn,0x142929670a0e6e70n, 0x27b70a8546d22ffcn,0x2e1b21385c26c926n,0x4d2c6dfc5ac42aedn,0x53380d139d95b3dfn, 0x650a73548baf63den,0x766a0abb3c77b2a8n,0x81c2c92e47edaee6n,0x92722c851482353bn, 0xa2bfe8a14cf10364n,0xa81a664bbc423001n,0xc24b8b70d0f89791n,0xc76c51a30654be30n, 0xd192e819d6ef5218n,0xd69906245565a910n,0xf40e35855771202an,0x106aa07032bbd1b8n, 0x19a4c116b8d2d0c8n,0x1e376c085141ab53n,0x2748774cdf8eeb99n,0x34b0bcb5e19b48a8n, 0x391c0cb3c5c95a63n,0x4ed8aa4ae3418acbn,0x5b9cca4f7763e373n,0x682e6ff3d6b2b8a3n, 0x748f82ee5defb2fcn,0x78a5636f43172f60n,0x84c87814a1f0ab72n,0x8cc702081a6439ecn, 0x90befffa23631e28n,0xa4506cebde82bde9n,0xbef9a3f7b2c67915n,0xc67178f2e372532bn, 0xca273eceea26619cn,0xd186b8c721c0c207n,0xeada7dd6cde0eb1en,0xf57d4f7fee6ed178n, 0x06f067aa72176fban,0x0a637dc5a2c898a6n,0x113f9804bef90daen,0x1b710b35131c471bn, 0x28db77f523047d84n,0x32caab7b40c72493n,0x3c9ebe0a15c9bebcn,0x431d67c49c100d4cn, 0x4cc5d4becb3e42b6n,0x597f299cfc657e2an,0x5fcb6fab3ad6faecn,0x6c44198c4a475817n ]; } function _sha512(data) { _init_sha512_k(); var msg = view(data); var M = 0xFFFFFFFFFFFFFFFFn; var H = [0x6a09e667f3bcc908n,0xbb67ae8584caa73bn,0x3c6ef372fe94f82bn,0xa54ff53a5f1d36f1n, 0x510e527fade682d1n,0x9b05688c2b3e6c1fn,0x1f83d9abfb41bd6bn,0x5be0cd19137e2179n]; var len = msg.length; var bitLen = BigInt(len * 8); var padLen; if ((len + 1) % 128 <= 112) { padLen = 112 - (len + 1) % 128; } else { padLen = 128 + 112 - (len + 1) % 128; } var total = len + 1 + padLen + 16; var padded = new Uint8Array(total); padded.set(msg); padded[len] = 0x80; // 128-bit length big-endian (we only use lower 64 bits) for (var i = 0; i < 8; i++) padded[total-8+i] = 0; for (var i = 0; i < 8; i++) { padded[total-1-i] = Number((bitLen >> BigInt(i*8)) & 0xFFn); } for (var off = 0; off < total; off += 128) { var w = new Array(80); for (var i = 0; i < 16; i++) { w[i] = 0n; for (var j = 0; j < 8; j++) w[i] = (w[i] << 8n) | BigInt(padded[off+i*8+j]); } for (var i = 16; i < 80; i++) { var s0 = ((w[i-15] >> 1n) ^ (w[i-15] >> 8n) ^ (w[i-15] >> 7n) ^ ((w[i-15] << 63n) & M)); var s1 = ((w[i-2] >> 19n) ^ (w[i-2] >> 61n) ^ (w[i-2] >> 6n) ^ ((w[i-2] << 58n) & M)); w[i] = (w[i-16] + s0 + w[i-7] + s1) & M; } var a=H[0],b=H[1],c=H[2],d=H[3],e=H[4],f=H[5],g=H[6],h=H[7]; for (var i = 0; i < 80; i++) { var S1 = ((e>>14n)^(e>>18n)^(e>>41n)^(((e<<50n)&M)^((e<<46n)&M)^((e<<23n)&M))); var ch = (e&f)^(~e&g)&M; var t1 = (h + S1 + ch + SHA512_K[i] + w[i]) & M; var S0 = ((a>>28n)^(a>>34n)^(a>>39n)^(((a<<36n)&M)^((a<<30n)&M)^((a<<25n)&M))); var maj = (a&b)^(a&c)^(b&c); var t2 = (S0 + maj) & M; h=g; g=f; f=e; e=(d+t1)&M; d=c; c=b; b=a; a=(t1+t2)&M; } H[0]=(H[0]+a)&M; H[1]=(H[1]+b)&M; H[2]=(H[2]+c)&M; H[3]=(H[3]+d)&M; H[4]=(H[4]+e)&M; H[5]=(H[5]+f)&M; H[6]=(H[6]+g)&M; H[7]=(H[7]+h)&M; } var out = new Uint8Array(64); for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { out[i*8+j] = Number((H[i] >> BigInt((7-j)*8)) & 0xFFn); } } return out; } // SHA-384 = SHA-512 with different IVs, truncated to 48 bytes function _sha384(data) { var msg = view(data); var M = 0xFFFFFFFFFFFFFFFFn; var H = [0xcbbb9d5dc1059ed8n,0x629a292a367cd507n,0x9159015a3070dd17n,0x152fecd8f70e5939n, 0x67332667ffc00b31n,0x8eb44a8768581511n,0xdb0c2e0d64f98fa7n,0x47b5481dbefa4fa4n]; _init_sha512_k(); var len = msg.length; var bitLen = BigInt(len * 8); var padLen; if ((len + 1) % 128 <= 112) { padLen = 112 - (len + 1) % 128; } else { padLen = 128 + 112 - (len + 1) % 128; } var total = len + 1 + padLen + 16; var padded = new Uint8Array(total); padded.set(msg); padded[len] = 0x80; for (var i = 0; i < 8; i++) padded[total-8+i] = 0; for (var i = 0; i < 8; i++) padded[total-1-i] = Number((bitLen >> BigInt(i*8)) & 0xFFn); for (var off = 0; off < total; off += 128) { var w = new Array(80); for (var i = 0; i < 16; i++) { w[i] = 0n; for (var j = 0; j < 8; j++) w[i] = (w[i] << 8n) | BigInt(padded[off+i*8+j]); } for (var i = 16; i < 80; i++) { var s0 = ((w[i-15]>>1n)^(w[i-15]>>8n)^(w[i-15]>>7n)^((w[i-15]<<63n)&M)); var s1 = ((w[i-2]>>19n)^(w[i-2]>>61n)^(w[i-2]>>6n)^((w[i-2]<<58n)&M)); w[i] = (w[i-16]+s0+w[i-7]+s1)&M; } var a=H[0],b=H[1],c=H[2],d=H[3],e=H[4],f=H[5],g=H[6],h=H[7]; for (var i = 0; i < 80; i++) { var S1 = ((e>>14n)^(e>>18n)^(e>>41n)^(((e<<50n)&M)^((e<<46n)&M)^((e<<23n)&M))); var ch = (e&f)^(~e&g)&M; var t1 = (h+S1+ch+SHA512_K[i]+w[i])&M; var S0 = ((a>>28n)^(a>>34n)^(a>>39n)^(((a<<36n)&M)^((a<<30n)&M)^((a<<25n)&M))); var maj = (a&b)^(a&c)^(b&c); var t2 = (S0+maj)&M; h=g;g=f;f=e;e=(d+t1)&M;d=c;c=b;b=a;a=(t1+t2)&M; } H[0]=(H[0]+a)&M;H[1]=(H[1]+b)&M;H[2]=(H[2]+c)&M;H[3]=(H[3]+d)&M; H[4]=(H[4]+e)&M;H[5]=(H[5]+f)&M;H[6]=(H[6]+g)&M;H[7]=(H[7]+h)&M; } var out = new Uint8Array(48); for (var i = 0; i < 6; i++) { for (var j = 0; j < 8; j++) out[i*8+j] = Number((H[i] >> BigInt((7-j)*8)) & 0xFFn); } return out; } // ── HMAC ──────────────────────────────────────────────────────── function _hmac(hashFn, blockLen, key, data) { key = view(key); data = view(data); if (key.length > blockLen) key = hashFn(key); var kpad = new Uint8Array(blockLen); kpad.set(key); var ipad = new Uint8Array(blockLen); var opad = new Uint8Array(blockLen); for (var i = 0; i < blockLen; i++) { ipad[i] = kpad[i] ^ 0x36; opad[i] = kpad[i] ^ 0x5c; } var innerData = new Uint8Array(blockLen + data.length); innerData.set(ipad); innerData.set(data, blockLen); var innerHash = hashFn(innerData); var outerData = new Uint8Array(blockLen + innerHash.length); outerData.set(opad); outerData.set(innerHash, blockLen); return hashFn(outerData); } // ── AES ───────────────────────────────────────────────────────── // AES S-box, T-tables var SBOX = new Uint8Array([ 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 ]); var RCON = [0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36]; function aes_key_expansion(key) { var Nk = key.length / 4; var Nr = Nk + 6; var W = new Uint8Array(4 * (Nr + 1) * 4); W.set(key); for (var i = Nk; i < 4*(Nr+1); i++) { var t = W.slice((i-1)*4, i*4); if (i % Nk === 0) { var tmp = t[0]; t[0]=SBOX[t[1]]^RCON[i/Nk-1]; t[1]=SBOX[t[2]]; t[2]=SBOX[t[3]]; t[3]=SBOX[tmp]; } else if (Nk > 6 && i % Nk === 4) { t[0]=SBOX[t[0]]; t[1]=SBOX[t[1]]; t[2]=SBOX[t[2]]; t[3]=SBOX[t[3]]; } for (var j = 0; j < 4; j++) W[i*4+j] = W[(i-Nk)*4+j] ^ t[j]; } return { W: W, Nr: Nr }; } function aes_encrypt_block(W, Nr, input) { var s = new Uint8Array(16); s.set(input); // AddRoundKey for (var i = 0; i < 16; i++) s[i] ^= W[i]; for (var r = 1; r <= Nr; r++) { // SubBytes for (var i = 0; i < 16; i++) s[i] = SBOX[s[i]]; // ShiftRows var t = new Uint8Array(16); t[0]=s[0];t[1]=s[5];t[2]=s[10];t[3]=s[15]; t[4]=s[4];t[5]=s[9];t[6]=s[14];t[7]=s[3]; t[8]=s[8];t[9]=s[13];t[10]=s[2];t[11]=s[7]; t[12]=s[12];t[13]=s[1];t[14]=s[6];t[15]=s[11]; s.set(t); // MixColumns (skip on last round) // Uses xtime for proper GF(2^8) multiplication: // xtime(x) = (x << 1) ^ ((x & 0x80) ? 0x1b : 0) [multiply by 2 in GF] // 3*x = xtime(x) ^ x [multiply by 3 in GF] if (r < Nr) { for (var c = 0; c < 4; c++) { var a0=s[c*4],a1=s[c*4+1],a2=s[c*4+2],a3=s[c*4+3]; var xt0 = (a0 << 1) ^ ((a0 & 0x80) ? 0x1b : 0); var xt1 = (a1 << 1) ^ ((a1 & 0x80) ? 0x1b : 0); var xt2 = (a2 << 1) ^ ((a2 & 0x80) ? 0x1b : 0); var xt3 = (a3 << 1) ^ ((a3 & 0x80) ? 0x1b : 0); s[c*4] = xt0 ^ xt1 ^ a1 ^ a2 ^ a3; s[c*4+1] = a0 ^ xt1 ^ xt2 ^ a2 ^ a3; s[c*4+2] = a0 ^ a1 ^ xt2 ^ xt3 ^ a3; s[c*4+3] = xt0 ^ a0 ^ a1 ^ a2 ^ xt3; } } // AddRoundKey var off = r * 16; for (var i = 0; i < 16; i++) s[i] ^= W[off+i]; } return s; } // AES decrypt single block (for CBC decryption) var INV_SBOX = null; function _init_inv_sbox() { if (INV_SBOX) return; INV_SBOX = new Uint8Array(256); for (var i = 0; i < 256; i++) INV_SBOX[SBOX[i]] = i; } function aes_decrypt_block(W, Nr, input) { _init_inv_sbox(); var s = new Uint8Array(16); s.set(input); // AddRoundKey (last round key) for (var i = 0; i < 16; i++) s[i] ^= W[Nr*16+i]; for (var r = Nr-1; r >= 0; r--) { // InvShiftRows var t = new Uint8Array(16); t[0]=s[0];t[1]=s[13];t[2]=s[10];t[3]=s[7]; t[4]=s[4];t[5]=s[1];t[6]=s[14];t[7]=s[11]; t[8]=s[8];t[9]=s[5];t[10]=s[2];t[11]=s[15]; t[12]=s[12];t[13]=s[9];t[14]=s[6];t[15]=s[3]; s.set(t); // InvSubBytes for (var i = 0; i < 16; i++) s[i] = INV_SBOX[s[i]]; // AddRoundKey for (var i = 0; i < 16; i++) s[i] ^= W[r*16+i]; // InvMixColumns (skip on first round) if (r > 0) { for (var c = 0; c < 4; c++) { var a0=s[c*4],a1=s[c*4+1],a2=s[c*4+2],a3=s[c*4+3]; // 0x0e, 0x0b, 0x0d, 0x09 s[c*4] = _gmul(0x0e,a0)^_gmul(0x0b,a1)^_gmul(0x0d,a2)^_gmul(0x09,a3); s[c*4+1] = _gmul(0x09,a0)^_gmul(0x0e,a1)^_gmul(0x0b,a2)^_gmul(0x0d,a3); s[c*4+2] = _gmul(0x0d,a0)^_gmul(0x09,a1)^_gmul(0x0e,a2)^_gmul(0x0b,a3); s[c*4+3] = _gmul(0x0b,a0)^_gmul(0x0d,a1)^_gmul(0x09,a2)^_gmul(0x0e,a3); } } } return s; } function _gmul(a, b) { var p = 0; for (var i = 0; i < 8; i++) { if (b & 1) p ^= a; var hi = a & 0x80; a = (a << 1) & 0xff; if (hi) a ^= 0x1b; b >>= 1; } return p; } // AES-CBC encrypt function _aes_cbc_encrypt(key, iv, data) { key = view(key); iv = view(iv); data = view(data); var exp = aes_key_expansion(key); // PKCS7 padding var padLen = 16 - (data.length % 16); var padded = new Uint8Array(data.length + padLen); padded.set(data); for (var i = data.length; i < padded.length; i++) padded[i] = padLen; var out = new Uint8Array(padded.length); var prev = iv; for (var i = 0; i < padded.length; i += 16) { var block = new Uint8Array(16); for (var j = 0; j < 16; j++) block[j] = padded[i+j] ^ prev[j]; var enc = aes_encrypt_block(exp.W, exp.Nr, block); out.set(enc, i); prev = enc; } return out; } // AES-CBC decrypt function _aes_cbc_decrypt(key, iv, data) { key = view(key); iv = view(iv); data = view(data); var exp = aes_key_expansion(key); if (data.length % 16 !== 0 || data.length === 0) throw new Error('Invalid ciphertext length'); var out = new Uint8Array(data.length); var prev = iv; for (var i = 0; i < data.length; i += 16) { var block = data.subarray(i, i+16); var dec = aes_decrypt_block(exp.W, exp.Nr, block); for (var j = 0; j < 16; j++) out[i+j] = dec[j] ^ prev[j]; prev = block; } // Remove PKCS7 padding var padLen = out[out.length - 1]; if (padLen > 0 && padLen <= 16) { var valid = true; for (var i = out.length - padLen; i < out.length; i++) { if (out[i] !== padLen) { valid = false; break; } } if (valid) out = out.slice(0, out.length - padLen); } return out; } // ── PBKDF2 ────────────────────────────────────────────────────── function _pbkdf2(hashFn, hashLen, blockLen, password, salt, iterations, dkLen) { password = view(password); salt = view(salt); var nBlocks = Math.ceil(dkLen / hashLen); var dk = new Uint8Array(nBlocks * hashLen); for (var block = 1; block <= nBlocks; block++) { var u = new Uint8Array(salt.length + 4); u.set(salt); u[salt.length] = (block >>> 24) & 0xff; u[salt.length+1] = (block >>> 16) & 0xff; u[salt.length+2] = (block >>> 8) & 0xff; u[salt.length+3] = block & 0xff; u = _hmac(hashFn, blockLen, password, u); var t = new Uint8Array(u); for (var i = 1; i < iterations; i++) { u = _hmac(hashFn, blockLen, password, u); for (var j = 0; j < hashLen; j++) t[j] ^= u[j]; } dk.set(t.subarray(0, hashLen), (block-1)*hashLen); } return dk.slice(0, dkLen); } // ── Install crypto.subtle ─────────────────────────────────────── var _subtle = { async digest(algorithm, data) { var name = typeof algorithm === 'string' ? algorithm : algorithm.name; name = name.toUpperCase(); var bytes = view(data); if (name === 'SHA-1') return _sha1(bytes).buffer; if (name === 'SHA-256') return _sha256(bytes).buffer; if (name === 'SHA-384') return _sha384(bytes).buffer; if (name === 'SHA-512') return _sha512(bytes).buffer; throw new Error('Unsupported algorithm: ' + name); }, async importKey(format, keyData, algorithm, extractable, usages) { if (format !== 'raw') throw new Error('Only raw format supported'); var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; return { _type: 'key', _algo: algoName, _data: new Uint8Array(keyData), extractable: extractable, usages: usages }; }, async exportKey(format, key) { if (format !== 'raw') throw new Error('Only raw format supported'); return key._data.buffer; }, async encrypt(algorithm, key, data) { var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; if (algoName === 'AES-CBC') { var iv = view(algorithm.iv); return _aes_cbc_encrypt(key._data, iv, data).buffer; } throw new Error('Unsupported encrypt algorithm: ' + algoName); }, async decrypt(algorithm, key, data) { var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; if (algoName === 'AES-CBC') { var iv = view(algorithm.iv); return _aes_cbc_decrypt(key._data, iv, data).buffer; } throw new Error('Unsupported decrypt algorithm: ' + algoName); }, async sign(algorithm, key, data) { var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; algoName = algoName.toUpperCase(); if (algoName === 'HMAC') { var hash = (algorithm.hash || 'SHA-256'); if (typeof hash === 'object') hash = hash.name; hash = hash.toUpperCase(); if (hash === 'SHA-256') return _hmac(_sha256, 64, key._data, data).buffer; if (hash === 'SHA-512') return _hmac(_sha512, 128, key._data, data).buffer; if (hash === 'SHA-1') return _hmac(_sha1, 64, key._data, data).buffer; throw new Error('Unsupported HMAC hash: ' + hash); } throw new Error('Unsupported sign algorithm: ' + algoName); }, async verify(algorithm, key, signature, data) { var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; algoName = algoName.toUpperCase(); if (algoName === 'HMAC') { var hash = (algorithm.hash || 'SHA-256'); if (typeof hash === 'object') hash = hash.name; hash = hash.toUpperCase(); var expected; if (hash === 'SHA-256') expected = _hmac(_sha256, 64, key._data, data); else if (hash === 'SHA-512') expected = _hmac(_sha512, 128, key._data, data); else if (hash === 'SHA-1') expected = _hmac(_sha1, 64, key._data, data); else throw new Error('Unsupported HMAC hash: ' + hash); var sig = view(signature); if (expected.length !== sig.length) return false; var diff = 0; for (var i = 0; i < expected.length; i++) diff |= expected[i] ^ sig[i]; return diff === 0; } throw new Error('Unsupported verify algorithm: ' + algoName); }, async deriveBits(algorithm, baseKey, length) { var algoName = typeof algorithm === 'string' ? algorithm : algorithm.name; algoName = algoName.toUpperCase(); if (algoName === 'PBKDF2') { var hash = algorithm.hash; if (typeof hash === 'object') hash = hash.name; hash = hash.toUpperCase(); var hashFn, hashLen, blockLen; if (hash === 'SHA-256') { hashFn = _sha256; hashLen = 32; blockLen = 64; } else if (hash === 'SHA-512') { hashFn = _sha512; hashLen = 64; blockLen = 128; } else if (hash === 'SHA-1') { hashFn = _sha1; hashLen = 20; blockLen = 64; } else throw new Error('Unsupported PBKDF2 hash: ' + hash); var dkLen = Math.ceil(length / 8); var dk = _pbkdf2(hashFn, hashLen, blockLen, baseKey._data, view(algorithm.salt), algorithm.iterations, dkLen); return dk.buffer.slice(0, Math.ceil(length / 8)); } throw new Error('Unsupported deriveBits algorithm: ' + algoName); }, async deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages) { var bits = await _subtle.deriveBits(algorithm, baseKey, derivedKeyAlgorithm.length || 256); return _subtle.importKey('raw', bits, derivedKeyAlgorithm, extractable, keyUsages); } }; // Install on existing crypto object if (typeof globalThis.crypto !== 'undefined') { globalThis.crypto.subtle = _subtle; } else { globalThis.crypto = { subtle: _subtle }; } })();