/**
* @namespace AES-256 encryption and associated modes
* @author Anonymized
* @description
*
Implementation of AES on 256 bit keys.
* @requires encoding
*/
var aes =
{
Stables: (function()
{
var a256 = function()
{
return [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
},
t5 = function(){return [a256(), a256(), a256(), a256(), a256()]},
encTable = t5(), decTable = t5(),
sbox = encTable[4], sboxInv = decTable[4],
i = 0, x = 0, xInv = 0, x2 = 0, x4 = 0, x8 = 0,
tEnc = 0, tDec = 0, s = 0, d = a256(), th = a256();
for(i=0; i < 256; i++)
th[((d[i & 255] = i<<1 ^ (i>>7)*283)^i) & 255] = i;
for(x=xInv=0; !sbox[x&255]; x^=(!x2?1:x2), xInv=th[xInv&255], xInv=(!xInv?1:xInv))
{
s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4;
s = s>>8 ^ s&255 ^ 99;
sbox[x&255] = s; sboxInv[s&255] = x;
x8 = d[(x4 = d[(x2 = d[x&255])&255])&255];
tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100;
tEnc = d[s&255]*0x101 ^ s*0x1010100;
for (i=0; i<4; i++)
{
encTable[i&3][x&255] = tEnc = tEnc<<24 ^ tEnc>>>8;
decTable[i&3][s&255] = tDec = tDec<<24 ^ tDec>>>8;
}
}
return [encTable, decTable];
})(),
key: (function()
{
var a = function(){ return [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
]};
return [a(),a()];
})(),
/** Set the key to use for encryption and decryption.
* @param {string} key ASCII key (32 bytes long)
*/
setKey: function(key)
{
var pad = function(s){var s=s+'';while(s.length<64) s+="0"; return s},
key = this._blockGen(pad(key),true), i = 0, j = 0,
k = [0,0,0,0,0,0,0,0], block = [0,0,0,0];
for(i=0; i<2; i++)
{
block = key.gen();
for(j=0; j<4; j++) k[(4*i+j)&7] = block[j&3];
}
this._setKey(k);
},
_setKey: function(key)
{
var i = 0, j = 0, rcon = 1, tmp = 0,
encKey = this.key[0],
decKey = this.key[1],
decTable = this.Stables[1],
sbox = this.Stables[0][4];
for(i = 0; i < 60; i++)
{
if(i < 8)
{
encKey[i & 63] = key[i & 7];
continue;
}
tmp = encKey[(i-1) & 63];
if(!(i%4))
{
tmp = sbox[tmp>>>24 & 255]<<24
^ sbox[tmp>>16 & 255]<<16
^ sbox[tmp>>8 & 255]<<8
^ sbox[tmp & 255];
if(!(i%8))
{
tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24;
rcon = rcon<<1 ^ (rcon>>7)*283;
}
}
encKey[i & 63] = encKey[(i-8) & 63] ^ tmp;
}
for(j = 0; i>0; j++, i--)
{
tmp = encKey[(!(j&3) ? i-4 : i)&63];
decKey[j & 63] =
(i<=4 || j<4) ? tmp :
decTable[0][sbox[tmp>>>24 & 255] & 255] ^
decTable[1][sbox[tmp>>16 & 255] & 255] ^
decTable[2][sbox[tmp>>8 & 255] & 255] ^
decTable[3][sbox[tmp & 255] & 255];
}
},
/** Constant time string equality - this is to prevent timing attacks
* This function is very slow and should only be used for sensitive comparisons
* @param {string} first string
* @param {string} second string
* @returns {boolean} true iff strings are equal
*/
ctEq: function(a, b)
{
var res = true, i = 0,
a = a+'', b=b+'',
n = a.length < b.length ? b.length : a.length;
for(i = 0; i < n; i++)
if(((i>>>=0)>>=0)>>24 & 255] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex & 63];
b2 = t0[b>>>24 & 255] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[(kIndex + 1) & 63];
c2 = t0[c>>>24 & 255] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[(kIndex + 2) & 63];
d = t0[d>>>24 & 255] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[(kIndex + 3) & 63];
kIndex += 4; a = a2; b = b2; c = c2;
}
for(i = 0; i < 4; i++)
{
out[(!dir ? i : (3&-i)) & 3] =
sbox[a>>>24 & 255]<<24 ^
sbox[b>>16 & 255]<<16 ^
sbox[c>>8 & 255]<<8 ^
sbox[d & 255] ^
key[kIndex++ & 63];
a2=a; a=b; b=c; c=d; d=a2;
}
return out;
},
/** Block generator function, with PKCS#5 support for padding.
* @private
* @param {string} s input string to process in blocks
* @param {boolean} dir false for encryption, true for decryption (no padding)
* @returns {blocks:number,gen:()->number array[4]} A record containing the number of blocks and the block generating function
*/
_blockGen: function(s, dir)
{
var s = s+'', len = s.length, block = 0, i = 0, e = len&15,
blocks = (!dir&&!e?1:0)+(!e?0:1)+(len>>4), pad = (blocks<<4)-len,
gen = function()
{
var res = [0,0,0,0], i = 0, j = 0,
m = 0, base = block++ << 4, tmp = 0;
for(i = 0; i < 4; i++)
{
for(tmp = 0, j = base+4*i, m = j+4; j < m; j++)
tmp = (tmp<<8)+encoding.a2b((j>>>=0)> (24-8*j) &255);
return res;
},
_xor4: function(x,y)
{
return [x[0]^y[0], x[1]^y[1], x[2]^y[2], x[3]^y[3]];
},
/** CBC mode encryption and decryption using AES.
* @param {string} s input plaintext or ciphertext (ASCII string)
* @param {string} iv initial vector of the encryption, a 16 bytes ASCII string
* @param {boolean} dir false for encryption, true for encryption
* @returns {string} result as an ASCII string
*/
CBC: function(s, iv, dir)
{
var i = 0, res = "", last = false,
input = this._blockGen(s, dir),
iv = this._blockGen(iv, true).gen(),
block = [0,0,0,0],
xor = this._xor4;
for(i=0; i16 || !!(tlen&1)) ? 8 : tlen,
s=s+'', sl=s.length, ol=sl>>3, L=0, i=0, iv = '',
tag = '', res = {data:'', tag:''};
for(L=2; L<4 && !!(ol>>>8*L); L++);
for(iv2+='';iv2.length < 16; iv2+="\x00");
for(i=0; i13-L) break }
tag = this._ccmTag(s, iv, adata, tlen, L);
res = this._ctrMode(s, iv, tag, tlen, L);
return res.data + res.tag;
},
/** Decryption in CCM mode.
* @param {string} s input ciphertext
* @param {string} iv random initialization vector (ASCII string)
* @param {string} adata Optional authenticated data (ASCII)
* @param {number} tlen tag length in bytes
* @return {valid:bolean,data:string} Object containing the decrypted data and authentication status
*/
CCM_decrypt: function(s, iv2, adata, tlen)
{
var tlen = (tlen<4 || tlen>16 || !!(tlen&1)) ? 8 : tlen,
s=s+'', sl=s.length, c = '', ol=(sl-tlen)>>3, L=0,
i=0, res = {data:'',tag:''}, tag = '', iv = '';
for(i=0; i>>8*L); L++);
for(iv2+='';iv2.length < 16; iv2+="\x00");
for(i=0; i13-L) break }
res = this._ctrMode(c, iv, tag, tlen, L);
s = this._ccmTag(res.data, iv, adata, tlen, L);
return {valid: this.ctEq(s,res.tag), data: res.data};
},
_ccmTag: function(s, iv, adata, tlen, L)
{
var i=0, s=s+'', sl=s.length, xor = this._xor4, c = [0,0,0,0],
ad = (function(x){var x=x+'', n=x.length, c=function(n){
return encoding.b2a(n)}; if(!n) return x; if(n<=0xFEFF)
return c(n>>16)+c(n&255)+x; return "\xff\xfe"+c(n>>>24)+
c(n>>16&255)+c(n>>8&255)+c(n&255)+x})(adata), res = '', T = '',
p = this._blockGen(s, true), q = this._blockGen(ad, true);
T = encoding.b2a(((adata==''?0:1)<<6) | (((tlen-2)>>1)<<3) | (L-1))+iv;
for(i=15-T.length; i>=0; i--) T += encoding.b2a(i>3?0:sl>>>8*i);
c = this._aes(this._blockGen(T,true).gen(), false);
if(!!ad) // Additional data
for(i=0; i