decrypter.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /**
  2. * @file decrypter.js
  3. *
  4. * An asynchronous implementation of AES-128 CBC decryption with
  5. * PKCS#7 padding.
  6. */
  7. import AES from './aes';
  8. import AsyncStream from './async-stream';
  9. import {unpad} from 'pkcs7';
  10. /**
  11. * Convert network-order (big-endian) bytes into their little-endian
  12. * representation.
  13. */
  14. const ntoh = function(word) {
  15. return (word << 24) |
  16. ((word & 0xff00) << 8) |
  17. ((word & 0xff0000) >> 8) |
  18. (word >>> 24);
  19. };
  20. /**
  21. * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
  22. *
  23. * @param {Uint8Array} encrypted the encrypted bytes
  24. * @param {Uint32Array} key the bytes of the decryption key
  25. * @param {Uint32Array} initVector the initialization vector (IV) to
  26. * use for the first round of CBC.
  27. * @return {Uint8Array} the decrypted bytes
  28. *
  29. * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
  30. * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
  31. * @see https://tools.ietf.org/html/rfc2315
  32. */
  33. const decrypt = function(encrypted, key, initVector) {
  34. // word-level access to the encrypted bytes
  35. const encrypted32 = new Int32Array(
  36. encrypted.buffer,
  37. encrypted.byteOffset,
  38. encrypted.byteLength >> 2
  39. );
  40. const decipher = new AES(Array.prototype.slice.call(key));
  41. // byte and word-level access for the decrypted output
  42. const decrypted = new Uint8Array(encrypted.byteLength);
  43. const decrypted32 = new Int32Array(decrypted.buffer);
  44. // temporary variables for working with the IV, encrypted, and
  45. // decrypted data
  46. let init0;
  47. let init1;
  48. let init2;
  49. let init3;
  50. let encrypted0;
  51. let encrypted1;
  52. let encrypted2;
  53. let encrypted3;
  54. // iteration variable
  55. let wordIx;
  56. // pull out the words of the IV to ensure we don't modify the
  57. // passed-in reference and easier access
  58. init0 = initVector[0];
  59. init1 = initVector[1];
  60. init2 = initVector[2];
  61. init3 = initVector[3];
  62. // decrypt four word sequences, applying cipher-block chaining (CBC)
  63. // to each decrypted block
  64. for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
  65. // convert big-endian (network order) words into little-endian
  66. // (javascript order)
  67. encrypted0 = ntoh(encrypted32[wordIx]);
  68. encrypted1 = ntoh(encrypted32[wordIx + 1]);
  69. encrypted2 = ntoh(encrypted32[wordIx + 2]);
  70. encrypted3 = ntoh(encrypted32[wordIx + 3]);
  71. // decrypt the block
  72. decipher.decrypt(
  73. encrypted0,
  74. encrypted1,
  75. encrypted2,
  76. encrypted3,
  77. decrypted32,
  78. wordIx
  79. );
  80. // XOR with the IV, and restore network byte-order to obtain the
  81. // plaintext
  82. decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
  83. decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
  84. decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
  85. decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
  86. // setup the IV for the next round
  87. init0 = encrypted0;
  88. init1 = encrypted1;
  89. init2 = encrypted2;
  90. init3 = encrypted3;
  91. }
  92. return decrypted;
  93. };
  94. /**
  95. * The `Decrypter` class that manages decryption of AES
  96. * data through `AsyncStream` objects and the `decrypt`
  97. * function
  98. *
  99. * @param {Uint8Array} encrypted the encrypted bytes
  100. * @param {Uint32Array} key the bytes of the decryption key
  101. * @param {Uint32Array} initVector the initialization vector (IV) to
  102. * @param {Function} done the function to run when done
  103. * @class Decrypter
  104. */
  105. class Decrypter {
  106. constructor(encrypted, key, initVector, done) {
  107. const step = Decrypter.STEP;
  108. const encrypted32 = new Int32Array(encrypted.buffer);
  109. const decrypted = new Uint8Array(encrypted.byteLength);
  110. let i = 0;
  111. this.asyncStream_ = new AsyncStream();
  112. // split up the encryption job and do the individual chunks asynchronously
  113. this.asyncStream_.push(this.decryptChunk_(
  114. encrypted32.subarray(i, i + step),
  115. key,
  116. initVector,
  117. decrypted
  118. ));
  119. for (i = step; i < encrypted32.length; i += step) {
  120. initVector = new Uint32Array([ntoh(encrypted32[i - 4]),
  121. ntoh(encrypted32[i - 3]),
  122. ntoh(encrypted32[i - 2]),
  123. ntoh(encrypted32[i - 1])]);
  124. this.asyncStream_.push(this.decryptChunk_(
  125. encrypted32.subarray(i, i + step),
  126. key,
  127. initVector,
  128. decrypted
  129. ));
  130. }
  131. // invoke the done() callback when everything is finished
  132. this.asyncStream_.push(function() {
  133. // remove pkcs#7 padding from the decrypted bytes
  134. done(null, unpad(decrypted));
  135. });
  136. }
  137. /**
  138. * a getter for step the maximum number of bytes to process at one time
  139. *
  140. * @return {number} the value of step 32000
  141. */
  142. static get STEP() {
  143. // 4 * 8000;
  144. return 32000;
  145. }
  146. /**
  147. * @private
  148. */
  149. decryptChunk_(encrypted, key, initVector, decrypted) {
  150. return function() {
  151. const bytes = decrypt(encrypted, key, initVector);
  152. decrypted.set(bytes, encrypted.byteOffset);
  153. };
  154. }
  155. }
  156. export {
  157. Decrypter,
  158. decrypt
  159. };