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.
Showing
2 changed files
with
68 additions
and
41 deletions
... | @@ -52,9 +52,7 @@ var AES, decrypt; | ... | @@ -52,9 +52,7 @@ var AES, decrypt; |
52 | * @param key {Array} The key as an array of 4, 6 or 8 words. | 52 | * @param key {Array} The key as an array of 4, 6 or 8 words. |
53 | */ | 53 | */ |
54 | AES = function (key) { | 54 | AES = function (key) { |
55 | if (!this._tables[0][0][0]) { | ||
56 | this._precompute(); | 55 | this._precompute(); |
57 | } | ||
58 | 56 | ||
59 | var i, j, tmp, | 57 | var i, j, tmp, |
60 | encKey, decKey, | 58 | encKey, decKey, |
... | @@ -212,27 +210,40 @@ AES.prototype = { | ... | @@ -212,27 +210,40 @@ AES.prototype = { |
212 | } | 210 | } |
213 | }; | 211 | }; |
214 | 212 | ||
215 | decrypt = function(encrypted, key) { | 213 | decrypt = function(encrypted, key, initVector) { |
216 | var | 214 | var |
217 | view = new DataView(encrypted.buffer), | 215 | encryptedView = new DataView(encrypted.buffer), |
218 | decrypted = new AES(key) | 216 | platformEndian = new Uint32Array(encrypted.byteLength / 4), |
217 | decipher = new AES(key), | ||
218 | decrypted = new Uint8Array(encrypted.byteLength), | ||
219 | decryptedView = new DataView(decrypted.buffer), | ||
220 | decryptedBlock, | ||
221 | word, byte; | ||
222 | |||
219 | // convert big-endian input to platform byte order for decryption | 223 | // convert big-endian input to platform byte order for decryption |
220 | .decrypt(new Uint32Array([ | 224 | for (byte = 0; byte < encrypted.byteLength; byte += 4) { |
221 | view.getUint32(0), | 225 | platformEndian[byte >>> 2] = encryptedView.getUint32(byte); |
222 | view.getUint32(4), | 226 | } |
223 | view.getUint32(8), | 227 | // decrypt four word sequences, applying cipher-block chaining (CBC) |
224 | view.getUint32(12) | 228 | // to each decrypted block |
225 | ])), | 229 | for (word = 0; word < platformEndian.length; word += 4) { |
226 | bytes = new Uint8Array(encrypted.byteLength); | 230 | // decrypt the block |
227 | 231 | decryptedBlock = decipher.decrypt(platformEndian.subarray(word, word + 4)); | |
228 | // convert platform byte order back to big-endian for unpadding | 232 | |
229 | view = new DataView(bytes.buffer); | 233 | // XOR with the IV, and restore network byte-order to obtain the |
230 | view.setUint32(0, decrypted[0]); | 234 | // plaintext |
231 | view.setUint32(4, decrypted[1]); | 235 | byte = word << 2; |
232 | view.setUint32(8, decrypted[2]); | 236 | decryptedView.setUint32(byte, decryptedBlock[0] ^ initVector[0]); |
233 | view.setUint32(12, decrypted[3]); | 237 | decryptedView.setUint32(byte + 4, decryptedBlock[1] ^ initVector[1]); |
234 | 238 | decryptedView.setUint32(byte + 8, decryptedBlock[2] ^ initVector[2]); | |
235 | return unpad(bytes); | 239 | decryptedView.setUint32(byte + 12, decryptedBlock[3] ^ initVector[3]); |
240 | |||
241 | // setup the IV for the next round | ||
242 | initVector = platformEndian.subarray(word, word + 4); | ||
243 | } | ||
244 | |||
245 | // remove any padding | ||
246 | return unpad(decrypted); | ||
236 | }; | 247 | }; |
237 | 248 | ||
238 | // exports | 249 | // exports | ... | ... |
... | @@ -23,37 +23,53 @@ | ... | @@ -23,37 +23,53 @@ |
23 | 23 | ||
24 | // see docs/hlse.md for instructions on how test data was generated | 24 | // see docs/hlse.md for instructions on how test data was generated |
25 | 25 | ||
26 | var stringFromBytes = function(bytes) { | ||
27 | var result = '', i; | ||
28 | |||
29 | for (i = 0; i < bytes.length; i++) { | ||
30 | result += String.fromCharCode(bytes[i]); | ||
31 | } | ||
32 | return result; | ||
33 | }; | ||
34 | |||
26 | module('Decryption'); | 35 | module('Decryption'); |
27 | 36 | ||
28 | test('decrypts using AES-128 CBC with PKCS7', function() { | 37 | test('decrypts a single AES-128 with PKCS7 block', function() { |
29 | // the string "howdy folks" with key and initialization | ||
30 | // vector | ||
31 | var | 38 | var |
32 | key = [0, 0, 0, 0], | 39 | key = [0, 0, 0, 0], |
33 | initVector = key, | 40 | initVector = key, |
41 | // the string "howdy folks" encrypted | ||
34 | encrypted = new Uint8Array([ | 42 | encrypted = new Uint8Array([ |
35 | 0xce, 0x90, 0x97, 0xd0, | 43 | 0xce, 0x90, 0x97, 0xd0, |
36 | 0x08, 0x46, 0x4d, 0x18, | 44 | 0x08, 0x46, 0x4d, 0x18, |
37 | 0x4f, 0xae, 0x01, 0x1c, | 45 | 0x4f, 0xae, 0x01, 0x1c, |
38 | 0x82, 0xa8, 0xf0, 0x67]), | 46 | 0x82, 0xa8, 0xf0, 0x67]); |
39 | length = 'howdy folks'.length, | ||
40 | plaintext = new Uint8Array(length), | ||
41 | i; | ||
42 | |||
43 | i = length; | ||
44 | while (i--) { | ||
45 | plaintext[i] = 'howdy folks'.charCodeAt(i); | ||
46 | } | ||
47 | 47 | ||
48 | // decrypt works on the sjcl example site | 48 | deepEqual('howdy folks', |
49 | // correct output: [1752135524, 2032166511, 1818981125, 84215045] | 49 | stringFromBytes(videojs.hls.decrypt(encrypted, key, initVector)), |
50 | |||
51 | deepEqual(plaintext, | ||
52 | new Uint8Array(videojs.hls.decrypt(encrypted, key, initVector)), | ||
53 | 'decrypted with a numeric key'); | ||
54 | deepEqual(plaintext, | ||
55 | new Uint8Array(videojs.hls.decrypt(encrypted, key, initVector)), | ||
56 | 'decrypted with a byte array key'); | 50 | 'decrypted with a byte array key'); |
57 | }); | 51 | }); |
58 | 52 | ||
53 | test('decrypts multiple AES-128 blocks with CBC', function() { | ||
54 | var | ||
55 | key = [0, 0, 0, 0], | ||
56 | initVector = key, | ||
57 | // the string "0123456789abcdef01234" encrypted | ||
58 | encrypted = new Uint8Array([ | ||
59 | 0x14, 0xf5, 0xfe, 0x74, | ||
60 | 0x69, 0x66, 0xf2, 0x92, | ||
61 | 0x65, 0x1c, 0x22, 0x88, | ||
62 | 0xbb, 0xff, 0x46, 0x09, | ||
63 | |||
64 | 0x0b, 0xde, 0x5e, 0x71, | ||
65 | 0x77, 0x87, 0xeb, 0x84, | ||
66 | 0xa9, 0x54, 0xc2, 0x45, | ||
67 | 0xe9, 0x4e, 0x29, 0xb3 | ||
68 | ]); | ||
69 | |||
70 | deepEqual('0123456789abcdef01234', | ||
71 | stringFromBytes(videojs.hls.decrypt(encrypted, key, initVector)), | ||
72 | 'decrypted multiple blocks'); | ||
73 | }); | ||
74 | |||
59 | })(window, window.videojs); | 75 | })(window, window.videojs); | ... | ... |
-
Please register or sign in to post a comment