@@ -25,7 +25,7 @@ var saved = {
2525 keylabels : { }
2626} ;
2727var AES_GCM = 'aes-128-gcm' ;
28- var PAD_SIZE = { 'aes128gcm' : 2 , 'aesgcm' : 2 , 'aesgcm128' : 1 } ;
28+ var PAD_SIZE = { 'aes128gcm' : 1 , 'aesgcm' : 2 , 'aesgcm128' : 1 } ;
2929var TAG_LENGTH = 16 ;
3030var KEY_LENGTH = 16 ;
3131var NONCE_LENGTH = 12 ;
@@ -313,15 +313,8 @@ function readHeader(buffer, header) {
313313 return 21 + idsz ;
314314}
315315
316- function decryptRecord ( key , counter , buffer , header ) {
317- keylog ( 'decrypt' , buffer ) ;
318- var nonce = generateNonce ( key . nonce , counter ) ;
319- var gcm = crypto . createDecipheriv ( AES_GCM , key . key , nonce ) ;
320- gcm . setAuthTag ( buffer . slice ( buffer . length - TAG_LENGTH ) ) ;
321- var data = gcm . update ( buffer . slice ( 0 , buffer . length - TAG_LENGTH ) ) ;
322- data = Buffer . concat ( [ data , gcm . final ( ) ] ) ;
323- keylog ( 'decrypted' , data ) ;
324- var padSize = PAD_SIZE [ header . version ] ;
316+ function unpadLegacy ( data , version ) {
317+ var padSize = PAD_SIZE [ version ] ;
325318 var pad = data . readUIntBE ( 0 , padSize ) ;
326319 if ( pad + padSize > data . length ) {
327320 throw new Error ( 'padding exceeds block size' ) ;
@@ -335,6 +328,40 @@ function decryptRecord(key, counter, buffer, header) {
335328 return data . slice ( padSize + pad ) ;
336329}
337330
331+ function unpad ( data , last ) {
332+ var i = data . length - 1 ;
333+ while ( i > 0 ) {
334+ if ( data [ i ] ) {
335+ if ( last ) {
336+ if ( data [ i ] !== 2 ) {
337+ throw new Error ( 'last record needs to start padding with a 2' ) ;
338+ }
339+ } else {
340+ if ( data [ i ] !== 1 ) {
341+ throw new Error ( 'last record needs to start padding with a 2' ) ;
342+ }
343+ }
344+ return data . slice ( 0 , i ) ;
345+ }
346+ -- i ;
347+ }
348+ throw new Error ( 'all zero plaintext' ) ;
349+ }
350+
351+ function decryptRecord ( key , counter , buffer , header , last ) {
352+ keylog ( 'decrypt' , buffer ) ;
353+ var nonce = generateNonce ( key . nonce , counter ) ;
354+ var gcm = crypto . createDecipheriv ( AES_GCM , key . key , nonce ) ;
355+ gcm . setAuthTag ( buffer . slice ( buffer . length - TAG_LENGTH ) ) ;
356+ var data = gcm . update ( buffer . slice ( 0 , buffer . length - TAG_LENGTH ) ) ;
357+ data = Buffer . concat ( [ data , gcm . final ( ) ] ) ;
358+ keylog ( 'decrypted' , data ) ;
359+ if ( header . version !== 'aes128gcm' ) {
360+ return unpadLegacy ( data , header . version ) ;
361+ }
362+ return unpad ( data , last ) ;
363+ }
364+
338365// TODO: this really should use the node streams stuff
339366
340367/**
@@ -375,38 +402,51 @@ function decrypt(buffer, params) {
375402
376403 for ( var i = 0 ; start < buffer . length ; ++ i ) {
377404 var end = start + chunkSize ;
378- if ( end === buffer . length ) {
405+ if ( header . version !== 'aes128gcm' && end === buffer . length ) {
379406 throw new Error ( 'Truncated payload' ) ;
380407 }
381408 end = Math . min ( end , buffer . length ) ;
382409 if ( end - start <= TAG_LENGTH ) {
383410 throw new Error ( 'Invalid block: too small at ' + i ) ;
384411 }
385412 var block = decryptRecord ( key , i , buffer . slice ( start , end ) ,
386- header ) ;
413+ header , end >= buffer . length ) ;
387414 result = Buffer . concat ( [ result , block ] ) ;
388415 start = end ;
389416 }
390417 return result ;
391418}
392419
393- function encryptRecord ( key , counter , buffer , pad , padSize ) {
420+ function encryptRecord ( key , counter , buffer , pad , header , last ) {
394421 keylog ( 'encrypt' , buffer ) ;
395422 pad = pad || 0 ;
396423 var nonce = generateNonce ( key . nonce , counter ) ;
397424 var gcm = crypto . createCipheriv ( AES_GCM , key . key , nonce ) ;
425+
426+ var ciphertext = [ ] ;
427+ var padSize = PAD_SIZE [ header . version ] ;
398428 var padding = new Buffer ( pad + padSize ) ;
399429 padding . fill ( 0 ) ;
400- padding . writeUIntBE ( pad , 0 , padSize ) ;
401- keylog ( 'padding' , padding ) ;
402- var epadding = gcm . update ( padding ) ;
403- var ebuffer = gcm . update ( buffer ) ;
430+
431+ if ( header . version !== 'aes128gcm' ) {
432+ padding . writeUIntBE ( pad , 0 , padSize ) ;
433+ keylog ( 'padding' , padding ) ;
434+ ciphertext . push ( gcm . update ( padding ) ) ;
435+ ciphertext . push ( gcm . update ( buffer ) ) ;
436+ } else {
437+ ciphertext . push ( gcm . update ( buffer ) ) ;
438+ padding . writeUIntBE ( last ? 2 : 1 , 0 , 1 ) ;
439+ keylog ( 'padding' , padding ) ;
440+ ciphertext . push ( gcm . update ( padding ) ) ;
441+ }
442+
404443 gcm . final ( ) ;
405444 var tag = gcm . getAuthTag ( ) ;
406445 if ( tag . length !== TAG_LENGTH ) {
407446 throw new Error ( 'invalid tag generated' ) ;
408447 }
409- return keylog ( 'encrypted' , Buffer . concat ( [ epadding , ebuffer , tag ] ) ) ;
448+ ciphertext . push ( tag ) ;
449+ return keylog ( 'encrypted' , Buffer . concat ( ciphertext ) ) ;
410450}
411451
412452function writeHeader ( header ) {
@@ -471,19 +511,30 @@ function encrypt(buffer, params) {
471511 }
472512 var pad = isNaN ( parseInt ( params . pad , 10 ) ) ? 0 : parseInt ( params . pad , 10 ) ;
473513
474- // Note the <= here ensures that we write out a padding-only block at the end
475- // of a buffer.
476- for ( var i = 0 ; start <= buffer . length ; ++ i ) {
514+ var counter = 0 ;
515+ var last = false ;
516+ while ( ! last ) {
477517 // Pad so that at least one data byte is in a block.
478- var recordPad = Math . min ( ( 1 << ( padSize * 8 ) ) - 1 , // maximum padding
479- Math . min ( header . rs - overhead - 1 , pad ) ) ;
518+ var recordPad = Math . min ( header . rs - overhead - 1 , pad ) ;
519+ if ( header . version !== 'aes128gcm' ) {
520+ recordPad = Math . min ( ( 1 << ( padSize * 8 ) ) - 1 , recordPad ) ;
521+ }
480522 pad -= recordPad ;
481523
482- var end = Math . min ( start + header . rs - overhead - recordPad , buffer . length ) ;
483- var block = encryptRecord ( key , i , buffer . slice ( start , end ) ,
484- recordPad , padSize ) ;
524+ var end = start + header . rs - overhead - recordPad ;
525+ if ( header . version !== 'aes128gcm' ) {
526+ // The > here ensures that we write out a padding-only block at the end
527+ // of a buffer.
528+ last = end > buffer . length ;
529+ } else {
530+ last = end >= buffer . length && pad <= 0 ;
531+ }
532+ var block = encryptRecord ( key , counter , buffer . slice ( start , end ) ,
533+ recordPad , header , last ) ;
485534 result = Buffer . concat ( [ result , block ] ) ;
486- start += header . rs - overhead - recordPad ;
535+
536+ start = end ;
537+ ++ counter ;
487538 }
488539 if ( pad ) {
489540 throw new Error ( 'Unable to pad by requested amount, ' + pad + ' remaining' ) ;
0 commit comments