@@ -52,10 +52,14 @@ function HMAC_hash(key, input) {
5252
5353/* HKDF as defined in RFC5869, using SHA-256 */
5454function HKDF_extract ( salt , ikm ) {
55- return HMAC_hash ( salt , ikm ) ;
55+ keylog ( 'salt' , salt ) ;
56+ keylog ( 'ikm' , ikm ) ;
57+ return keylog ( 'extract' , HMAC_hash ( salt , ikm ) ) ;
5658}
5759
5860function HKDF_expand ( prk , info , l ) {
61+ keylog ( 'prk' , prk ) ;
62+ keylog ( 'info' , info ) ;
5963 var output = new Buffer ( 0 ) ;
6064 var T = new Buffer ( 0 ) ;
6165 info = new Buffer ( info , 'ascii' ) ;
@@ -67,7 +71,7 @@ function HKDF_expand(prk, info, l) {
6771 output = Buffer . concat ( [ output , T ] ) ;
6872 }
6973
70- return output . slice ( 0 , l ) ;
74+ return keylog ( 'expand' , output . slice ( 0 , l ) ) ;
7175}
7276
7377function HKDF ( salt , ikm , info , len ) {
@@ -136,33 +140,83 @@ function extractSecretAndContext(header, mode) {
136140 keylog ( 'secret' , result . secret ) ;
137141 keylog ( 'context' , result . context ) ;
138142 if ( header . authSecret ) {
139- result . secret = HKDF ( base64 . decode ( header . authSecret ) , result . secret ,
143+ result . secret = HKDF ( header . authSecret , result . secret ,
140144 info ( 'auth' , new Buffer ( 0 ) ) , SHA_256_LENGTH ) ;
141145 keylog ( 'authsecret' , result . secret ) ;
142146 }
143147 return result ;
144148}
145149
150+ function extractSecret ( header , mode ) {
151+ if ( header . key ) {
152+ if ( header . key . length !== KEY_LENGTH ) {
153+ throw new Error ( 'An explicit key must be ' + KEY_LENGTH + ' bytes' ) ;
154+ }
155+ return keylog ( 'secret key' , header . key ) ;
156+ }
157+
158+ // We need a key identifier for all the rest.
159+ var key = header . keymap && header . keymap [ header . keyid ] ;
160+ if ( ! key ) {
161+ throw new Error ( 'No saved key (keyid: "' + header . keyid + '")' ) ;
162+ }
163+
164+ // Simple key
165+ if ( ! header . dh ) {
166+ return keylog ( 'secret saved' , key ) ;
167+ }
168+
169+ // We are doing DH
170+ var senderPubKey , receiverPubKey ;
171+ if ( mode === MODE_ENCRYPT ) {
172+ senderPubKey = key . getPublicKey ( ) ;
173+ receiverPubKey = header . dh ;
174+ } else if ( mode === MODE_DECRYPT ) {
175+ senderPubKey = header . dh ;
176+ receiverPubKey = key . getPublicKey ( ) ;
177+ } else {
178+ throw new Error ( 'Unknown mode only ' + MODE_ENCRYPT +
179+ ' and ' + MODE_DECRYPT + ' supported' ) ;
180+ }
181+ keylog ( 'authsecret' , header . authSecret ) ;
182+ return keylog ( 'secret dh' ,
183+ HKDF ( header . authSecret || Buffer . from ( '' ) ,
184+ key . computeSecret ( header . dh ) ,
185+ Buffer . concat ( [
186+ Buffer . from ( 'WebPush: info\0' ) ,
187+ receiverPubKey ,
188+ senderPubKey
189+ ] ) ,
190+ SHA_256_LENGTH ) ) ;
191+ }
192+
146193function deriveKeyAndNonce ( header , mode ) {
147194 if ( ! header . salt ) {
148195 throw new Error ( 'must include a salt parameter for ' + header . type ) ;
149196 }
150- var s = extractSecretAndContext ( header , mode ) ;
151- var prk = HKDF_extract ( header . salt , s . secret ) ;
152197 var keyInfo ;
153198 var nonceInfo ;
199+ var secret ;
154200 if ( header . type === 'aesgcm128' ) {
201+ // really old
155202 keyInfo = 'Content-Encoding: aesgcm128' ;
156203 nonceInfo = 'Content-Encoding: nonce' ;
204+ secret = extractSecretAndContext ( header , mode ) . secret ;
157205 } else if ( header . type === 'aesgcm' ) {
206+ // old
207+ var s = extractSecretAndContext ( header , mode ) ;
158208 keyInfo = info ( 'aesgcm' , s . context ) ;
159209 nonceInfo = info ( 'nonce' , s . context ) ;
210+ secret = s . secret ;
160211 } else if ( header . type === 'aes128gcm' ) {
161- keyInfo = 'Content-Encoding: aesgcm128\0' ;
162- nonceInfo = 'Content-Encoding: nonce\0' ;
212+ // latest
213+ keyInfo = Buffer . from ( 'Content-Encoding: aesgcm128\0' ) ;
214+ nonceInfo = Buffer . from ( 'Content-Encoding: nonce\0' ) ;
215+ secret = extractSecret ( header , mode ) ;
163216 } else {
164217 throw new Error ( 'Unable to set context for mode ' + params . type ) ;
165218 }
219+ var prk = HKDF_extract ( header . salt , secret ) ;
166220 var result = {
167221 key : HKDF_expand ( prk , keyInfo , KEY_LENGTH ) ,
168222 nonce : HKDF_expand ( prk , nonceInfo , NONCE_LENGTH )
@@ -172,57 +226,56 @@ function deriveKeyAndNonce(header, mode) {
172226 return result ;
173227}
174228
175- function determineRecordSize ( rs , type ) {
176- rs = parseInt ( rs , 10 ) ;
177- if ( isNaN ( rs ) ) {
178- return 4096 ;
229+ function fillCommonParams ( header , params ) {
230+ if ( params . key ) {
231+ header . key = base64 . decode ( params . key ) ;
179232 }
180- var padSize = PAD_SIZE [ type ] ;
181- if ( rs <= padSize ) {
182- throw new Error ( 'The rs parameter has to be greater than ' + padSize ) ;
233+ header . keymap = params . keymap ;
234+ if ( params . dh ) {
235+ header . dh = base64 . decode ( params . dh ) ;
183236 }
184- return rs ;
185- }
186-
187- function extractSalt ( salt ) {
188- if ( ! salt ) {
189- throw new Error ( 'A salt is required' ) ;
237+ if ( params . authSecret ) {
238+ header . authSecret = base64 . decode ( params . authSecret ) ;
190239 }
191- salt = base64 . decode ( salt ) ;
192- if ( salt . length !== KEY_LENGTH ) {
193- throw new Error ( 'The salt parameter must be ' + KEY_LENGTH + ' bytes' ) ;
194- }
195- return salt ;
240+ return header ;
196241}
197242
198243/* Used when decrypting aes128gcm to populate the header values. */
199244function readHeader ( buffer , params ) {
200245 var idsz = buffer . readUIntBE ( 20 , 1 ) ;
201- keylog ( 'header' , buffer . slice ( 0 , 21 + idsz ) ) ;
202- return {
246+ var header = {
203247 type : 'aes128gcm' ,
204248 salt : buffer . slice ( 0 , KEY_LENGTH ) ,
205249 rs : buffer . readUIntBE ( KEY_LENGTH , 4 ) ,
206250 keyid : buffer . slice ( 21 , 21 + idsz ) . toString ( 'utf-8' ) ,
207- key : params . key ? base64 . decode ( params . key ) : undefined ,
208- dh : params . dh ? base64 . decode ( params . dh ) : undefined ,
209- authSecret : params . authSecret ? base64 . decode ( params . authSecret ) : undefined ,
210251 headerLength : 21 + idsz
211252 } ;
253+ return fillCommonParams ( header , params ) ;
212254}
213255
214- /* Used when decrypting to populate the header values for aesgcm[128]. */
256+ /* Used when decrypting to populate the header values for aesgcm[128]. Used also
257+ * to parse the |param| argument when encrypting. */
215258function parseParams ( params ) {
216- var type = ( params . padSize === 1 ) ? 'aesgcm128' : 'aesgcm' ;
217- return {
218- type : type ,
219- salt : params . salt ? extractSalt ( params . salt ) : undefined ,
220- rs : determineRecordSize ( params . rs , type ) ,
259+ var header = {
260+ type : ( params . padSize === 1 ) ? 'aesgcm128' : 'aesgcm' ,
221261 keyid : params . keyid ,
222- key : params . key ? base64 . decode ( params . key ) : undefined ,
223- dh : params . dh ? base64 . decode ( params . dh ) : undefined ,
224- authSecret : params . authSecret ? base64 . decode ( params . authSecret ) : undefined
262+ headerLength : 0
225263 } ;
264+ header . rs = parseInt ( params . rs , 10 ) ;
265+ if ( isNaN ( header . rs ) ) {
266+ header . rs = 4096 ;
267+ }
268+ if ( header . rs <= PAD_SIZE [ header . type ] ) {
269+ throw new Error ( 'The rs parameter has to be greater than ' +
270+ PAD_SIZE [ header . type ] ) ;
271+ }
272+ if ( params . salt ) {
273+ header . salt = base64 . decode ( params . salt ) ;
274+ if ( header . salt . length !== KEY_LENGTH ) {
275+ throw new Error ( 'The salt parameter must be ' + KEY_LENGTH + ' bytes' ) ;
276+ }
277+ }
278+ return fillCommonParams ( header , params ) ;
226279}
227280
228281function generateNonce ( base , counter ) {
@@ -261,11 +314,14 @@ function decryptRecord(key, counter, buffer, header) {
261314/**
262315 * Decrypt some bytes. This uses the parameters to determine the key and block
263316 * size, which are described in the draft. Binary values are base64url encoded.
264- * For an explicit key that key is used. For a keyid on its own, the value of
265- * the key is a buffer that is stored with saveKey(). For ECDH, the p256-dh
266- * parameter identifies the public share of the recipient and the keyid is
267- * anECDH key pair (created by crypto.createECDH()) that is stored using
268- * saveKey().
317+ *
318+ * If |params.key| is specified, that value is used as the key.
319+ *
320+ * If |params.keyid| is specified without |params.dh|, the keyid value is used
321+ * to lookup the |params.keymap| for a buffer containing the key.
322+ *
323+ * For ECDH, |params.dh| includes the public key of the sender. The ECDH key
324+ * pair used to decrypt is looked up using |params.keymap[params.keyid]|.
269325 */
270326function decrypt ( buffer , params ) {
271327 var header ;
@@ -322,15 +378,21 @@ function encodeHeader(header) {
322378 }
323379 ints . writeUIntBE ( header . rs , 0 , 4 ) ;
324380 ints . writeUIntBE ( keyid . length , 4 , 1 ) ;
325- return keylog ( 'header' , Buffer . concat ( [ header . salt , ints , keyid ] ) ) ;
381+ return Buffer . concat ( [ header . salt , ints , keyid ] ) ;
326382}
327383
328384/**
329385 * Encrypt some bytes. This uses the parameters to determine the key and block
330- * size, which are described in the draft. Note that for encryption, the
331- * p256-dh parameter identifies the public share of the recipient and the keyid
332- * identifies a local DH key pair (created by crypto.createECDH() or
333- * crypto.createDiffieHellman()).
386+ * size, which are described in the draft.
387+ *
388+ * If |params.key| is specified, that value is used as the key.
389+ *
390+ * If |params.keyid| is specified without |params.dh|, the keyid value is used
391+ * to lookup the |params.keymap| for a buffer containing the key.
392+ *
393+ * For Diffie-Hellman, |params.dh| includes the public key of the receiver. The
394+ * ECDH key pair used to encrypt is looked up using |params.keymap[params.keyid]|.
395+ * Key pairs can be created using |crypto.createECDH()|.
334396 */
335397function encrypt ( buffer , params ) {
336398 if ( ! Buffer . isBuffer ( buffer ) ) {
@@ -346,7 +408,6 @@ function encrypt(buffer, params) {
346408 header . salt = crypto . randomBytes ( KEY_LENGTH ) ;
347409 result = encodeHeader ( header ) ;
348410 }
349- header . rs = determineRecordSize ( params . rs , header . type ) ;
350411
351412 var key = deriveKeyAndNonce ( header , MODE_ENCRYPT ) ;
352413 var start = 0 ;
@@ -374,10 +435,14 @@ function encrypt(buffer, params) {
374435}
375436
376437/**
377- * This function saves a key under the provided identifier. This is used to
378- * save the keys that are used to decrypt and encrypt blobs that are identified
379- * by a 'keyid'. DH or ECDH keys that are used with the 'dh' parameter need to
380- * include a label (included in 'dhLabel') that identifies them.
438+ * This function saves a key under the provided identifier. This is only used
439+ * by aesgcm and aesgcm128 codings. For the aes128gcm coding, use the |keymap|
440+ * parameter to encrypt() and decrypt().
441+ *
442+ * This is used to save the keys that are used to decrypt and encrypt blobs that
443+ * are identified by a 'keyid'. DH or ECDH keys that are used with the 'dh'
444+ * parameter need to include a label (included in 'dhLabel') that identifies
445+ * them.
381446 */
382447function saveKey ( id , key , dhLabel ) {
383448 savedKeys [ id ] = key ;
0 commit comments