8cf9c6e1 by brandonocasey

splitting up decrypter into multiple files

1 parent 97d703e4
...@@ -35,22 +35,6 @@ ...@@ -35,22 +35,6 @@
35 * are those of the authors and should not be interpreted as representing 35 * are those of the authors and should not be interpreted as representing
36 * official policies, either expressed or implied, of the authors. 36 * official policies, either expressed or implied, of the authors.
37 */ 37 */
38 import Stream from './stream';
39 import {unpad} from 'pkcs7';
40
41 /**
42 * Convert network-order (big-endian) bytes into their little-endian
43 * representation.
44 */
45 const ntoh = function(word) {
46 return (word << 24) |
47 ((word & 0xff00) << 8) |
48 ((word & 0xff0000) >> 8) |
49 (word >>> 24);
50 };
51
52
53 let aesTables;
54 38
55 /** 39 /**
56 * Schedule out an AES key for both encryption and decryption. This 40 * Schedule out an AES key for both encryption and decryption. This
...@@ -59,7 +43,7 @@ let aesTables; ...@@ -59,7 +43,7 @@ let aesTables;
59 * @constructor 43 * @constructor
60 * @param key {Array} The key as an array of 4, 6 or 8 words. 44 * @param key {Array} The key as an array of 4, 6 or 8 words.
61 */ 45 */
62 class AES { 46 export default class AES {
63 constructor(key) { 47 constructor(key) {
64 /** 48 /**
65 * The expanded S-box and inverse S-box tables. These will be computed 49 * The expanded S-box and inverse S-box tables. These will be computed
...@@ -255,161 +239,4 @@ class AES { ...@@ -255,161 +239,4 @@ class AES {
255 a2 = a; a = b; b = c; c = d; d = a2; 239 a2 = a; a = b; b = c; c = d; d = a2;
256 } 240 }
257 } 241 }
258
259 }
260
261 /* eslint-disable max-len */
262 /**
263 * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
264 * @param encrypted {Uint8Array} the encrypted bytes
265 * @param key {Uint32Array} the bytes of the decryption key
266 * @param initVector {Uint32Array} the initialization vector (IV) to
267 * use for the first round of CBC.
268 * @return {Uint8Array} the decrypted bytes
269 *
270 * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
271 * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
272 * @see https://tools.ietf.org/html/rfc2315
273 */
274 /* eslint-enable max-len */
275 export const decrypt = function(encrypted, key, initVector) {
276 // word-level access to the encrypted bytes
277 let encrypted32 = new Int32Array(encrypted.buffer,
278 encrypted.byteOffset,
279 encrypted.byteLength >> 2);
280
281 let decipher = new AES(Array.prototype.slice.call(key));
282
283 // byte and word-level access for the decrypted output
284 let decrypted = new Uint8Array(encrypted.byteLength);
285 let decrypted32 = new Int32Array(decrypted.buffer);
286
287 // temporary variables for working with the IV, encrypted, and
288 // decrypted data
289 let init0;
290 let init1;
291 let init2;
292 let init3;
293 let encrypted0;
294 let encrypted1;
295 let encrypted2;
296 let encrypted3;
297
298 // iteration variable
299 let wordIx;
300
301 // pull out the words of the IV to ensure we don't modify the
302 // passed-in reference and easier access
303 init0 = initVector[0];
304 init1 = initVector[1];
305 init2 = initVector[2];
306 init3 = initVector[3];
307
308 // decrypt four word sequences, applying cipher-block chaining (CBC)
309 // to each decrypted block
310 for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
311 // convert big-endian (network order) words into little-endian
312 // (javascript order)
313 encrypted0 = ntoh(encrypted32[wordIx]);
314 encrypted1 = ntoh(encrypted32[wordIx + 1]);
315 encrypted2 = ntoh(encrypted32[wordIx + 2]);
316 encrypted3 = ntoh(encrypted32[wordIx + 3]);
317
318 // decrypt the block
319 decipher.decrypt(encrypted0,
320 encrypted1,
321 encrypted2,
322 encrypted3,
323 decrypted32,
324 wordIx);
325
326 // XOR with the IV, and restore network byte-order to obtain the
327 // plaintext
328 decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
329 decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
330 decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
331 decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
332
333 // setup the IV for the next round
334 init0 = encrypted0;
335 init1 = encrypted1;
336 init2 = encrypted2;
337 init3 = encrypted3;
338 }
339
340 return decrypted;
341 };
342
343 export class AsyncStream extends Stream {
344 constructor() {
345 super(Stream);
346 this.jobs = [];
347 this.delay = 1;
348 this.timeout_ = null;
349 }
350 processJob_() {
351 this.jobs.shift()();
352 if (this.jobs.length) {
353 this.timeout_ = setTimeout(this.processJob_.bind(this),
354 this.delay);
355 } else {
356 this.timeout_ = null;
357 }
358 }
359 push(job) {
360 this.jobs.push(job);
361 if (!this.timeout_) {
362 this.timeout_ = setTimeout(this.processJob_.bind(this),
363 this.delay);
364 }
365 }
366 } 242 }
367
368 export class Decrypter {
369 constructor(encrypted, key, initVector, done) {
370 let step = Decrypter.STEP;
371 let encrypted32 = new Int32Array(encrypted.buffer);
372 let decrypted = new Uint8Array(encrypted.byteLength);
373 let i = 0;
374
375 this.asyncStream_ = new AsyncStream();
376
377 // split up the encryption job and do the individual chunks asynchronously
378 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
379 key,
380 initVector,
381 decrypted));
382 for (i = step; i < encrypted32.length; i += step) {
383 initVector = new Uint32Array([ntoh(encrypted32[i - 4]),
384 ntoh(encrypted32[i - 3]),
385 ntoh(encrypted32[i - 2]),
386 ntoh(encrypted32[i - 1])]);
387 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
388 key,
389 initVector,
390 decrypted));
391 }
392 // invoke the done() callback when everything is finished
393 this.asyncStream_.push(function() {
394 // remove pkcs#7 padding from the decrypted bytes
395 done(null, unpad(decrypted));
396 });
397 }
398 decryptChunk_(encrypted, key, initVector, decrypted) {
399 return function() {
400 let bytes = decrypt(encrypted, key, initVector);
401
402 decrypted.set(bytes, encrypted.byteOffset);
403 };
404 }
405 }
406
407 // the maximum number of bytes to process at one time
408 // 4 * 8000;
409 Decrypter.STEP = 32000;
410
411 export default {
412 decrypt,
413 Decrypter,
414 AsyncStream
415 };
......
1 import Stream from '../stream';
2
3 export default class AsyncStream extends Stream {
4 constructor() {
5 super(Stream);
6 this.jobs = [];
7 this.delay = 1;
8 this.timeout_ = null;
9 }
10 processJob_() {
11 this.jobs.shift()();
12 if (this.jobs.length) {
13 this.timeout_ = setTimeout(this.processJob_.bind(this),
14 this.delay);
15 } else {
16 this.timeout_ = null;
17 }
18 }
19 push(job) {
20 this.jobs.push(job);
21 if (!this.timeout_) {
22 this.timeout_ = setTimeout(this.processJob_.bind(this),
23 this.delay);
24 }
25 }
26 }
27
1 import ntoh from './ntoh';
2 import AES from './aes';
3
4 /* eslint-disable max-len */
5 /**
6 * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
7 * @param encrypted {Uint8Array} the encrypted bytes
8 * @param key {Uint32Array} the bytes of the decryption key
9 * @param initVector {Uint32Array} the initialization vector (IV) to
10 * use for the first round of CBC.
11 * @return {Uint8Array} the decrypted bytes
12 *
13 * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
14 * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
15 * @see https://tools.ietf.org/html/rfc2315
16 */
17 /* eslint-enable max-len */
18 const decrypt = function(encrypted, key, initVector) {
19 // word-level access to the encrypted bytes
20 let encrypted32 = new Int32Array(encrypted.buffer,
21 encrypted.byteOffset,
22 encrypted.byteLength >> 2);
23
24 let decipher = new AES(Array.prototype.slice.call(key));
25
26 // byte and word-level access for the decrypted output
27 let decrypted = new Uint8Array(encrypted.byteLength);
28 let decrypted32 = new Int32Array(decrypted.buffer);
29
30 // temporary variables for working with the IV, encrypted, and
31 // decrypted data
32 let init0;
33 let init1;
34 let init2;
35 let init3;
36 let encrypted0;
37 let encrypted1;
38 let encrypted2;
39 let encrypted3;
40
41 // iteration variable
42 let wordIx;
43
44 // pull out the words of the IV to ensure we don't modify the
45 // passed-in reference and easier access
46 init0 = initVector[0];
47 init1 = initVector[1];
48 init2 = initVector[2];
49 init3 = initVector[3];
50
51 // decrypt four word sequences, applying cipher-block chaining (CBC)
52 // to each decrypted block
53 for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
54 // convert big-endian (network order) words into little-endian
55 // (javascript order)
56 encrypted0 = ntoh(encrypted32[wordIx]);
57 encrypted1 = ntoh(encrypted32[wordIx + 1]);
58 encrypted2 = ntoh(encrypted32[wordIx + 2]);
59 encrypted3 = ntoh(encrypted32[wordIx + 3]);
60
61 // decrypt the block
62 decipher.decrypt(encrypted0,
63 encrypted1,
64 encrypted2,
65 encrypted3,
66 decrypted32,
67 wordIx);
68
69 // XOR with the IV, and restore network byte-order to obtain the
70 // plaintext
71 decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
72 decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
73 decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
74 decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
75
76 // setup the IV for the next round
77 init0 = encrypted0;
78 init1 = encrypted1;
79 init2 = encrypted2;
80 init3 = encrypted3;
81 }
82
83 return decrypted;
84 };
85
86 export default decrypt;
1 import AsyncStream from './async-stream';
2 import ntoh from './ntoh';
3 import {unpad} from 'pkcs7';
4 import decrypt from './decrypt';
5
6 export default class Decrypter {
7 constructor(encrypted, key, initVector, done) {
8 let step = Decrypter.STEP;
9 let encrypted32 = new Int32Array(encrypted.buffer);
10 let decrypted = new Uint8Array(encrypted.byteLength);
11 let i = 0;
12
13 this.asyncStream_ = new AsyncStream();
14
15 // split up the encryption job and do the individual chunks asynchronously
16 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
17 key,
18 initVector,
19 decrypted));
20 for (i = step; i < encrypted32.length; i += step) {
21 initVector = new Uint32Array([ntoh(encrypted32[i - 4]),
22 ntoh(encrypted32[i - 3]),
23 ntoh(encrypted32[i - 2]),
24 ntoh(encrypted32[i - 1])]);
25 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
26 key,
27 initVector,
28 decrypted));
29 }
30 // invoke the done() callback when everything is finished
31 this.asyncStream_.push(function() {
32 // remove pkcs#7 padding from the decrypted bytes
33 done(null, unpad(decrypted));
34 });
35 }
36 decryptChunk_(encrypted, key, initVector, decrypted) {
37 return function() {
38 let bytes = decrypt(encrypted, key, initVector);
39
40 decrypted.set(bytes, encrypted.byteOffset);
41 };
42 }
43 }
44
45 // the maximum number of bytes to process at one time
46 // 4 * 8000;
47 Decrypter.STEP = 32000;
48
1 import decrypt from './decrypt';
2 import AsyncStream from './async-stream';
3 import Decrypter from './decrypter';
4
5 export default {
6 decrypt,
7 Decrypter,
8 AsyncStream
9 };
1 /**
2 * Convert network-order (big-endian) bytes into their little-endian
3 * representation.
4 */
5 const ntoh = function(word) {
6 return (word << 24) |
7 ((word & 0xff00) << 8) |
8 ((word & 0xff0000) >> 8) |
9 (word >>> 24);
10 };
11
12 export default ntoh;