You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1091 lines
38 KiB
1091 lines
38 KiB
/** |
|
* Advanced Encryption Standard (AES) implementation. |
|
* |
|
* This implementation is based on the public domain library 'jscrypto' which |
|
* was written by: |
|
* |
|
* Emily Stark (estark@stanford.edu) |
|
* Mike Hamburg (mhamburg@stanford.edu) |
|
* Dan Boneh (dabo@cs.stanford.edu) |
|
* |
|
* Parts of this code are based on the OpenSSL implementation of AES: |
|
* http://www.openssl.org |
|
* |
|
* @author Dave Longley |
|
* |
|
* Copyright (c) 2010-2014 Digital Bazaar, Inc. |
|
*/ |
|
var forge = require('./forge'); |
|
require('./cipher'); |
|
require('./cipherModes'); |
|
require('./util'); |
|
|
|
/* AES API */ |
|
module.exports = forge.aes = forge.aes || {}; |
|
|
|
/** |
|
* Deprecated. Instead, use: |
|
* |
|
* var cipher = forge.cipher.createCipher('AES-<mode>', key); |
|
* cipher.start({iv: iv}); |
|
* |
|
* Creates an AES cipher object to encrypt data using the given symmetric key. |
|
* The output will be stored in the 'output' member of the returned cipher. |
|
* |
|
* The key and iv may be given as a string of bytes, an array of bytes, |
|
* a byte buffer, or an array of 32-bit words. |
|
* |
|
* @param key the symmetric key to use. |
|
* @param iv the initialization vector to use. |
|
* @param output the buffer to write to, null to create one. |
|
* @param mode the cipher mode to use (default: 'CBC'). |
|
* |
|
* @return the cipher. |
|
*/ |
|
forge.aes.startEncrypting = function(key, iv, output, mode) { |
|
var cipher = _createCipher({ |
|
key: key, |
|
output: output, |
|
decrypt: false, |
|
mode: mode |
|
}); |
|
cipher.start(iv); |
|
return cipher; |
|
}; |
|
|
|
/** |
|
* Deprecated. Instead, use: |
|
* |
|
* var cipher = forge.cipher.createCipher('AES-<mode>', key); |
|
* |
|
* Creates an AES cipher object to encrypt data using the given symmetric key. |
|
* |
|
* The key may be given as a string of bytes, an array of bytes, a |
|
* byte buffer, or an array of 32-bit words. |
|
* |
|
* @param key the symmetric key to use. |
|
* @param mode the cipher mode to use (default: 'CBC'). |
|
* |
|
* @return the cipher. |
|
*/ |
|
forge.aes.createEncryptionCipher = function(key, mode) { |
|
return _createCipher({ |
|
key: key, |
|
output: null, |
|
decrypt: false, |
|
mode: mode |
|
}); |
|
}; |
|
|
|
/** |
|
* Deprecated. Instead, use: |
|
* |
|
* var decipher = forge.cipher.createDecipher('AES-<mode>', key); |
|
* decipher.start({iv: iv}); |
|
* |
|
* Creates an AES cipher object to decrypt data using the given symmetric key. |
|
* The output will be stored in the 'output' member of the returned cipher. |
|
* |
|
* The key and iv may be given as a string of bytes, an array of bytes, |
|
* a byte buffer, or an array of 32-bit words. |
|
* |
|
* @param key the symmetric key to use. |
|
* @param iv the initialization vector to use. |
|
* @param output the buffer to write to, null to create one. |
|
* @param mode the cipher mode to use (default: 'CBC'). |
|
* |
|
* @return the cipher. |
|
*/ |
|
forge.aes.startDecrypting = function(key, iv, output, mode) { |
|
var cipher = _createCipher({ |
|
key: key, |
|
output: output, |
|
decrypt: true, |
|
mode: mode |
|
}); |
|
cipher.start(iv); |
|
return cipher; |
|
}; |
|
|
|
/** |
|
* Deprecated. Instead, use: |
|
* |
|
* var decipher = forge.cipher.createDecipher('AES-<mode>', key); |
|
* |
|
* Creates an AES cipher object to decrypt data using the given symmetric key. |
|
* |
|
* The key may be given as a string of bytes, an array of bytes, a |
|
* byte buffer, or an array of 32-bit words. |
|
* |
|
* @param key the symmetric key to use. |
|
* @param mode the cipher mode to use (default: 'CBC'). |
|
* |
|
* @return the cipher. |
|
*/ |
|
forge.aes.createDecryptionCipher = function(key, mode) { |
|
return _createCipher({ |
|
key: key, |
|
output: null, |
|
decrypt: true, |
|
mode: mode |
|
}); |
|
}; |
|
|
|
/** |
|
* Creates a new AES cipher algorithm object. |
|
* |
|
* @param name the name of the algorithm. |
|
* @param mode the mode factory function. |
|
* |
|
* @return the AES algorithm object. |
|
*/ |
|
forge.aes.Algorithm = function(name, mode) { |
|
if(!init) { |
|
initialize(); |
|
} |
|
var self = this; |
|
self.name = name; |
|
self.mode = new mode({ |
|
blockSize: 16, |
|
cipher: { |
|
encrypt: function(inBlock, outBlock) { |
|
return _updateBlock(self._w, inBlock, outBlock, false); |
|
}, |
|
decrypt: function(inBlock, outBlock) { |
|
return _updateBlock(self._w, inBlock, outBlock, true); |
|
} |
|
} |
|
}); |
|
self._init = false; |
|
}; |
|
|
|
/** |
|
* Initializes this AES algorithm by expanding its key. |
|
* |
|
* @param options the options to use. |
|
* key the key to use with this algorithm. |
|
* decrypt true if the algorithm should be initialized for decryption, |
|
* false for encryption. |
|
*/ |
|
forge.aes.Algorithm.prototype.initialize = function(options) { |
|
if(this._init) { |
|
return; |
|
} |
|
|
|
var key = options.key; |
|
var tmp; |
|
|
|
/* Note: The key may be a string of bytes, an array of bytes, a byte |
|
buffer, or an array of 32-bit integers. If the key is in bytes, then |
|
it must be 16, 24, or 32 bytes in length. If it is in 32-bit |
|
integers, it must be 4, 6, or 8 integers long. */ |
|
|
|
if(typeof key === 'string' && |
|
(key.length === 16 || key.length === 24 || key.length === 32)) { |
|
// convert key string into byte buffer |
|
key = forge.util.createBuffer(key); |
|
} else if(forge.util.isArray(key) && |
|
(key.length === 16 || key.length === 24 || key.length === 32)) { |
|
// convert key integer array into byte buffer |
|
tmp = key; |
|
key = forge.util.createBuffer(); |
|
for(var i = 0; i < tmp.length; ++i) { |
|
key.putByte(tmp[i]); |
|
} |
|
} |
|
|
|
// convert key byte buffer into 32-bit integer array |
|
if(!forge.util.isArray(key)) { |
|
tmp = key; |
|
key = []; |
|
|
|
// key lengths of 16, 24, 32 bytes allowed |
|
var len = tmp.length(); |
|
if(len === 16 || len === 24 || len === 32) { |
|
len = len >>> 2; |
|
for(var i = 0; i < len; ++i) { |
|
key.push(tmp.getInt32()); |
|
} |
|
} |
|
} |
|
|
|
// key must be an array of 32-bit integers by now |
|
if(!forge.util.isArray(key) || |
|
!(key.length === 4 || key.length === 6 || key.length === 8)) { |
|
throw new Error('Invalid key parameter.'); |
|
} |
|
|
|
// encryption operation is always used for these modes |
|
var mode = this.mode.name; |
|
var encryptOp = (['CFB', 'OFB', 'CTR', 'GCM'].indexOf(mode) !== -1); |
|
|
|
// do key expansion |
|
this._w = _expandKey(key, options.decrypt && !encryptOp); |
|
this._init = true; |
|
}; |
|
|
|
/** |
|
* Expands a key. Typically only used for testing. |
|
* |
|
* @param key the symmetric key to expand, as an array of 32-bit words. |
|
* @param decrypt true to expand for decryption, false for encryption. |
|
* |
|
* @return the expanded key. |
|
*/ |
|
forge.aes._expandKey = function(key, decrypt) { |
|
if(!init) { |
|
initialize(); |
|
} |
|
return _expandKey(key, decrypt); |
|
}; |
|
|
|
/** |
|
* Updates a single block. Typically only used for testing. |
|
* |
|
* @param w the expanded key to use. |
|
* @param input an array of block-size 32-bit words. |
|
* @param output an array of block-size 32-bit words. |
|
* @param decrypt true to decrypt, false to encrypt. |
|
*/ |
|
forge.aes._updateBlock = _updateBlock; |
|
|
|
/** Register AES algorithms **/ |
|
|
|
registerAlgorithm('AES-ECB', forge.cipher.modes.ecb); |
|
registerAlgorithm('AES-CBC', forge.cipher.modes.cbc); |
|
registerAlgorithm('AES-CFB', forge.cipher.modes.cfb); |
|
registerAlgorithm('AES-OFB', forge.cipher.modes.ofb); |
|
registerAlgorithm('AES-CTR', forge.cipher.modes.ctr); |
|
registerAlgorithm('AES-GCM', forge.cipher.modes.gcm); |
|
|
|
function registerAlgorithm(name, mode) { |
|
var factory = function() { |
|
return new forge.aes.Algorithm(name, mode); |
|
}; |
|
forge.cipher.registerAlgorithm(name, factory); |
|
} |
|
|
|
/** AES implementation **/ |
|
|
|
var init = false; // not yet initialized |
|
var Nb = 4; // number of words comprising the state (AES = 4) |
|
var sbox; // non-linear substitution table used in key expansion |
|
var isbox; // inversion of sbox |
|
var rcon; // round constant word array |
|
var mix; // mix-columns table |
|
var imix; // inverse mix-columns table |
|
|
|
/** |
|
* Performs initialization, ie: precomputes tables to optimize for speed. |
|
* |
|
* One way to understand how AES works is to imagine that 'addition' and |
|
* 'multiplication' are interfaces that require certain mathematical |
|
* properties to hold true (ie: they are associative) but they might have |
|
* different implementations and produce different kinds of results ... |
|
* provided that their mathematical properties remain true. AES defines |
|
* its own methods of addition and multiplication but keeps some important |
|
* properties the same, ie: associativity and distributivity. The |
|
* explanation below tries to shed some light on how AES defines addition |
|
* and multiplication of bytes and 32-bit words in order to perform its |
|
* encryption and decryption algorithms. |
|
* |
|
* The basics: |
|
* |
|
* The AES algorithm views bytes as binary representations of polynomials |
|
* that have either 1 or 0 as the coefficients. It defines the addition |
|
* or subtraction of two bytes as the XOR operation. It also defines the |
|
* multiplication of two bytes as a finite field referred to as GF(2^8) |
|
* (Note: 'GF' means "Galois Field" which is a field that contains a finite |
|
* number of elements so GF(2^8) has 256 elements). |
|
* |
|
* This means that any two bytes can be represented as binary polynomials; |
|
* when they multiplied together and modularly reduced by an irreducible |
|
* polynomial of the 8th degree, the results are the field GF(2^8). The |
|
* specific irreducible polynomial that AES uses in hexadecimal is 0x11b. |
|
* This multiplication is associative with 0x01 as the identity: |
|
* |
|
* (b * 0x01 = GF(b, 0x01) = b). |
|
* |
|
* The operation GF(b, 0x02) can be performed at the byte level by left |
|
* shifting b once and then XOR'ing it (to perform the modular reduction) |
|
* with 0x11b if b is >= 128. Repeated application of the multiplication |
|
* of 0x02 can be used to implement the multiplication of any two bytes. |
|
* |
|
* For instance, multiplying 0x57 and 0x13, denoted as GF(0x57, 0x13), can |
|
* be performed by factoring 0x13 into 0x01, 0x02, and 0x10. Then these |
|
* factors can each be multiplied by 0x57 and then added together. To do |
|
* the multiplication, values for 0x57 multiplied by each of these 3 factors |
|
* can be precomputed and stored in a table. To add them, the values from |
|
* the table are XOR'd together. |
|
* |
|
* AES also defines addition and multiplication of words, that is 4-byte |
|
* numbers represented as polynomials of 3 degrees where the coefficients |
|
* are the values of the bytes. |
|
* |
|
* The word [a0, a1, a2, a3] is a polynomial a3x^3 + a2x^2 + a1x + a0. |
|
* |
|
* Addition is performed by XOR'ing like powers of x. Multiplication |
|
* is performed in two steps, the first is an algebriac expansion as |
|
* you would do normally (where addition is XOR). But the result is |
|
* a polynomial larger than 3 degrees and thus it cannot fit in a word. So |
|
* next the result is modularly reduced by an AES-specific polynomial of |
|
* degree 4 which will always produce a polynomial of less than 4 degrees |
|
* such that it will fit in a word. In AES, this polynomial is x^4 + 1. |
|
* |
|
* The modular product of two polynomials 'a' and 'b' is thus: |
|
* |
|
* d(x) = d3x^3 + d2x^2 + d1x + d0 |
|
* with |
|
* d0 = GF(a0, b0) ^ GF(a3, b1) ^ GF(a2, b2) ^ GF(a1, b3) |
|
* d1 = GF(a1, b0) ^ GF(a0, b1) ^ GF(a3, b2) ^ GF(a2, b3) |
|
* d2 = GF(a2, b0) ^ GF(a1, b1) ^ GF(a0, b2) ^ GF(a3, b3) |
|
* d3 = GF(a3, b0) ^ GF(a2, b1) ^ GF(a1, b2) ^ GF(a0, b3) |
|
* |
|
* As a matrix: |
|
* |
|
* [d0] = [a0 a3 a2 a1][b0] |
|
* [d1] [a1 a0 a3 a2][b1] |
|
* [d2] [a2 a1 a0 a3][b2] |
|
* [d3] [a3 a2 a1 a0][b3] |
|
* |
|
* Special polynomials defined by AES (0x02 == {02}): |
|
* a(x) = {03}x^3 + {01}x^2 + {01}x + {02} |
|
* a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}. |
|
* |
|
* These polynomials are used in the MixColumns() and InverseMixColumns() |
|
* operations, respectively, to cause each element in the state to affect |
|
* the output (referred to as diffusing). |
|
* |
|
* RotWord() uses: a0 = a1 = a2 = {00} and a3 = {01}, which is the |
|
* polynomial x3. |
|
* |
|
* The ShiftRows() method modifies the last 3 rows in the state (where |
|
* the state is 4 words with 4 bytes per word) by shifting bytes cyclically. |
|
* The 1st byte in the second row is moved to the end of the row. The 1st |
|
* and 2nd bytes in the third row are moved to the end of the row. The 1st, |
|
* 2nd, and 3rd bytes are moved in the fourth row. |
|
* |
|
* More details on how AES arithmetic works: |
|
* |
|
* In the polynomial representation of binary numbers, XOR performs addition |
|
* and subtraction and multiplication in GF(2^8) denoted as GF(a, b) |
|
* corresponds with the multiplication of polynomials modulo an irreducible |
|
* polynomial of degree 8. In other words, for AES, GF(a, b) will multiply |
|
* polynomial 'a' with polynomial 'b' and then do a modular reduction by |
|
* an AES-specific irreducible polynomial of degree 8. |
|
* |
|
* A polynomial is irreducible if its only divisors are one and itself. For |
|
* the AES algorithm, this irreducible polynomial is: |
|
* |
|
* m(x) = x^8 + x^4 + x^3 + x + 1, |
|
* |
|
* or {01}{1b} in hexadecimal notation, where each coefficient is a bit: |
|
* 100011011 = 283 = 0x11b. |
|
* |
|
* For example, GF(0x57, 0x83) = 0xc1 because |
|
* |
|
* 0x57 = 87 = 01010111 = x^6 + x^4 + x^2 + x + 1 |
|
* 0x85 = 131 = 10000101 = x^7 + x + 1 |
|
* |
|
* (x^6 + x^4 + x^2 + x + 1) * (x^7 + x + 1) |
|
* = x^13 + x^11 + x^9 + x^8 + x^7 + |
|
* x^7 + x^5 + x^3 + x^2 + x + |
|
* x^6 + x^4 + x^2 + x + 1 |
|
* = x^13 + x^11 + x^9 + x^8 + x^6 + x^5 + x^4 + x^3 + 1 = y |
|
* y modulo (x^8 + x^4 + x^3 + x + 1) |
|
* = x^7 + x^6 + 1. |
|
* |
|
* The modular reduction by m(x) guarantees the result will be a binary |
|
* polynomial of less than degree 8, so that it can fit in a byte. |
|
* |
|
* The operation to multiply a binary polynomial b with x (the polynomial |
|
* x in binary representation is 00000010) is: |
|
* |
|
* b_7x^8 + b_6x^7 + b_5x^6 + b_4x^5 + b_3x^4 + b_2x^3 + b_1x^2 + b_0x^1 |
|
* |
|
* To get GF(b, x) we must reduce that by m(x). If b_7 is 0 (that is the |
|
* most significant bit is 0 in b) then the result is already reduced. If |
|
* it is 1, then we can reduce it by subtracting m(x) via an XOR. |
|
* |
|
* It follows that multiplication by x (00000010 or 0x02) can be implemented |
|
* by performing a left shift followed by a conditional bitwise XOR with |
|
* 0x1b. This operation on bytes is denoted by xtime(). Multiplication by |
|
* higher powers of x can be implemented by repeated application of xtime(). |
|
* |
|
* By adding intermediate results, multiplication by any constant can be |
|
* implemented. For instance: |
|
* |
|
* GF(0x57, 0x13) = 0xfe because: |
|
* |
|
* xtime(b) = (b & 128) ? (b << 1 ^ 0x11b) : (b << 1) |
|
* |
|
* Note: We XOR with 0x11b instead of 0x1b because in javascript our |
|
* datatype for b can be larger than 1 byte, so a left shift will not |
|
* automatically eliminate bits that overflow a byte ... by XOR'ing the |
|
* overflow bit with 1 (the extra one from 0x11b) we zero it out. |
|
* |
|
* GF(0x57, 0x02) = xtime(0x57) = 0xae |
|
* GF(0x57, 0x04) = xtime(0xae) = 0x47 |
|
* GF(0x57, 0x08) = xtime(0x47) = 0x8e |
|
* GF(0x57, 0x10) = xtime(0x8e) = 0x07 |
|
* |
|
* GF(0x57, 0x13) = GF(0x57, (0x01 ^ 0x02 ^ 0x10)) |
|
* |
|
* And by the distributive property (since XOR is addition and GF() is |
|
* multiplication): |
|
* |
|
* = GF(0x57, 0x01) ^ GF(0x57, 0x02) ^ GF(0x57, 0x10) |
|
* = 0x57 ^ 0xae ^ 0x07 |
|
* = 0xfe. |
|
*/ |
|
function initialize() { |
|
init = true; |
|
|
|
/* Populate the Rcon table. These are the values given by |
|
[x^(i-1),{00},{00},{00}] where x^(i-1) are powers of x (and x = 0x02) |
|
in the field of GF(2^8), where i starts at 1. |
|
|
|
rcon[0] = [0x00, 0x00, 0x00, 0x00] |
|
rcon[1] = [0x01, 0x00, 0x00, 0x00] 2^(1-1) = 2^0 = 1 |
|
rcon[2] = [0x02, 0x00, 0x00, 0x00] 2^(2-1) = 2^1 = 2 |
|
... |
|
rcon[9] = [0x1B, 0x00, 0x00, 0x00] 2^(9-1) = 2^8 = 0x1B |
|
rcon[10] = [0x36, 0x00, 0x00, 0x00] 2^(10-1) = 2^9 = 0x36 |
|
|
|
We only store the first byte because it is the only one used. |
|
*/ |
|
rcon = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]; |
|
|
|
// compute xtime table which maps i onto GF(i, 0x02) |
|
var xtime = new Array(256); |
|
for(var i = 0; i < 128; ++i) { |
|
xtime[i] = i << 1; |
|
xtime[i + 128] = (i + 128) << 1 ^ 0x11B; |
|
} |
|
|
|
// compute all other tables |
|
sbox = new Array(256); |
|
isbox = new Array(256); |
|
mix = new Array(4); |
|
imix = new Array(4); |
|
for(var i = 0; i < 4; ++i) { |
|
mix[i] = new Array(256); |
|
imix[i] = new Array(256); |
|
} |
|
var e = 0, ei = 0, e2, e4, e8, sx, sx2, me, ime; |
|
for(var i = 0; i < 256; ++i) { |
|
/* We need to generate the SubBytes() sbox and isbox tables so that |
|
we can perform byte substitutions. This requires us to traverse |
|
all of the elements in GF, find their multiplicative inverses, |
|
and apply to each the following affine transformation: |
|
|
|
bi' = bi ^ b(i + 4) mod 8 ^ b(i + 5) mod 8 ^ b(i + 6) mod 8 ^ |
|
b(i + 7) mod 8 ^ ci |
|
for 0 <= i < 8, where bi is the ith bit of the byte, and ci is the |
|
ith bit of a byte c with the value {63} or {01100011}. |
|
|
|
It is possible to traverse every possible value in a Galois field |
|
using what is referred to as a 'generator'. There are many |
|
generators (128 out of 256): 3,5,6,9,11,82 to name a few. To fully |
|
traverse GF we iterate 255 times, multiplying by our generator |
|
each time. |
|
|
|
On each iteration we can determine the multiplicative inverse for |
|
the current element. |
|
|
|
Suppose there is an element in GF 'e'. For a given generator 'g', |
|
e = g^x. The multiplicative inverse of e is g^(255 - x). It turns |
|
out that if use the inverse of a generator as another generator |
|
it will produce all of the corresponding multiplicative inverses |
|
at the same time. For this reason, we choose 5 as our inverse |
|
generator because it only requires 2 multiplies and 1 add and its |
|
inverse, 82, requires relatively few operations as well. |
|
|
|
In order to apply the affine transformation, the multiplicative |
|
inverse 'ei' of 'e' can be repeatedly XOR'd (4 times) with a |
|
bit-cycling of 'ei'. To do this 'ei' is first stored in 's' and |
|
'x'. Then 's' is left shifted and the high bit of 's' is made the |
|
low bit. The resulting value is stored in 's'. Then 'x' is XOR'd |
|
with 's' and stored in 'x'. On each subsequent iteration the same |
|
operation is performed. When 4 iterations are complete, 'x' is |
|
XOR'd with 'c' (0x63) and the transformed value is stored in 'x'. |
|
For example: |
|
|
|
s = 01000001 |
|
x = 01000001 |
|
|
|
iteration 1: s = 10000010, x ^= s |
|
iteration 2: s = 00000101, x ^= s |
|
iteration 3: s = 00001010, x ^= s |
|
iteration 4: s = 00010100, x ^= s |
|
x ^= 0x63 |
|
|
|
This can be done with a loop where s = (s << 1) | (s >> 7). However, |
|
it can also be done by using a single 16-bit (in this case 32-bit) |
|
number 'sx'. Since XOR is an associative operation, we can set 'sx' |
|
to 'ei' and then XOR it with 'sx' left-shifted 1,2,3, and 4 times. |
|
The most significant bits will flow into the high 8 bit positions |
|
and be correctly XOR'd with one another. All that remains will be |
|
to cycle the high 8 bits by XOR'ing them all with the lower 8 bits |
|
afterwards. |
|
|
|
At the same time we're populating sbox and isbox we can precompute |
|
the multiplication we'll need to do to do MixColumns() later. |
|
*/ |
|
|
|
// apply affine transformation |
|
sx = ei ^ (ei << 1) ^ (ei << 2) ^ (ei << 3) ^ (ei << 4); |
|
sx = (sx >> 8) ^ (sx & 255) ^ 0x63; |
|
|
|
// update tables |
|
sbox[e] = sx; |
|
isbox[sx] = e; |
|
|
|
/* Mixing columns is done using matrix multiplication. The columns |
|
that are to be mixed are each a single word in the current state. |
|
The state has Nb columns (4 columns). Therefore each column is a |
|
4 byte word. So to mix the columns in a single column 'c' where |
|
its rows are r0, r1, r2, and r3, we use the following matrix |
|
multiplication: |
|
|
|
[2 3 1 1]*[r0,c]=[r'0,c] |
|
[1 2 3 1] [r1,c] [r'1,c] |
|
[1 1 2 3] [r2,c] [r'2,c] |
|
[3 1 1 2] [r3,c] [r'3,c] |
|
|
|
r0, r1, r2, and r3 are each 1 byte of one of the words in the |
|
state (a column). To do matrix multiplication for each mixed |
|
column c' we multiply the corresponding row from the left matrix |
|
with the corresponding column from the right matrix. In total, we |
|
get 4 equations: |
|
|
|
r0,c' = 2*r0,c + 3*r1,c + 1*r2,c + 1*r3,c |
|
r1,c' = 1*r0,c + 2*r1,c + 3*r2,c + 1*r3,c |
|
r2,c' = 1*r0,c + 1*r1,c + 2*r2,c + 3*r3,c |
|
r3,c' = 3*r0,c + 1*r1,c + 1*r2,c + 2*r3,c |
|
|
|
As usual, the multiplication is as previously defined and the |
|
addition is XOR. In order to optimize mixing columns we can store |
|
the multiplication results in tables. If you think of the whole |
|
column as a word (it might help to visualize by mentally rotating |
|
the equations above by counterclockwise 90 degrees) then you can |
|
see that it would be useful to map the multiplications performed on |
|
each byte (r0, r1, r2, r3) onto a word as well. For instance, we |
|
could map 2*r0,1*r0,1*r0,3*r0 onto a word by storing 2*r0 in the |
|
highest 8 bits and 3*r0 in the lowest 8 bits (with the other two |
|
respectively in the middle). This means that a table can be |
|
constructed that uses r0 as an index to the word. We can do the |
|
same with r1, r2, and r3, creating a total of 4 tables. |
|
|
|
To construct a full c', we can just look up each byte of c in |
|
their respective tables and XOR the results together. |
|
|
|
Also, to build each table we only have to calculate the word |
|
for 2,1,1,3 for every byte ... which we can do on each iteration |
|
of this loop since we will iterate over every byte. After we have |
|
calculated 2,1,1,3 we can get the results for the other tables |
|
by cycling the byte at the end to the beginning. For instance |
|
we can take the result of table 2,1,1,3 and produce table 3,2,1,1 |
|
by moving the right most byte to the left most position just like |
|
how you can imagine the 3 moved out of 2,1,1,3 and to the front |
|
to produce 3,2,1,1. |
|
|
|
There is another optimization in that the same multiples of |
|
the current element we need in order to advance our generator |
|
to the next iteration can be reused in performing the 2,1,1,3 |
|
calculation. We also calculate the inverse mix column tables, |
|
with e,9,d,b being the inverse of 2,1,1,3. |
|
|
|
When we're done, and we need to actually mix columns, the first |
|
byte of each state word should be put through mix[0] (2,1,1,3), |
|
the second through mix[1] (3,2,1,1) and so forth. Then they should |
|
be XOR'd together to produce the fully mixed column. |
|
*/ |
|
|
|
// calculate mix and imix table values |
|
sx2 = xtime[sx]; |
|
e2 = xtime[e]; |
|
e4 = xtime[e2]; |
|
e8 = xtime[e4]; |
|
me = |
|
(sx2 << 24) ^ // 2 |
|
(sx << 16) ^ // 1 |
|
(sx << 8) ^ // 1 |
|
(sx ^ sx2); // 3 |
|
ime = |
|
(e2 ^ e4 ^ e8) << 24 ^ // E (14) |
|
(e ^ e8) << 16 ^ // 9 |
|
(e ^ e4 ^ e8) << 8 ^ // D (13) |
|
(e ^ e2 ^ e8); // B (11) |
|
// produce each of the mix tables by rotating the 2,1,1,3 value |
|
for(var n = 0; n < 4; ++n) { |
|
mix[n][e] = me; |
|
imix[n][sx] = ime; |
|
// cycle the right most byte to the left most position |
|
// ie: 2,1,1,3 becomes 3,2,1,1 |
|
me = me << 24 | me >>> 8; |
|
ime = ime << 24 | ime >>> 8; |
|
} |
|
|
|
// get next element and inverse |
|
if(e === 0) { |
|
// 1 is the inverse of 1 |
|
e = ei = 1; |
|
} else { |
|
// e = 2e + 2*2*2*(10e)) = multiply e by 82 (chosen generator) |
|
// ei = ei + 2*2*ei = multiply ei by 5 (inverse generator) |
|
e = e2 ^ xtime[xtime[xtime[e2 ^ e8]]]; |
|
ei ^= xtime[xtime[ei]]; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Generates a key schedule using the AES key expansion algorithm. |
|
* |
|
* The AES algorithm takes the Cipher Key, K, and performs a Key Expansion |
|
* routine to generate a key schedule. The Key Expansion generates a total |
|
* of Nb*(Nr + 1) words: the algorithm requires an initial set of Nb words, |
|
* and each of the Nr rounds requires Nb words of key data. The resulting |
|
* key schedule consists of a linear array of 4-byte words, denoted [wi ], |
|
* with i in the range 0 <= i < Nb(Nr + 1). |
|
* |
|
* KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk) |
|
* AES-128 (Nb=4, Nk=4, Nr=10) |
|
* AES-192 (Nb=4, Nk=6, Nr=12) |
|
* AES-256 (Nb=4, Nk=8, Nr=14) |
|
* Note: Nr=Nk+6. |
|
* |
|
* Nb is the number of columns (32-bit words) comprising the State (or |
|
* number of bytes in a block). For AES, Nb=4. |
|
* |
|
* @param key the key to schedule (as an array of 32-bit words). |
|
* @param decrypt true to modify the key schedule to decrypt, false not to. |
|
* |
|
* @return the generated key schedule. |
|
*/ |
|
function _expandKey(key, decrypt) { |
|
// copy the key's words to initialize the key schedule |
|
var w = key.slice(0); |
|
|
|
/* RotWord() will rotate a word, moving the first byte to the last |
|
byte's position (shifting the other bytes left). |
|
|
|
We will be getting the value of Rcon at i / Nk. 'i' will iterate |
|
from Nk to (Nb * Nr+1). Nk = 4 (4 byte key), Nb = 4 (4 words in |
|
a block), Nr = Nk + 6 (10). Therefore 'i' will iterate from |
|
4 to 44 (exclusive). Each time we iterate 4 times, i / Nk will |
|
increase by 1. We use a counter iNk to keep track of this. |
|
*/ |
|
|
|
// go through the rounds expanding the key |
|
var temp, iNk = 1; |
|
var Nk = w.length; |
|
var Nr1 = Nk + 6 + 1; |
|
var end = Nb * Nr1; |
|
for(var i = Nk; i < end; ++i) { |
|
temp = w[i - 1]; |
|
if(i % Nk === 0) { |
|
// temp = SubWord(RotWord(temp)) ^ Rcon[i / Nk] |
|
temp = |
|
sbox[temp >>> 16 & 255] << 24 ^ |
|
sbox[temp >>> 8 & 255] << 16 ^ |
|
sbox[temp & 255] << 8 ^ |
|
sbox[temp >>> 24] ^ (rcon[iNk] << 24); |
|
iNk++; |
|
} else if(Nk > 6 && (i % Nk === 4)) { |
|
// temp = SubWord(temp) |
|
temp = |
|
sbox[temp >>> 24] << 24 ^ |
|
sbox[temp >>> 16 & 255] << 16 ^ |
|
sbox[temp >>> 8 & 255] << 8 ^ |
|
sbox[temp & 255]; |
|
} |
|
w[i] = w[i - Nk] ^ temp; |
|
} |
|
|
|
/* When we are updating a cipher block we always use the code path for |
|
encryption whether we are decrypting or not (to shorten code and |
|
simplify the generation of look up tables). However, because there |
|
are differences in the decryption algorithm, other than just swapping |
|
in different look up tables, we must transform our key schedule to |
|
account for these changes: |
|
|
|
1. The decryption algorithm gets its key rounds in reverse order. |
|
2. The decryption algorithm adds the round key before mixing columns |
|
instead of afterwards. |
|
|
|
We don't need to modify our key schedule to handle the first case, |
|
we can just traverse the key schedule in reverse order when decrypting. |
|
|
|
The second case requires a little work. |
|
|
|
The tables we built for performing rounds will take an input and then |
|
perform SubBytes() and MixColumns() or, for the decrypt version, |
|
InvSubBytes() and InvMixColumns(). But the decrypt algorithm requires |
|
us to AddRoundKey() before InvMixColumns(). This means we'll need to |
|
apply some transformations to the round key to inverse-mix its columns |
|
so they'll be correct for moving AddRoundKey() to after the state has |
|
had its columns inverse-mixed. |
|
|
|
To inverse-mix the columns of the state when we're decrypting we use a |
|
lookup table that will apply InvSubBytes() and InvMixColumns() at the |
|
same time. However, the round key's bytes are not inverse-substituted |
|
in the decryption algorithm. To get around this problem, we can first |
|
substitute the bytes in the round key so that when we apply the |
|
transformation via the InvSubBytes()+InvMixColumns() table, it will |
|
undo our substitution leaving us with the original value that we |
|
want -- and then inverse-mix that value. |
|
|
|
This change will correctly alter our key schedule so that we can XOR |
|
each round key with our already transformed decryption state. This |
|
allows us to use the same code path as the encryption algorithm. |
|
|
|
We make one more change to the decryption key. Since the decryption |
|
algorithm runs in reverse from the encryption algorithm, we reverse |
|
the order of the round keys to avoid having to iterate over the key |
|
schedule backwards when running the encryption algorithm later in |
|
decryption mode. In addition to reversing the order of the round keys, |
|
we also swap each round key's 2nd and 4th rows. See the comments |
|
section where rounds are performed for more details about why this is |
|
done. These changes are done inline with the other substitution |
|
described above. |
|
*/ |
|
if(decrypt) { |
|
var tmp; |
|
var m0 = imix[0]; |
|
var m1 = imix[1]; |
|
var m2 = imix[2]; |
|
var m3 = imix[3]; |
|
var wnew = w.slice(0); |
|
end = w.length; |
|
for(var i = 0, wi = end - Nb; i < end; i += Nb, wi -= Nb) { |
|
// do not sub the first or last round key (round keys are Nb |
|
// words) as no column mixing is performed before they are added, |
|
// but do change the key order |
|
if(i === 0 || i === (end - Nb)) { |
|
wnew[i] = w[wi]; |
|
wnew[i + 1] = w[wi + 3]; |
|
wnew[i + 2] = w[wi + 2]; |
|
wnew[i + 3] = w[wi + 1]; |
|
} else { |
|
// substitute each round key byte because the inverse-mix |
|
// table will inverse-substitute it (effectively cancel the |
|
// substitution because round key bytes aren't sub'd in |
|
// decryption mode) and swap indexes 3 and 1 |
|
for(var n = 0; n < Nb; ++n) { |
|
tmp = w[wi + n]; |
|
wnew[i + (3&-n)] = |
|
m0[sbox[tmp >>> 24]] ^ |
|
m1[sbox[tmp >>> 16 & 255]] ^ |
|
m2[sbox[tmp >>> 8 & 255]] ^ |
|
m3[sbox[tmp & 255]]; |
|
} |
|
} |
|
} |
|
w = wnew; |
|
} |
|
|
|
return w; |
|
} |
|
|
|
/** |
|
* Updates a single block (16 bytes) using AES. The update will either |
|
* encrypt or decrypt the block. |
|
* |
|
* @param w the key schedule. |
|
* @param input the input block (an array of 32-bit words). |
|
* @param output the updated output block. |
|
* @param decrypt true to decrypt the block, false to encrypt it. |
|
*/ |
|
function _updateBlock(w, input, output, decrypt) { |
|
/* |
|
Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)]) |
|
begin |
|
byte state[4,Nb] |
|
state = in |
|
AddRoundKey(state, w[0, Nb-1]) |
|
for round = 1 step 1 to Nr-1 |
|
SubBytes(state) |
|
ShiftRows(state) |
|
MixColumns(state) |
|
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) |
|
end for |
|
SubBytes(state) |
|
ShiftRows(state) |
|
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) |
|
out = state |
|
end |
|
|
|
InvCipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)]) |
|
begin |
|
byte state[4,Nb] |
|
state = in |
|
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) |
|
for round = Nr-1 step -1 downto 1 |
|
InvShiftRows(state) |
|
InvSubBytes(state) |
|
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) |
|
InvMixColumns(state) |
|
end for |
|
InvShiftRows(state) |
|
InvSubBytes(state) |
|
AddRoundKey(state, w[0, Nb-1]) |
|
out = state |
|
end |
|
*/ |
|
|
|
// Encrypt: AddRoundKey(state, w[0, Nb-1]) |
|
// Decrypt: AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) |
|
var Nr = w.length / 4 - 1; |
|
var m0, m1, m2, m3, sub; |
|
if(decrypt) { |
|
m0 = imix[0]; |
|
m1 = imix[1]; |
|
m2 = imix[2]; |
|
m3 = imix[3]; |
|
sub = isbox; |
|
} else { |
|
m0 = mix[0]; |
|
m1 = mix[1]; |
|
m2 = mix[2]; |
|
m3 = mix[3]; |
|
sub = sbox; |
|
} |
|
var a, b, c, d, a2, b2, c2; |
|
a = input[0] ^ w[0]; |
|
b = input[decrypt ? 3 : 1] ^ w[1]; |
|
c = input[2] ^ w[2]; |
|
d = input[decrypt ? 1 : 3] ^ w[3]; |
|
var i = 3; |
|
|
|
/* In order to share code we follow the encryption algorithm when both |
|
encrypting and decrypting. To account for the changes required in the |
|
decryption algorithm, we use different lookup tables when decrypting |
|
and use a modified key schedule to account for the difference in the |
|
order of transformations applied when performing rounds. We also get |
|
key rounds in reverse order (relative to encryption). */ |
|
for(var round = 1; round < Nr; ++round) { |
|
/* As described above, we'll be using table lookups to perform the |
|
column mixing. Each column is stored as a word in the state (the |
|
array 'input' has one column as a word at each index). In order to |
|
mix a column, we perform these transformations on each row in c, |
|
which is 1 byte in each word. The new column for c0 is c'0: |
|
|
|
m0 m1 m2 m3 |
|
r0,c'0 = 2*r0,c0 + 3*r1,c0 + 1*r2,c0 + 1*r3,c0 |
|
r1,c'0 = 1*r0,c0 + 2*r1,c0 + 3*r2,c0 + 1*r3,c0 |
|
r2,c'0 = 1*r0,c0 + 1*r1,c0 + 2*r2,c0 + 3*r3,c0 |
|
r3,c'0 = 3*r0,c0 + 1*r1,c0 + 1*r2,c0 + 2*r3,c0 |
|
|
|
So using mix tables where c0 is a word with r0 being its upper |
|
8 bits and r3 being its lower 8 bits: |
|
|
|
m0[c0 >> 24] will yield this word: [2*r0,1*r0,1*r0,3*r0] |
|
... |
|
m3[c0 & 255] will yield this word: [1*r3,1*r3,3*r3,2*r3] |
|
|
|
Therefore to mix the columns in each word in the state we |
|
do the following (& 255 omitted for brevity): |
|
c'0,r0 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] |
|
c'0,r1 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] |
|
c'0,r2 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] |
|
c'0,r3 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3] |
|
|
|
However, before mixing, the algorithm requires us to perform |
|
ShiftRows(). The ShiftRows() transformation cyclically shifts the |
|
last 3 rows of the state over different offsets. The first row |
|
(r = 0) is not shifted. |
|
|
|
s'_r,c = s_r,(c + shift(r, Nb) mod Nb |
|
for 0 < r < 4 and 0 <= c < Nb and |
|
shift(1, 4) = 1 |
|
shift(2, 4) = 2 |
|
shift(3, 4) = 3. |
|
|
|
This causes the first byte in r = 1 to be moved to the end of |
|
the row, the first 2 bytes in r = 2 to be moved to the end of |
|
the row, the first 3 bytes in r = 3 to be moved to the end of |
|
the row: |
|
|
|
r1: [c0 c1 c2 c3] => [c1 c2 c3 c0] |
|
r2: [c0 c1 c2 c3] [c2 c3 c0 c1] |
|
r3: [c0 c1 c2 c3] [c3 c0 c1 c2] |
|
|
|
We can make these substitutions inline with our column mixing to |
|
generate an updated set of equations to produce each word in the |
|
state (note the columns have changed positions): |
|
|
|
c0 c1 c2 c3 => c0 c1 c2 c3 |
|
c0 c1 c2 c3 c1 c2 c3 c0 (cycled 1 byte) |
|
c0 c1 c2 c3 c2 c3 c0 c1 (cycled 2 bytes) |
|
c0 c1 c2 c3 c3 c0 c1 c2 (cycled 3 bytes) |
|
|
|
Therefore: |
|
|
|
c'0 = 2*r0,c0 + 3*r1,c1 + 1*r2,c2 + 1*r3,c3 |
|
c'0 = 1*r0,c0 + 2*r1,c1 + 3*r2,c2 + 1*r3,c3 |
|
c'0 = 1*r0,c0 + 1*r1,c1 + 2*r2,c2 + 3*r3,c3 |
|
c'0 = 3*r0,c0 + 1*r1,c1 + 1*r2,c2 + 2*r3,c3 |
|
|
|
c'1 = 2*r0,c1 + 3*r1,c2 + 1*r2,c3 + 1*r3,c0 |
|
c'1 = 1*r0,c1 + 2*r1,c2 + 3*r2,c3 + 1*r3,c0 |
|
c'1 = 1*r0,c1 + 1*r1,c2 + 2*r2,c3 + 3*r3,c0 |
|
c'1 = 3*r0,c1 + 1*r1,c2 + 1*r2,c3 + 2*r3,c0 |
|
|
|
... and so forth for c'2 and c'3. The important distinction is |
|
that the columns are cycling, with c0 being used with the m0 |
|
map when calculating c0, but c1 being used with the m0 map when |
|
calculating c1 ... and so forth. |
|
|
|
When performing the inverse we transform the mirror image and |
|
skip the bottom row, instead of the top one, and move upwards: |
|
|
|
c3 c2 c1 c0 => c0 c3 c2 c1 (cycled 3 bytes) *same as encryption |
|
c3 c2 c1 c0 c1 c0 c3 c2 (cycled 2 bytes) |
|
c3 c2 c1 c0 c2 c1 c0 c3 (cycled 1 byte) *same as encryption |
|
c3 c2 c1 c0 c3 c2 c1 c0 |
|
|
|
If you compare the resulting matrices for ShiftRows()+MixColumns() |
|
and for InvShiftRows()+InvMixColumns() the 2nd and 4th columns are |
|
different (in encrypt mode vs. decrypt mode). So in order to use |
|
the same code to handle both encryption and decryption, we will |
|
need to do some mapping. |
|
|
|
If in encryption mode we let a=c0, b=c1, c=c2, d=c3, and r<N> be |
|
a row number in the state, then the resulting matrix in encryption |
|
mode for applying the above transformations would be: |
|
|
|
r1: a b c d |
|
r2: b c d a |
|
r3: c d a b |
|
r4: d a b c |
|
|
|
If we did the same in decryption mode we would get: |
|
|
|
r1: a d c b |
|
r2: b a d c |
|
r3: c b a d |
|
r4: d c b a |
|
|
|
If instead we swap d and b (set b=c3 and d=c1), then we get: |
|
|
|
r1: a b c d |
|
r2: d a b c |
|
r3: c d a b |
|
r4: b c d a |
|
|
|
Now the 1st and 3rd rows are the same as the encryption matrix. All |
|
we need to do then to make the mapping exactly the same is to swap |
|
the 2nd and 4th rows when in decryption mode. To do this without |
|
having to do it on each iteration, we swapped the 2nd and 4th rows |
|
in the decryption key schedule. We also have to do the swap above |
|
when we first pull in the input and when we set the final output. */ |
|
a2 = |
|
m0[a >>> 24] ^ |
|
m1[b >>> 16 & 255] ^ |
|
m2[c >>> 8 & 255] ^ |
|
m3[d & 255] ^ w[++i]; |
|
b2 = |
|
m0[b >>> 24] ^ |
|
m1[c >>> 16 & 255] ^ |
|
m2[d >>> 8 & 255] ^ |
|
m3[a & 255] ^ w[++i]; |
|
c2 = |
|
m0[c >>> 24] ^ |
|
m1[d >>> 16 & 255] ^ |
|
m2[a >>> 8 & 255] ^ |
|
m3[b & 255] ^ w[++i]; |
|
d = |
|
m0[d >>> 24] ^ |
|
m1[a >>> 16 & 255] ^ |
|
m2[b >>> 8 & 255] ^ |
|
m3[c & 255] ^ w[++i]; |
|
a = a2; |
|
b = b2; |
|
c = c2; |
|
} |
|
|
|
/* |
|
Encrypt: |
|
SubBytes(state) |
|
ShiftRows(state) |
|
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) |
|
|
|
Decrypt: |
|
InvShiftRows(state) |
|
InvSubBytes(state) |
|
AddRoundKey(state, w[0, Nb-1]) |
|
*/ |
|
// Note: rows are shifted inline |
|
output[0] = |
|
(sub[a >>> 24] << 24) ^ |
|
(sub[b >>> 16 & 255] << 16) ^ |
|
(sub[c >>> 8 & 255] << 8) ^ |
|
(sub[d & 255]) ^ w[++i]; |
|
output[decrypt ? 3 : 1] = |
|
(sub[b >>> 24] << 24) ^ |
|
(sub[c >>> 16 & 255] << 16) ^ |
|
(sub[d >>> 8 & 255] << 8) ^ |
|
(sub[a & 255]) ^ w[++i]; |
|
output[2] = |
|
(sub[c >>> 24] << 24) ^ |
|
(sub[d >>> 16 & 255] << 16) ^ |
|
(sub[a >>> 8 & 255] << 8) ^ |
|
(sub[b & 255]) ^ w[++i]; |
|
output[decrypt ? 1 : 3] = |
|
(sub[d >>> 24] << 24) ^ |
|
(sub[a >>> 16 & 255] << 16) ^ |
|
(sub[b >>> 8 & 255] << 8) ^ |
|
(sub[c & 255]) ^ w[++i]; |
|
} |
|
|
|
/** |
|
* Deprecated. Instead, use: |
|
* |
|
* forge.cipher.createCipher('AES-<mode>', key); |
|
* forge.cipher.createDecipher('AES-<mode>', key); |
|
* |
|
* Creates a deprecated AES cipher object. This object's mode will default to |
|
* CBC (cipher-block-chaining). |
|
* |
|
* The key and iv may be given as a string of bytes, an array of bytes, a |
|
* byte buffer, or an array of 32-bit words. |
|
* |
|
* @param options the options to use. |
|
* key the symmetric key to use. |
|
* output the buffer to write to. |
|
* decrypt true for decryption, false for encryption. |
|
* mode the cipher mode to use (default: 'CBC'). |
|
* |
|
* @return the cipher. |
|
*/ |
|
function _createCipher(options) { |
|
options = options || {}; |
|
var mode = (options.mode || 'CBC').toUpperCase(); |
|
var algorithm = 'AES-' + mode; |
|
|
|
var cipher; |
|
if(options.decrypt) { |
|
cipher = forge.cipher.createDecipher(algorithm, options.key); |
|
} else { |
|
cipher = forge.cipher.createCipher(algorithm, options.key); |
|
} |
|
|
|
// backwards compatible start API |
|
var start = cipher.start; |
|
cipher.start = function(iv, options) { |
|
// backwards compatibility: support second arg as output buffer |
|
var output = null; |
|
if(options instanceof forge.util.ByteBuffer) { |
|
output = options; |
|
options = {}; |
|
} |
|
options = options || {}; |
|
options.output = output; |
|
options.iv = iv; |
|
start.call(cipher, options); |
|
}; |
|
|
|
return cipher; |
|
}
|
|
|