Skip to content

Commit 22e7216

Browse files
committed
Merge pull request #141 from marco-c/remove_intermediate_standard
Remove intermediate standard
2 parents ee52762 + b9156f6 commit 22e7216

File tree

2 files changed

+28
-112
lines changed

2 files changed

+28
-112
lines changed

index.js

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,7 @@ function encryptOld(userPublicKey, payload) {
8080
};
8181
}
8282

83-
// Intermediate standard, Firefox 46.
84-
function encryptIntermediate(userPublicKey, userAuth, payload) {
85-
var localCurve = crypto.createECDH('prime256v1');
86-
var localPublicKey = localCurve.generateKeys();
87-
88-
var salt = urlBase64.encode(crypto.randomBytes(16));
89-
90-
ece.saveKey('webpushKey', localCurve, 'P-256');
91-
92-
var cipherText = ece.encrypt(payload, {
93-
keyid: 'webpushKey',
94-
dh: userPublicKey,
95-
salt: salt,
96-
authSecret: userAuth,
97-
padSize: 1,
98-
});
99-
100-
return {
101-
localPublicKey: localPublicKey,
102-
salt: salt,
103-
cipherText: cipherText,
104-
};
105-
}
106-
107-
// New standard, Firefox 47+ and Chrome 50+.
83+
// New standard, Firefox 46+ and Chrome 50+.
10884
function encrypt(userPublicKey, userAuth, payload) {
10985
var localCurve = crypto.createECDH('prime256v1');
11086
var localPublicKey = localCurve.generateKeys();
@@ -159,8 +135,8 @@ function sendNotification(endpoint, params) {
159135
if (userAuth) {
160136
if (typeof userAuth !== 'string') {
161137
throw new Error('userAuth should be a base64-encoded string.');
162-
} else if (urlBase64.decode(userAuth).length < 12) {
163-
throw new Error('userAuth should be at least 12 bytes long');
138+
} else if (urlBase64.decode(userAuth).length < 16) {
139+
throw new Error('userAuth should be at least 16 bytes long');
164140
}
165141
}
166142

@@ -177,47 +153,34 @@ function sendNotification(endpoint, params) {
177153
}
178154
};
179155

180-
var encrypted;
181-
var useCryptoKey = false;
156+
var requestPayload;
182157
if (typeof payload !== 'undefined') {
158+
var encrypted;
183159
var encodingHeader;
184-
160+
var cryptoHeaderName;
185161
if (userAuth) {
186-
useCryptoKey = true;
187-
188-
var userAuthBuf = urlBase64.decode(userAuth);
189-
if (userAuthBuf.length === 16) {
190-
// Use the new standard if userAuth is defined and is 16 bytes long (Firefox 47+ and Chrome 50+).
191-
encrypted = encrypt(userPublicKey, userAuth, new Buffer(payload));
192-
encodingHeader = 'aesgcm';
193-
} else {
194-
// Use the intermediate standard if userAuth is defined and is 12 bytes long (Firefox 46).
195-
encrypted = encryptIntermediate(userPublicKey, userAuth, new Buffer(payload));
196-
encodingHeader = 'aesgcm128';
197-
}
162+
// Use the new standard if userAuth is defined (Firefox 46+ and Chrome 50+).
163+
encrypted = encrypt(userPublicKey, userAuth, new Buffer(payload));
164+
encodingHeader = 'aesgcm';
165+
cryptoHeaderName = 'Crypto-Key';
198166
} else {
199-
// Use the old standard if userAuth isn't defined (Firefox 45).
167+
// Use the old standard if userAuth isn't defined (up to Firefox 45).
200168
encrypted = encryptOld(userPublicKey, new Buffer(payload));
201169
encodingHeader = 'aesgcm128';
170+
cryptoHeaderName = 'Encryption-Key';
202171
}
203172

204173
options.headers = {
205-
'Content-Length': encrypted.cipherText.length,
206174
'Content-Type': 'application/octet-stream',
175+
'Content-Encoding': encodingHeader,
207176
'Encryption': 'keyid=p256dh;salt=' + encrypted.salt,
208177
};
209178

210-
var cryptoHeader = 'keyid=p256dh;dh=' + urlBase64.encode(encrypted.localPublicKey);
179+
options.headers[cryptoHeaderName] = 'keyid=p256dh;dh=' + urlBase64.encode(encrypted.localPublicKey);
211180

212-
if (useCryptoKey) {
213-
options.headers['Crypto-Key'] = cryptoHeader;
214-
} else {
215-
options.headers['Encryption-Key'] = cryptoHeader;
216-
}
217-
options.headers['Content-Encoding'] = encodingHeader;
181+
requestPayload = encrypted.cipherText;
218182
}
219183

220-
var gcmPayload;
221184
if (isGCM) {
222185
if (!gcmAPIKey) {
223186
console.warn('Attempt to send push notification to GCM endpoint, but no GCM key is defined'.bold.red);
@@ -229,19 +192,18 @@ function sendNotification(endpoint, params) {
229192
var gcmObj = {
230193
registration_ids: [ subscriptionId ],
231194
};
232-
if (encrypted) {
233-
gcmObj['raw_data'] = encrypted.cipherText.toString('base64');
195+
if (requestPayload) {
196+
gcmObj['raw_data'] = requestPayload.toString('base64');
234197
}
235-
gcmPayload = JSON.stringify(gcmObj);
198+
requestPayload = JSON.stringify(gcmObj);
236199

237200
options.path = options.path.substring(0, options.path.length - subscriptionId.length - 1);
238201

239202
options.headers['Authorization'] = 'key=' + gcmAPIKey;
240203
options.headers['Content-Type'] = 'application/json';
241-
options.headers['Content-Length'] = gcmPayload.length;
242204
}
243205

244-
if (vapid && !isGCM && (!encrypted || useCryptoKey)) {
206+
if (vapid && !isGCM && (typeof payload === 'undefined' || 'Crypto-Key' in options.headers)) {
245207
// VAPID isn't supported by GCM.
246208
// We also can't use it when there's a payload on Firefox 45, because
247209
// Firefox 45 uses the old standard with Encryption-Key.
@@ -278,6 +240,10 @@ function sendNotification(endpoint, params) {
278240
options.headers['TTL'] = 2419200; // Default TTL is four weeks.
279241
}
280242

243+
if (requestPayload) {
244+
options.headers['Content-Length'] = requestPayload.length;
245+
}
246+
281247
var expectedStatusCode = isGCM ? 200 : 201;
282248
var pushRequest = https.request(options, function(pushResponse) {
283249
var body = "";
@@ -295,10 +261,8 @@ function sendNotification(endpoint, params) {
295261
});
296262
});
297263

298-
if (isGCM) {
299-
pushRequest.write(gcmPayload);
300-
} else if (typeof payload !== 'undefined') {
301-
pushRequest.write(encrypted.cipherText);
264+
if (requestPayload) {
265+
pushRequest.write(requestPayload);
302266
}
303267

304268
pushRequest.end();

test/testSendNotification.js

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,9 @@ suite('sendNotification', function() {
8484
salt: salt,
8585
padSize: 1,
8686
});
87-
} else if (req.headers['content-encoding'] === 'aesgcm128') {
88-
ece.saveKey('webpushKey', userCurve, 'P-256');
87+
} else if (cryptoHeader === 'crypto-key') {
88+
assert.equal(req.headers['content-encoding'], 'aesgcm', 'Content-Encoding header correct');
8989

90-
var decrypted = ece.decrypt(body, {
91-
keyid: 'webpushKey',
92-
dh: appServerPublicKey,
93-
salt: salt,
94-
authSecret: urlBase64.encode(intermediateUserAuth),
95-
padSize: 1,
96-
});
97-
} else if (req.headers['content-encoding'] === 'aesgcm') {
9890
ece.saveKey('webpushKey', userCurve, 'P-256');
9991

10092
var decrypted = ece.decrypt(body, {
@@ -105,7 +97,7 @@ suite('sendNotification', function() {
10597
padSize: 2,
10698
});
10799
} else {
108-
assert(false, 'Invalid crypto header or content-encoding header value');
100+
assert(false, 'Invalid crypto header value');
109101
}
110102
} else {
111103
assert.equal(req.headers[cryptoHeader].indexOf('keyid=p256dh;dh='), 0, 'Encryption-Key header correct');
@@ -202,23 +194,6 @@ suite('sendNotification', function() {
202194
});
203195
});
204196

205-
test('send/receive string (intermediate standard)', function() {
206-
return startServer('hello')
207-
.then(function() {
208-
return webPush.sendNotification('https://127.0.0.1:' + serverPort, {
209-
userPublicKey: urlBase64.encode(userPublicKey),
210-
userAuth: urlBase64.encode(intermediateUserAuth),
211-
payload: 'hello',
212-
});
213-
})
214-
.then(function(body) {
215-
assert(true, 'sendNotification promise resolved');
216-
assert.equal(body, 'ok');
217-
}, function(e) {
218-
assert(false, 'sendNotification promise rejected with: ' + e);
219-
});
220-
});
221-
222197
test('send/receive string (new standard)', function() {
223198
return startServer('hello')
224199
.then(function() {
@@ -550,29 +525,6 @@ suite('sendNotification', function() {
550525
});
551526
});
552527

553-
test('send notification with message (intermediate standard) with vapid', function() {
554-
return startServer('hello', undefined, undefined, undefined, true)
555-
.then(function() {
556-
return webPush.sendNotification('https://127.0.0.1:' + serverPort, {
557-
userPublicKey: urlBase64.encode(userPublicKey),
558-
userAuth: urlBase64.encode(intermediateUserAuth),
559-
payload: 'hello',
560-
vapid: {
561-
audience: 'https://www.mozilla.org/',
562-
subject: 'mailto:[email protected]',
563-
privateKey: vapidKeys.privateKey,
564-
publicKey: vapidKeys.publicKey,
565-
},
566-
});
567-
})
568-
.then(function(body) {
569-
assert(true, 'sendNotification promise resolved');
570-
assert.equal(body, 'ok');
571-
}, function(e) {
572-
assert(false, 'sendNotification promise rejected with ' + e);
573-
});
574-
});
575-
576528
test('send notification with message (new standard) with vapid', function() {
577529
return startServer('hello', undefined, undefined, undefined, true)
578530
.then(function() {

0 commit comments

Comments
 (0)