209 lines
5.9 KiB
JavaScript
209 lines
5.9 KiB
JavaScript
/** @fileOverview Low-level AES implementation.
|
|
*
|
|
* This file contains a low-level implementation of AES, optimized for
|
|
* size and for efficiency on several browsers. It is based on
|
|
* OpenSSL's aes_core.c, a public-domain implementation by Vincent
|
|
* Rijmen, Antoon Bosselaers and Paulo Barreto.
|
|
*
|
|
* An older version of this implementation is available in the public
|
|
* domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
|
|
* Stanford University 2008-2010 and BSD-licensed for liability
|
|
* reasons.
|
|
*
|
|
* @author Emily Stark
|
|
* @author Mike Hamburg
|
|
* @author Dan Boneh
|
|
*/
|
|
|
|
/**
|
|
* Schedule out an AES key for both encryption and decryption. This
|
|
* is a low-level class. Use a cipher mode to do bulk encryption.
|
|
*
|
|
* @constructor
|
|
* @param {Array} key The key as an array of 4, 6 or 8 words.
|
|
*
|
|
* @class Advanced Encryption Standard (low-level interface)
|
|
*/
|
|
sjcl.cipher.aes = function (key) {
|
|
if (!this._tables[0][0][0]) {
|
|
this._precompute();
|
|
}
|
|
|
|
var i, j, tmp,
|
|
encKey, decKey,
|
|
sbox = this._tables[0][4], decTable = this._tables[1],
|
|
keyLen = key.length, rcon = 1;
|
|
|
|
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
|
|
throw new sjcl.exception.invalid("invalid aes key size");
|
|
}
|
|
|
|
this._key = [encKey = key.slice(0), decKey = []];
|
|
|
|
// schedule encryption keys
|
|
for (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 (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]];
|
|
}
|
|
}
|
|
};
|
|
|
|
sjcl.cipher.aes.prototype = {
|
|
// public
|
|
/* Something like this might appear here eventually
|
|
name: "AES",
|
|
blockSize: 4,
|
|
keySizes: [4,6,8],
|
|
*/
|
|
|
|
/**
|
|
* 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: [[[],[],[],[],[]],[[],[],[],[],[]]],
|
|
|
|
/**
|
|
* Expand the S-box tables.
|
|
*
|
|
* @private
|
|
*/
|
|
_precompute: function () {
|
|
var encTable = this._tables[0], decTable = this._tables[1],
|
|
sbox = encTable[4], sboxInv = decTable[4],
|
|
i, x, xInv, d=[], th=[], 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;
|
|
}
|
|
}
|
|
|
|
// Compactify. Considerable speedup on Firefox.
|
|
for (i = 0; i < 5; i++) {
|
|
encTable[i] = encTable[i].slice(0);
|
|
decTable[i] = decTable[i].slice(0);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 sjcl.exception.invalid("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 = [0,0,0,0],
|
|
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;
|
|
}
|
|
};
|
|
|