pluginengine01 / crates /bex-js /assets /crypto_subtle.js
krystv's picture
Upload 107 files
3374e90 verified
// 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 };
}
})();