mirror of
https://github.com/billbuchanan/appliedcrypto.git
synced 2026-02-23 04:07:58 +00:00
173 lines
5.1 KiB
JavaScript
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;
|
|
}
|
|
};
|