@@ -101,13 +101,16 @@ function lengthPrefix(buffer) {
101101}
102102
103103function extractDH ( header , mode ) {
104- if ( ! header . keymap [ header . keyid ] ) {
105- throw new Error ( 'No known DH key for ' + header . keyid ) ;
104+ var key = header . privateKey ;
105+ if ( ! key ) {
106+ if ( ! header . keymap || ! header . keyid || ! header . keymap [ header . keyid ] ) {
107+ throw new Error ( 'No known DH key for ' + header . keyid ) ;
108+ }
109+ key = header . keymap [ header . keyid ] ;
106110 }
107111 if ( ! header . keylabels [ header . keyid ] ) {
108112 throw new Error ( 'No known DH key label for ' + header . keyid ) ;
109113 }
110- var key = header . keymap [ header . keyid ] ;
111114 var senderPubKey , receiverPubKey ;
112115 if ( mode === MODE_ENCRYPT ) {
113116 senderPubKey = key . getPublicKey ( ) ;
@@ -139,7 +142,7 @@ function extractSecretAndContext(header, mode) {
139142 }
140143 } else if ( header . dh ) { // receiver/decrypt
141144 result = extractDH ( header , mode ) ;
142- } else if ( header . keyid ) {
145+ } else if ( typeof header . keyid !== undefined ) {
143146 result . secret = header . keymap [ header . keyid ] ;
144147 }
145148 if ( ! result . secret ) {
@@ -155,41 +158,27 @@ function extractSecretAndContext(header, mode) {
155158 return result ;
156159}
157160
158- function extractSecret ( header , mode ) {
159- if ( header . key ) {
160- if ( header . key . length !== KEY_LENGTH ) {
161- throw new Error ( 'An explicit key must be ' + KEY_LENGTH + ' bytes' ) ;
162- }
163- return keylog ( 'secret key' , header . key ) ;
164- }
165-
166- // We need a key identifier for all the rest.
167- var key = header . keymap && header . keymap [ header . keyid ] ;
168- if ( ! key ) {
169- throw new Error ( 'No saved key (keyid: "' + header . keyid + '")' ) ;
170- }
171-
172- // Simple key
173- if ( ! header . dh ) {
174- return keylog ( 'secret saved' , key ) ;
161+ function webpushSecret ( header , mode ) {
162+ if ( ! header . authSecret ) {
163+ throw new Error ( 'No authentication secret for webpush' ) ;
175164 }
165+ keylog ( 'authsecret' , header . authSecret ) ;
176166
177- // We are doing DH
178- var senderPubKey , receiverPubKey ;
167+ var remotePubKey , senderPubKey , receiverPubKey ;
179168 if ( mode === MODE_ENCRYPT ) {
180- senderPubKey = key . getPublicKey ( ) ;
181- receiverPubKey = header . dh ;
169+ senderPubKey = header . privateKey . getPublicKey ( ) ;
170+ remotePubKey = receiverPubKey = header . dh ;
182171 } else if ( mode === MODE_DECRYPT ) {
183- senderPubKey = header . dh ;
184- receiverPubKey = key . getPublicKey ( ) ;
172+ remotePubKey = senderPubKey = header . keyid ;
173+ receiverPubKey = header . privateKey . getPublicKey ( ) ;
185174 } else {
186175 throw new Error ( 'Unknown mode only ' + MODE_ENCRYPT +
187176 ' and ' + MODE_DECRYPT + ' supported' ) ;
188177 }
189- keylog ( 'authsecret ' , header . authSecret ) ;
178+ keylog ( 'remote pubkey ' , remotePubKey ) ;
190179 return keylog ( 'secret dh' ,
191- HKDF ( header . authSecret || Buffer . from ( '' ) ,
192- key . computeSecret ( header . dh ) ,
180+ HKDF ( header . authSecret ,
181+ header . privateKey . computeSecret ( remotePubKey ) ,
193182 Buffer . concat ( [
194183 Buffer . from ( 'WebPush: info\0' ) ,
195184 receiverPubKey ,
@@ -198,6 +187,26 @@ function extractSecret(header, mode) {
198187 SHA_256_LENGTH ) ) ;
199188}
200189
190+ function extractSecret ( header , mode ) {
191+ if ( header . key ) {
192+ if ( header . key . length !== KEY_LENGTH ) {
193+ throw new Error ( 'An explicit key must be ' + KEY_LENGTH + ' bytes' ) ;
194+ }
195+ return keylog ( 'secret key' , header . key ) ;
196+ }
197+
198+ if ( ! header . privateKey ) {
199+ // Lookup based on keyid
200+ var key = header . keymap && header . keymap [ header . keyid ] ;
201+ if ( ! key ) {
202+ throw new Error ( 'No saved key (keyid: "' + header . keyid + '")' ) ;
203+ }
204+ return key ;
205+ }
206+
207+ return webpushSecret ( header , mode ) ;
208+ }
209+
201210function deriveKeyAndNonce ( header , mode ) {
202211 if ( ! header . salt ) {
203212 throw new Error ( 'must include a salt parameter for ' + header . version ) ;
@@ -234,10 +243,6 @@ function deriveKeyAndNonce(header, mode) {
234243 return result ;
235244}
236245
237- function fillCommonParams ( header , params ) {
238- return header ;
239- }
240-
241246/* Parse command-line arguments. */
242247function parseParams ( params ) {
243248 var header = { } ;
@@ -266,8 +271,13 @@ function parseParams(params) {
266271 if ( params . key ) {
267272 header . key = decode ( params . key ) ;
268273 } else {
269- header . keymap = params . keymap || saved . keymap ;
270- header . keylabels = params . keylabels || saved . keylabels ;
274+ header . privateKey = params . privateKey ;
275+ if ( ! header . privateKey ) {
276+ header . keymap = params . keymap || saved . keymap ;
277+ }
278+ if ( header . version !== 'aes128gcm' ) {
279+ header . keylabels = params . keylabels || saved . keylabels ;
280+ }
271281 if ( params . dh ) {
272282 header . dh = decode ( params . dh ) ;
273283 }
@@ -294,7 +304,7 @@ function readHeader(buffer, header) {
294304 var idsz = buffer . readUIntBE ( 20 , 1 ) ;
295305 header . salt = buffer . slice ( 0 , KEY_LENGTH ) ;
296306 header . rs = buffer . readUIntBE ( KEY_LENGTH , 4 ) ;
297- header . keyid = buffer . slice ( 21 , 21 + idsz ) . toString ( 'utf-8' ) ;
307+ header . keyid = buffer . slice ( 21 , 21 + idsz ) ;
298308 return 21 + idsz ;
299309}
300310
@@ -336,8 +346,12 @@ function decryptRecord(key, counter, buffer, header) {
336346 * If |params.keyid| is specified without |params.dh|, the keyid value is used
337347 * to lookup the |params.keymap| for a buffer containing the key.
338348 *
339- * For ECDH , |params.dh| includes the public key of the sender. The ECDH key
349+ * For version aesgcm and aesgcm128 , |params.dh| includes the public key of the sender. The ECDH key
340350 * pair used to decrypt is looked up using |params.keymap[params.keyid]|.
351+ *
352+ * Version aes128gcm is stricter. The |params.privateKey| includes the private
353+ * key of the receiver. The keyid is extracted from the header and used as the
354+ * ECDH public key of the sender.
341355 */
342356function decrypt ( buffer , params ) {
343357 var header = parseParams ( params ) ;
@@ -410,9 +424,12 @@ function writeHeader(header) {
410424 * If |params.keyid| is specified without |params.dh|, the keyid value is used
411425 * to lookup the |params.keymap| for a buffer containing the key.
412426 *
413- * For Diffie-Hellman, |params.dh| includes the public key of the receiver. The
414- * ECDH key pair used to encrypt is looked up using |params.keymap[params.keyid]|.
415- * Key pairs can be created using |crypto.createECDH()|.
427+ * For Diffie-Hellman (WebPush), |params.dh| includes the public key of the
428+ * receiver. |params.privateKey| is used to establish a shared secret. For
429+ * versions aesgcm and aesgcm128, if a private key is not provided, the ECDH key
430+ * pair used to encrypt is looked up using |params.keymap[params.keyid]|, and
431+ * |params.keymap| defaults to the values saved with saveKey(). Key pairs can
432+ * be created using |crypto.createECDH()|.
416433 */
417434function encrypt ( buffer , params ) {
418435 if ( ! Buffer . isBuffer ( buffer ) ) {
@@ -425,6 +442,10 @@ function encrypt(buffer, params) {
425442
426443 var result ;
427444 if ( header . version === 'aes128gcm' ) {
445+ // Save the DH public key in the header.
446+ if ( header . privateKey && ! header . keyid ) {
447+ header . keyid = header . privateKey . getPublicKey ( ) ;
448+ }
428449 result = writeHeader ( header ) ;
429450 } else {
430451 // No header on other versions
0 commit comments