Skip to content

Commit 6b0c0fc

Browse files
authored
Merge pull request #118 from pusher/base64-master-key
Accept master enc key as base64
2 parents 3eab10d + 4ccec3f commit 6b0c0fc

File tree

7 files changed

+116
-18
lines changed

7 files changed

+116
-18
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
[UPGRADED] development dependencies
44

5+
[ADDED] encryptionMasterKeyBase64 constructor parameter to make it easier to use the full range of 32 byte binary values in encryption key
6+
[DEPRECATED] encryptionMasterKey constructor parameter - use encryptionMasterKeyBase64
7+
58
## 3.0.0 (2019-09-26)
69

710
[REMOVED] Support for Node versions < 8

README.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ var pusher = new Pusher({
6767
cluster: 'CLUSTER', // if `host` is present, it will override the `cluster` option.
6868
host: 'HOST', // optional, defaults to api.pusherapp.com
6969
port: PORT, // optional, defaults to 80 for non-TLS connections and 443 for TLS connections
70-
encryptionMasterKey: ENCRYPTION_MASTER_KEY, // a 32 character long key used to derive secrets for end to end encryption (see below!)
70+
encryptionMasterKeyBase64: ENCRYPTION_MASTER_KEY, // a base64 string which encodes 32 bytes, used to derive the per-channel encryption keys (see below!)
7171
});
7272
```
7373

@@ -82,7 +82,7 @@ var pusher = Pusher.forCluster("CLUSTER", {
8282
secret: 'SECRET_KEY',
8383
useTLS: USE_TLS, // optional, defaults to false
8484
port: PORT, // optional, defaults to 80 for non-TLS connections and 443 for TLS connections
85-
encryptionMasterKey: ENCRYPTION_MASTER_KEY, // a 32 character long key used to derive secrets for end to end encryption (see below!)
85+
encryptionMasterKeyBase64: ENCRYPTION_MASTER_KEY, // a base64 string which encodes 32 bytes, used to derive the per-channel encryption keys (see below!)
8686
});
8787
```
8888

@@ -188,15 +188,22 @@ This library supports end-to-end encryption of your private channels. This means
188188

