@@ -6,7 +6,7 @@ var base64 = require('urlsafe-base64');
66var savedKeys = { } ;
77var keyLabels = { } ;
88var AES_GCM = 'aes-128-gcm' ;
9- var PAD_SIZE = 2 ;
9+ var PAD_SIZE = { 'aes128gcm' : 2 , 'aesgcm' : 2 , 'aesgcm128' : 1 } ;
1010var TAG_LENGTH = 16 ;
1111var KEY_LENGTH = 16 ;
1212var NONCE_LENGTH = 12 ;
@@ -62,31 +62,19 @@ function info(base, context) {
6262 return result ;
6363}
6464
65- function extractSalt ( salt ) {
66- if ( ! salt ) {
67- throw new Error ( 'A salt is required' ) ;
68- }
69- salt = base64 . decode ( salt ) ;
70- if ( salt . length !== KEY_LENGTH ) {
71- throw new Error ( 'The salt parameter must be ' + KEY_LENGTH + ' bytes' ) ;
72- }
73- return salt ;
74- }
75-
7665function lengthPrefix ( buffer ) {
77- var b = Buffer . concat ( [ new Buffer ( 2 ) , buffer ] ) ;
66+ var b = Buffer . concat ( [ new Buffer ( 2 ) , buffer ] ) ;
7867 b . writeUIntBE ( buffer . length , 0 , 2 ) ;
7968 return b ;
8069}
8170
82- function extractDH ( keyid , dh , mode ) {
71+ function extractDH ( keyid , share , mode ) {
8372 if ( ! savedKeys [ keyid ] ) {
8473 throw new Error ( 'No known DH key for ' + keyid ) ;
8574 }
8675 if ( ! keyLabels [ keyid ] ) {
8776 throw new Error ( 'No known DH key label for ' + keyid ) ;
8877 }
89- var share = base64 . decode ( dh ) ;
9078 var key = savedKeys [ keyid ] ;
9179 var senderPubKey , receiverPubKey ;
9280 if ( mode === MODE_ENCRYPT ) {
@@ -109,46 +97,51 @@ function extractDH(keyid, dh, mode) {
10997 } ;
11098}
11199
112- function extractSecretAndContext ( params , mode ) {
100+ function extractSecretAndContext ( header , mode ) {
113101 var result = { secret : null , context : new Buffer ( 0 ) } ;
114- if ( params . key ) {
115- result . secret = base64 . decode ( params . key ) ;
102+ if ( header . key ) {
103+ result . secret = header . key ;
116104 if ( result . secret . length !== KEY_LENGTH ) {
117105 throw new Error ( 'An explicit key must be ' + KEY_LENGTH + ' bytes' ) ;
118106 }
119- } else if ( params . dh ) { // receiver/decrypt
120- result = extractDH ( params . keyid , params . dh , mode ) ;
121- } else if ( params . keyid ) {
122- result . secret = savedKeys [ params . keyid ] ;
107+ } else if ( header . dh ) { // receiver/decrypt
108+ result = extractDH ( header . keyid , header . dh , mode ) ;
109+ } else if ( header . keyid ) {
110+ result . secret = savedKeys [ header . keyid ] ;
123111 }
124112 if ( ! result . secret ) {
113+ console . warn ( header ) ;
125114 throw new Error ( 'Unable to determine key' ) ;
126115 }
127116 keylog ( 'secret' , result . secret ) ;
128117 keylog ( 'context' , result . context ) ;
129- if ( params . authSecret ) {
130- result . secret = HKDF ( base64 . decode ( params . authSecret ) , result . secret ,
118+ if ( header . authSecret ) {
119+ result . secret = HKDF ( base64 . decode ( header . authSecret ) , result . secret ,
131120 info ( 'auth' , new Buffer ( 0 ) ) , SHA_256_LENGTH ) ;
132121 keylog ( 'authsecret' , result . secret ) ;
133122 }
134123 return result ;
135124}
136125
137- function deriveKeyAndNonce ( params , mode ) {
138- var padSize = params . padSize || PAD_SIZE ;
139- var salt = extractSalt ( params . salt ) ;
140- var s = extractSecretAndContext ( params , mode ) ;
141- var prk = HKDF_extract ( salt , s . secret ) ;
126+ function deriveKeyAndNonce ( header , mode ) {
127+ if ( ! header . salt ) {
128+ throw new Error ( 'must include a salt parameter for ' + header . type ) ;
129+ }
130+ var s = extractSecretAndContext ( header , mode ) ;
131+ var prk = HKDF_extract ( header . salt , s . secret ) ;
142132 var keyInfo ;
143133 var nonceInfo ;
144- if ( padSize === 1 ) {
134+ if ( header . type === 'aesgcm128' ) {
145135 keyInfo = 'Content-Encoding: aesgcm128' ;
146136 nonceInfo = 'Content-Encoding: nonce' ;
147- } else if ( padSize === 2 ) {
137+ } else if ( header . type === 'aesgcm' ) {
148138 keyInfo = info ( 'aesgcm' , s . context ) ;
149139 nonceInfo = info ( 'nonce' , s . context ) ;
140+ } else if ( header . type === 'aes128gcm' ) {
141+ keyInfo = 'Content-Encoding: aesgcm128\0' ;
142+ nonceInfo = 'Content-Encoding: nonce\0' ;
150143 } else {
151- throw new Error ( 'Unable to set context for padSize ' + params . padSize ) ;
144+ throw new Error ( 'Unable to set context for mode ' + params . type ) ;
152145 }
153146 var result = {
154147 key : HKDF_expand ( prk , keyInfo , KEY_LENGTH ) ,
@@ -159,18 +152,58 @@ function deriveKeyAndNonce(params, mode) {
159152 return result ;
160153}
161154
162- function determineRecordSize ( params ) {
163- var rs = parseInt ( params . rs , 10 ) ;
155+ function determineRecordSize ( rs , type ) {
156+ rs = parseInt ( rs , 10 ) ;
164157 if ( isNaN ( rs ) ) {
165158 return 4096 ;
166159 }
167- var padSize = params . padSize || PAD_SIZE ;
160+ var padSize = PAD_SIZE [ type ] ;
168161 if ( rs <= padSize ) {
169162 throw new Error ( 'The rs parameter has to be greater than ' + padSize ) ;
170163 }
171164 return rs ;
172165}
173166
167+ function extractSalt ( salt ) {
168+ if ( ! salt ) {
169+ throw new Error ( 'A salt is required' ) ;
170+ }
171+ salt = base64 . decode ( salt ) ;
172+ if ( salt . length !== KEY_LENGTH ) {
173+ throw new Error ( 'The salt parameter must be ' + KEY_LENGTH + ' bytes' ) ;
174+ }
175+ return salt ;
176+ }
177+
178+ /* Used when decrypting aes128gcm to populate the header values. */
179+ function readHeader ( params , buffer ) {
180+ var idsz = buffer . readUIntBE ( 20 , 1 ) ;
181+ return {
182+ type : 'aes128gcm' ,
183+ salt : buffer . slice ( 0 , KEY_LENGTH ) ,
184+ rs : buffer . readUIntBE ( KEY_LENGTH , 4 ) ,
185+ keyid : buffer . slice ( 21 , 21 + idsz ) . toString ( 'utf-8' ) ,
186+ key : params . key ? base64 . decode ( params . key ) : undefined ,
187+ dh : params . dh ? base64 . decode ( params . dh ) : undefined ,
188+ authSecret : params . authSecret ? base64 . decode ( params . authSecret ) : undefined
189+ } ;
190+ }
191+
192+ /* Used when decrypting to populate the header values for aesgcm[128]. */
193+ function parseParams ( params ) {
194+ console . warn ( params ) ;
195+ var type = ( params . padSize === 1 ) ? 'aesgcm128' : 'aesgcm' ;
196+ return {
197+ type : type ,
198+ salt : params . salt ? extractSalt ( params . salt ) : undefined ,
199+ rs : determineRecordSize ( params . rs , type ) ,
200+ keyid : params . keyid ,
201+ key : params . key ? base64 . decode ( params . key ) : undefined ,
202+ dh : params . dh ? base64 . decode ( params . dh ) : undefined ,
203+ authSecret : params . authSecret ? base64 . decode ( params . authSecret ) : undefined
204+ } ;
205+ }
206+
174207function generateNonce ( base , counter ) {
175208 var nonce = new Buffer ( base ) ;
176209 var m = nonce . readUIntBE ( nonce . length - 6 , 6 ) ;
@@ -181,17 +214,21 @@ function generateNonce(base, counter) {
181214 return nonce ;
182215}
183216
184- function decryptRecord ( key , counter , buffer , padSize ) {
217+ function decryptRecord ( key , counter , buffer , header ) {
185218 keylog ( 'decrypt' , buffer ) ;
186219 var nonce = generateNonce ( key . nonce , counter ) ;
187220 var gcm = crypto . createDecipheriv ( AES_GCM , key . key , nonce ) ;
188221 gcm . setAuthTag ( buffer . slice ( buffer . length - TAG_LENGTH ) ) ;
189222 var data = gcm . update ( buffer . slice ( 0 , buffer . length - TAG_LENGTH ) ) ;
190223 data = Buffer . concat ( [ data , gcm . final ( ) ] ) ;
191224 keylog ( 'decrypted' , data ) ;
192- padSize = padSize || PAD_SIZE
225+ var padSize = PAD_SIZE [ header . type ] ;
193226 var pad = data . readUIntBE ( 0 , padSize ) ;
194227 if ( pad + padSize > data . length ) {
228+ console . warn ( header ) ;
229+ console . warn ( pad ) ;
230+ console . warn ( padSize ) ;
231+ console . warn ( data . length ) ;
195232 throw new Error ( 'padding exceeds block size' ) ;
196233 }
197234 var padCheck = new Buffer ( pad ) ;
@@ -214,13 +251,19 @@ function decryptRecord(key, counter, buffer, padSize) {
214251 * saveKey().
215252 */
216253function decrypt ( buffer , params ) {
217- var key = deriveKeyAndNonce ( params , MODE_DECRYPT ) ;
218- var rs = determineRecordSize ( params ) ;
254+ var header ;
255+ if ( params . salt ) {
256+ header = parseParams ( params ) ;
257+ } else {
258+ header = readHeader ( buffer , params ) ;
259+ }
260+ var key = deriveKeyAndNonce ( header , MODE_DECRYPT ) ;
261+ buffer = buffer . slice ( header . len ) ;
219262 var start = 0 ;
220263 var result = new Buffer ( 0 ) ;
221264
222265 for ( var i = 0 ; start < buffer . length ; ++ i ) {
223- var end = start + rs + TAG_LENGTH ;
266+ var end = start + header . rs + TAG_LENGTH ;
224267 if ( end === buffer . length ) {
225268 throw new Error ( 'Truncated payload' ) ;
226269 }
@@ -229,7 +272,7 @@ function decrypt(buffer, params) {
229272 throw new Error ( 'Invalid block: too small at ' + i ) ;
230273 }
231274 var block = decryptRecord ( key , i , buffer . slice ( start , end ) ,
232- params . padSize ) ;
275+ header ) ;
233276 result = Buffer . concat ( [ result , block ] ) ;
234277 start = end ;
235278 }
@@ -241,7 +284,6 @@ function encryptRecord(key, counter, buffer, pad, padSize) {
241284 pad = pad || 0 ;
242285 var nonce = generateNonce ( key . nonce , counter ) ;
243286 var gcm = crypto . createCipheriv ( AES_GCM , key . key , nonce ) ;
244- padSize = padSize || PAD_SIZE ;
245287 var padding = new Buffer ( pad + padSize ) ;
246288 padding . fill ( 0 ) ;
247289 padding . writeUIntBE ( pad , 0 , padSize ) ;
@@ -257,6 +299,17 @@ function encryptRecord(key, counter, buffer, pad, padSize) {
257299 return encrypted ;
258300}
259301
302+ function encodeHeader ( header ) {
303+ var ints = new Buffer ( 5 ) ;
304+ var keyid = Buffer . from ( header . keyid || '' ) ;
305+ if ( keyid . length > 255 ) {
306+ throw new Error ( 'keyid is too large' ) ;
307+ }
308+ ints . writeUIntBE ( header . rs , 0 , 4 ) ;
309+ ints . writeUIntBE ( keyid . length , 4 , 1 ) ;
310+ return Buffer . concat ( [ header . salt , ints , keyid ] ) ;
311+ }
312+
260313/**
261314 * Encrypt some bytes. This uses the parameters to determine the key and block
262315 * size, which are described in the draft. Note that for encryption, the
@@ -268,26 +321,36 @@ function encrypt(buffer, params) {
268321 if ( ! Buffer . isBuffer ( buffer ) ) {
269322 throw new Error ( 'buffer argument must be a Buffer' ) ;
270323 }
271- var key = deriveKeyAndNonce ( params , MODE_ENCRYPT ) ;
272- var rs = determineRecordSize ( params ) ;
324+ var result ;
325+ var header ;
326+ header = parseParams ( params ) ;
327+ if ( params . salt ) { // old versions
328+ result = new Buffer ( 0 ) ;
329+ } else {
330+ header . type = 'aes128gcm' ;
331+ header . salt = crypto . randomBytes ( KEY_LENGTH ) ;
332+ result = encodeHeader ( header ) ;
333+ }
334+ header . rs = determineRecordSize ( params . rs , header . type ) ;
335+
336+ var key = deriveKeyAndNonce ( header , MODE_ENCRYPT ) ;
273337 var start = 0 ;
274- var result = new Buffer ( 0 ) ;
275- var padSize = params . padSize || PAD_SIZE ;
338+ var padSize = PAD_SIZE [ header . type ] ;
276339 var pad = isNaN ( parseInt ( params . pad , 10 ) ) ? 0 : parseInt ( params . pad , 10 ) ;
277340
278341 // Note the <= here ensures that we write out a padding-only block at the end
279342 // of a buffer.
280343 for ( var i = 0 ; start <= buffer . length ; ++ i ) {
281344 // Pad so that at least one data byte is in a block.
282345 var recordPad = Math . min ( ( 1 << ( padSize * 8 ) ) - 1 , // maximum padding
283- Math . min ( rs - padSize - 1 , pad ) ) ;
346+ Math . min ( header . rs - padSize - 1 , pad ) ) ;
284347 pad -= recordPad ;
285348
286- var end = Math . min ( start + rs - padSize - recordPad , buffer . length ) ;
349+ var end = Math . min ( start + header . rs - padSize - recordPad , buffer . length ) ;
287350 var block = encryptRecord ( key , i , buffer . slice ( start , end ) ,
288351 recordPad , padSize ) ;
289352 result = Buffer . concat ( [ result , block ] ) ;
290- start += rs - padSize - recordPad ;
353+ start += header . rs - padSize - recordPad ;
291354 }
292355 if ( pad ) {
293356 throw new Error ( 'Unable to pad by requested amount, ' + pad + ' remaining' ) ;
0 commit comments