Files
billbuchanan 44789e8d3a Update
2021-01-18 20:47:18 +00:00

173 lines
5.1 KiB
JavaScript

module.exports = AES;
//@param {Array} key The key as an array of 4, 6 or 8 words.
function AES (key) {
if (!this._tables[0][0][0]) this._precompute();
var tmp, encKey, decKey;
var sbox = this._tables[0][4];
var decTable = this._tables[1];
var keyLen = key.length;
var rcon = 1;
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
throw new Error("invalid aes key size");
}
this._key = [encKey = key.slice(0), decKey = []];
// schedule encryption keys
for (var i = keyLen; i < 4 * keyLen + 28; i++) {
tmp = encKey[i-1];
// apply sbox
if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) {
tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255]<< 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
// shift rows and add rcon
if (i % keyLen === 0) {
tmp = tmp << 8 ^ tmp >>> 24 ^ rcon<<24;
rcon = rcon << 1 ^ (rcon >> 7) * 283;
}
}
encKey[i] = encKey[i-keyLen] ^ tmp;
}
// schedule decryption keys
for (var j = 0; i; j++, i--) {
tmp = encKey[j&3 ? i : i - 4];
if (i<=4 || j<4) {
decKey[j] = tmp;
} else {
decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^
decTable[1][sbox[tmp>>16 & 255]] ^
decTable[2][sbox[tmp>>8 & 255]] ^
decTable[3][sbox[tmp & 255]];
}
}
};
AES.prototype = {
/**
* Encrypt an array of 4 big-endian words.
* @param {Array} data The plaintext.
* @return {Array} The ciphertext.
*/
encrypt:function (data) { return this._crypt(data, 0); },
/**
* Decrypt an array of 4 big-endian words.
* @param {Array} data The ciphertext.
* @return {Array} The plaintext.
*/
decrypt:function (data) { return this._crypt(data, 1); },
/**
* The expanded S-box and inverse S-box tables. These will be computed
* on the client so that we don't have to send them down the wire.
*
* There are two tables, _tables[0] is for encryption and
* _tables[1] is for decryption.
*
* The first 4 sub-tables are the expanded S-box with MixColumns. The
* last (_tables[01][4]) is the S-box itself.
*
* @private
*/
_tables: [
[ new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256) ],
[ new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256) ]
],
//Expand the S-box tables.
_precompute: function () {
var encTable = this._tables[0], decTable = this._tables[1],
sbox = encTable[4], sboxInv = decTable[4],
i, x, xInv, d=new Uint8Array(256), th=new Uint8Array(256), x2, x4, x8, s, tEnc, tDec;
// Compute double and third tables
for (i = 0; i < 256; i++) {
th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i;
}
for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
// Compute sbox
s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
s = s >> 8 ^ s & 255 ^ 99;
sbox[x] = s;
sboxInv[s] = x;
// Compute MixColumns
x8 = d[x4 = d[x2 = d[x]]];
tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100;
tEnc = d[s]*0x101 ^ s*0x1010100;
for (i = 0; i < 4; i++) {
encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8;
decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8;
}
}
},
/**
* Encryption and decryption core.
* @param {Array} input Four words to be encrypted or decrypted.
* @param dir The direction, 0 for encrypt and 1 for decrypt.
* @return {Array} The four encrypted or decrypted words.
* @private
*/
_crypt:function (input, dir) {
if (input.length !== 4) {
throw new Error("invalid aes block size");
}
var key = this._key[dir],
// state variables a,b,c,d are loaded with pre-whitened data
a = input[0] ^ key[0],
b = input[dir ? 3 : 1] ^ key[1],
c = input[2] ^ key[2],
d = input[dir ? 1 : 3] ^ key[3],
a2, b2, c2,
nInnerRounds = key.length/4 - 2,
i,
kIndex = 4,
out = new Uint32Array(4),// <--- this is slower in Node.js, about the same in Chrome */
table = this._tables[dir],
// load up the tables
t0 = table[0],
t1 = table[1],
t2 = table[2],
t3 = table[3],
sbox = table[4];
// Inner rounds. Cribbed from OpenSSL.
for (i = 0; i < nInnerRounds; i++) {
a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex];
b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1];
c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2];
d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3];
kIndex += 4;
a=a2; b=b2; c=c2;
}
// Last round.
for (i = 0; i < 4; i++) {
out[dir ? 3&-i : i] =
sbox[a>>>24 ]<<24 ^
sbox[b>>16 & 255]<<16 ^
sbox[c>>8 & 255]<<8 ^
sbox[d & 255] ^
key[kIndex++];
a2=a; a=b; b=c; c=d; d=a2;
}
return out;
}
};