189189
1. You should first set up Private channels. This involves [creating an authentication endpoint on your server](https://pusher.com/docs/authenticating_users).
190190

191-
2. Next, Specify your 32 character `encryption_master_key`. This is secret and you should never share this with anyone. Not even Pusher.
191+
2. Next, generate your 32 byte master encryption key, encode it as base64 and pass it to the Pusher constructor.
192+
193+
This is secret and you should never share this with anyone.
194+
Not even Pusher.
195+
196+
```bash
197+
openssl rand -base64 32
198+
```
192199

193200
```javascript
194201
var pusher = new Pusher({
195202
appId: 'APP_ID',
196203
key: 'APP_KEY',
197204
secret: 'SECRET_KEY',
198205
useTLS: true,
199-
encryptionMasterKey: 'abcdefghijklmnopqrstuvwxyzabcdef',
206+
encryptionMasterKeyBase64: '<KEY GENERATED BY PREVIOUS COMMAND>',
200207
});
201208
```
202209

@@ -230,11 +237,11 @@ Using presence channels is similar to private channels, but you can specify extr
230237

231238
```javascript
232239
var channelData = {
233-
user_id: 'unique_user_id',
234-
user_info: {
235-
name: 'Phil Leggetter'
236-
twitter_id: '@leggetter'
237-
}
240+
user_id: 'unique_user_id',
241+
user_info: {
242+
name: 'Phil Leggetter'
243+
twitter_id: '@leggetter'
244+
}
238245
};
239246
var auth = pusher.authenticate(socketId, channel, channelData);
240247
```
@@ -263,10 +270,10 @@ Params can't include following keys:
263270
The following example provides the signature of the callback and an example of parsing the result:
264271
```javascript
265272
pusher.get({ path: '/channels', params: {} }, function(error, request, response) {
266-
if (response.statusCode === 200) {
267-
var result = JSON.parse(response.body);
268-
var channelsInfo = result.channels;
269-
}
273+
if (response.statusCode === 200) {
274+
var result = JSON.parse(response.body);
275+
var channelsInfo = result.channels;
276+
}
270277
});
271278
```
272279

lib/config.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var Token = require('./token');
2+
var isBase64 = require('is-base64')
23

34
function Config(options) {
45
options = options || {};
@@ -23,15 +24,41 @@ function Config(options) {
2324
this.timeout = options.timeout;
2425
this.keepAlive = options.keepAlive;
2526

27+
// Handle deprecated raw 32 byte string as key
2628
if (options.encryptionMasterKey !== undefined) {
29+
if (options.encryptionMasterKeyBase64 !== undefined) {
30+
throw new Error("Do not specify both encryptionMasterKey and encryptionMasterKeyBase64. " +
31+
"encryptionMasterKey is deprecated, please specify only encryptionMasterKeyBase64.");
32+
}
33+
console.warn("`encryptionMasterKey` option is deprecated in favor of `encryptionMasterKeyBase64`");
2734
if (typeof(options.encryptionMasterKey) !== 'string') {
2835
throw new Error("encryptionMasterKey must be a string");
2936
}
3037
if (options.encryptionMasterKey.length !== 32) {
31-
throw new Error("encryptionMasterKey must be 32 characters long, but the string '" + options.encryptionMasterKey + "' is " + options.encryptionMasterKey.length + " characters long");
38+
throw new Error("encryptionMasterKey must be 32 bytes long, but the string '" +
39+
options.encryptionMasterKey + "' is " + options.encryptionMasterKey.length + " bytes long");
3240
}
41+
3342
this.encryptionMasterKey = options.encryptionMasterKey;
3443
}
44+
45+
// Handle base64 encoded 32 byte key to encourage use of the full range of byte values
46+
if (options.encryptionMasterKeyBase64 !== undefined) {
47+
if (typeof(options.encryptionMasterKeyBase64) !== 'string') {
48+
throw new Error("encryptionMasterKeyBase64 must be a string");
49+
}
50+
if (!isBase64(options.encryptionMasterKeyBase64)) {
51+
throw new Error("encryptionMasterKeyBase64 must be valid base64");
52+
}
53+
54+
var decodedKey = Buffer.from(options.encryptionMasterKeyBase64, "base64").toString("binary");
55+
if (decodedKey.length !== 32) {
56+
throw new Error("encryptionMasterKeyBase64 must decode to 32 bytes, but the string " +
57+
options.encryptionMasterKeyBase64 + "' decodes to " + decodedKey.length + " bytes");
58+
}
59+
60+
this.encryptionMasterKey = decodedKey;
61+
}
3562
}
3663

3764
Config.prototype.prefixPath = function(subPath) {

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"@types/request": "^2.47.1",
2626
"request": "2.88.0",
2727
"tweetnacl": "^1.0.0",
28-
"tweetnacl-util": "^0.15.0"
28+
"tweetnacl-util": "^0.15.0",
29+
"is-base64": "^1.1.0"
2930
},
3031
"devDependencies": {
3132
"expect.js": "=0.3.1",

tests/integration/pusher/constructor.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,55 @@ describe("Pusher", function() {
9191
var pusher = new Pusher({ timeout: 1001 });
9292
expect(pusher.config.timeout).to.equal(1001);
9393
});
94+
95+
it("should support `encryptionMasterKey` of 32 bytes", function() {
96+
var key = "01234567890123456789012345678901"
97+
var pusher = new Pusher({ encryptionMasterKey: key });
98+
expect(pusher.config.encryptionMasterKey).to.equal(key);
99+
});
100+
101+
it("should reject `encryptionMasterKey` of 31 bytes", function() {
102+
var key = "0123456789012345678901234567890"
103+
expect(function () {
104+
var pusher = new Pusher({ encryptionMasterKey: key });
105+
}).to.throwException(/31 bytes/);
106+
});
107+
108+
it("should reject `encryptionMasterKey` of 33 bytes", function() {
109+
var key = "012345678901234567890123456789012"
110+
expect(function () {
111+
var pusher = new Pusher({ encryptionMasterKey: key });
112+
}).to.throwException(/33 bytes/);
113+
});
114+
115+
it("should support `encryptionMasterKeyBase64` which decodes to 32 bytes", function() {
116+
var key = "01234567890123456789012345678901"
117+
var keyBase64 = Buffer.from(key, "binary").toString("base64");
118+
var pusher = new Pusher({ encryptionMasterKeyBase64: keyBase64 });
119+
expect(pusher.config.encryptionMasterKey).to.equal(key);
120+
});
121+
122+
it("should reject `encryptionMasterKeyBase64` which decodes to 31 bytes", function() {
123+
var key = "0123456789012345678901234567890"
124+
var keyBase64 = Buffer.from(key, "binary").toString("base64");
125+
expect(function () {
126+
var pusher = new Pusher({ encryptionMasterKeyBase64: keyBase64 });
127+
}).to.throwException(/31 bytes/);
128+
});
129+
130+
it("should reject `encryptionMasterKeyBase64` which decodes to 33 bytes", function() {
131+
var key = "012345678901234567890123456789012"
132+
var keyBase64 = Buffer.from(key, "binary").toString("base64");
133+
expect(function () {
134+
var pusher = new Pusher({ encryptionMasterKeyBase64: keyBase64 });
135+
}).to.throwException(/33 bytes/);
136+
});
137+
138+
it("should reject `encryptionMasterKeyBase64` which is invalid base64", function() {
139+
var keyBase64 = "aGkgd(GhlcmUK"
140+
expect(function () {
141+
var pusher = new Pusher({ encryptionMasterKeyBase64: keyBase64 });
142+
}).to.throwException(/valid base64/);
143+
});
94144
});
95145
});

tests/integration/pusher/trigger.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,15 @@ describe("Pusher", function() {
360360
describe("Pusher with encryptionMasterKey", function() {
361361
var pusher;
362362

363-
var testMasterKey = "01234567890123456789012345678901";
363+
var testMasterKey = Buffer.from("01234567890123456789012345678901", "binary").toString("base64");
364364

365365
beforeEach(function() {
366-
pusher = new Pusher({ appId: 1234, key: "f00d", secret: "beef", encryptionMasterKey: testMasterKey });
366+
pusher = new Pusher({
367+
appId: 1234,
368+
key: "f00d",
369+
secret: "beef",
370+
encryptionMasterKeyBase64: testMasterKey
371+
});
367372
nock.disableNetConnect();
368373
});
369374

@@ -388,7 +393,7 @@ describe("Pusher with encryptionMasterKey", function() {
388393

389394
pusher.trigger("one", "my_event", { some: "data "}, done);
390395
});
391-
396+
392397
it("should encrypt the body of an event triggered on a private-encrypted- channel", function(done) {
393398
var sentPlaintext = "Hello!";
394399

0 commit comments

Comments
 (0)