splitting up decrypter into multiple files
Showing
6 changed files
with
183 additions
and
174 deletions
... | @@ -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 | }; | ... | ... |
src/decrypter/async-stream.js
0 → 100644
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 |
src/decrypter/decrypt.js
0 → 100644
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; |
src/decrypter/decrypter.js
0 → 100644
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 |
src/decrypter/index.js
0 → 100644
-
Please register or sign in to post a comment