68cfe65a by David LaPalomento

Decrypt multiple blocks with cipher block chaining

Generalize the decryption function to use the initialization vector and decrypt multiple four-word sequences using cipher block chaining. Add a utility function to convert ASCII bytes to strings to clean up the test cases a bit.
1 parent fa251c38
......@@ -52,9 +52,7 @@ var AES, decrypt;
* @param key {Array} The key as an array of 4, 6 or 8 words.
*/
AES = function (key) {
if (!this._tables[0][0][0]) {
this._precompute();
}
this._precompute();
var i, j, tmp,
encKey, decKey,
......@@ -212,27 +210,40 @@ AES.prototype = {
}
};
decrypt = function(encrypted, key) {
decrypt = function(encrypted, key, initVector) {
var
view = new DataView(encrypted.buffer),
decrypted = new AES(key)
// convert big-endian input to platform byte order for decryption
.decrypt(new Uint32Array([
view.getUint32(0),
view.getUint32(4),
view.getUint32(8),
view.getUint32(12)
])),
bytes = new Uint8Array(encrypted.byteLength);
encryptedView = new DataView(encrypted.buffer),
platformEndian = new Uint32Array(encrypted.byteLength / 4),
decipher = new AES(key),
decrypted = new Uint8Array(encrypted.byteLength),
decryptedView = new DataView(decrypted.buffer),
decryptedBlock,
word, byte;
// convert big-endian input to platform byte order for decryption
for (byte = 0; byte < encrypted.byteLength; byte += 4) {
platformEndian[byte >>> 2] = encryptedView.getUint32(byte);
}
// decrypt four word sequences, applying cipher-block chaining (CBC)
// to each decrypted block
for (word = 0; word < platformEndian.length; word += 4) {
// decrypt the block
decryptedBlock = decipher.decrypt(platformEndian.subarray(word, word + 4));
// convert platform byte order back to big-endian for unpadding
view = new DataView(bytes.buffer);
view.setUint32(0, decrypted[0]);
view.setUint32(4, decrypted[1]);
view.setUint32(8, decrypted[2]);
view.setUint32(12, decrypted[3]);
// XOR with the IV, and restore network byte-order to obtain the
// plaintext
byte = word << 2;
decryptedView.setUint32(byte, decryptedBlock[0] ^ initVector[0]);
decryptedView.setUint32(byte + 4, decryptedBlock[1] ^ initVector[1]);
decryptedView.setUint32(byte + 8, decryptedBlock[2] ^ initVector[2]);
decryptedView.setUint32(byte + 12, decryptedBlock[3] ^ initVector[3]);
// setup the IV for the next round
initVector = platformEndian.subarray(word, word + 4);
}
return unpad(bytes);
// remove any padding
return unpad(decrypted);
};
// exports
......
......@@ -23,37 +23,53 @@
// see docs/hlse.md for instructions on how test data was generated
var stringFromBytes = function(bytes) {
var result = '', i;
for (i = 0; i < bytes.length; i++) {
result += String.fromCharCode(bytes[i]);
}
return result;
};
module('Decryption');
test('decrypts using AES-128 CBC with PKCS7', function() {
// the string "howdy folks" with key and initialization
// vector
test('decrypts a single AES-128 with PKCS7 block', function() {
var
key = [0, 0, 0, 0],
initVector = key,
// the string "howdy folks" encrypted
encrypted = new Uint8Array([
0xce, 0x90, 0x97, 0xd0,
0x08, 0x46, 0x4d, 0x18,
0x4f, 0xae, 0x01, 0x1c,
0x82, 0xa8, 0xf0, 0x67]),
length = 'howdy folks'.length,
plaintext = new Uint8Array(length),
i;
i = length;
while (i--) {
plaintext[i] = 'howdy folks'.charCodeAt(i);
}
0x82, 0xa8, 0xf0, 0x67]);
deepEqual('howdy folks',
stringFromBytes(videojs.hls.decrypt(encrypted, key, initVector)),
'decrypted with a byte array key');
});
test('decrypts multiple AES-128 blocks with CBC', function() {
var
key = [0, 0, 0, 0],
initVector = key,
// the string "0123456789abcdef01234" encrypted
encrypted = new Uint8Array([
0x14, 0xf5, 0xfe, 0x74,
0x69, 0x66, 0xf2, 0x92,
0x65, 0x1c, 0x22, 0x88,
0xbb, 0xff, 0x46, 0x09,
// decrypt works on the sjcl example site
// correct output: [1752135524, 2032166511, 1818981125, 84215045]
0x0b, 0xde, 0x5e, 0x71,
0x77, 0x87, 0xeb, 0x84,
0xa9, 0x54, 0xc2, 0x45,
0xe9, 0x4e, 0x29, 0xb3
]);
deepEqual(plaintext,
new Uint8Array(videojs.hls.decrypt(encrypted, key, initVector)),
'decrypted with a numeric key');
deepEqual(plaintext,
new Uint8Array(videojs.hls.decrypt(encrypted, key, initVector)),
'decrypted with a byte array key');
deepEqual('0123456789abcdef01234',
stringFromBytes(videojs.hls.decrypt(encrypted, key, initVector)),
'decrypted multiple blocks');
});
})(window, window.videojs);
......