@webex/internal-plugin-encryption 3.0.0-beta.4 → 3.0.0-beta.400
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/config.js +0 -9
- package/dist/config.js.map +1 -1
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/encryption.js +25 -74
- package/dist/encryption.js.map +1 -1
- package/dist/ensure-buffer.browser.js +0 -12
- package/dist/ensure-buffer.browser.js.map +1 -1
- package/dist/ensure-buffer.js +5 -12
- package/dist/ensure-buffer.js.map +1 -1
- package/dist/index.js +7 -33
- package/dist/index.js.map +1 -1
- package/dist/kms-batcher.js +7 -30
- package/dist/kms-batcher.js.map +1 -1
- package/dist/kms-certificate-validation.js +24 -90
- package/dist/kms-certificate-validation.js.map +1 -1
- package/dist/kms-dry-error-interceptor.js +1 -23
- package/dist/kms-dry-error-interceptor.js.map +1 -1
- package/dist/kms-errors.js +21 -51
- package/dist/kms-errors.js.map +1 -1
- package/dist/kms.js +88 -218
- package/dist/kms.js.map +1 -1
- package/package.json +15 -15
- package/src/config.js +3 -3
- package/src/constants.js +3 -0
- package/src/encryption.js +74 -57
- package/src/ensure-buffer.browser.js +0 -1
- package/src/ensure-buffer.js +5 -5
- package/src/index.js +120 -96
- package/src/kms-batcher.js +53 -45
- package/src/kms-certificate-validation.js +48 -50
- package/src/kms-dry-error-interceptor.js +8 -4
- package/src/kms-errors.js +47 -16
- package/src/kms.js +219 -212
- package/test/integration/spec/encryption.js +313 -231
- package/test/integration/spec/kms.js +532 -405
- package/test/integration/spec/payload-transfom.js +69 -69
- package/test/unit/spec/encryption.js +21 -18
- package/test/unit/spec/kms-certificate-validation.js +76 -34
- package/test/unit/spec/kms-errors.js +70 -0
- package/test/unit/spec/kms.js +103 -0
package/src/kms.js
CHANGED
|
@@ -29,7 +29,7 @@ const KMS = WebexPlugin.extend({
|
|
|
29
29
|
namespace: 'Encryption',
|
|
30
30
|
|
|
31
31
|
children: {
|
|
32
|
-
batcher: KMSBatcher
|
|
32
|
+
batcher: KMSBatcher,
|
|
33
33
|
},
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -41,9 +41,7 @@ const KMS = WebexPlugin.extend({
|
|
|
41
41
|
* @param {string} options.keyUri
|
|
42
42
|
* @returns {Promise<Key>}
|
|
43
43
|
*/
|
|
44
|
-
bindKey({
|
|
45
|
-
kro, kroUri, key, keyUri
|
|
46
|
-
}) {
|
|
44
|
+
bindKey({kro, kroUri, key, keyUri}) {
|
|
47
45
|
kroUri = kroUri || kro.uri;
|
|
48
46
|
keyUri = keyUri || key.uri;
|
|
49
47
|
|
|
@@ -62,13 +60,12 @@ const KMS = WebexPlugin.extend({
|
|
|
62
60
|
return this.request({
|
|
63
61
|
method: 'update',
|
|
64
62
|
resourceUri: kroUri,
|
|
65
|
-
uri: keyUri
|
|
66
|
-
})
|
|
67
|
-
.
|
|
68
|
-
this.logger.info('kms: bound key to resource');
|
|
63
|
+
uri: keyUri,
|
|
64
|
+
}).then((res) => {
|
|
65
|
+
this.logger.info('kms: bound key to resource');
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
return res.key;
|
|
68
|
+
});
|
|
72
69
|
},
|
|
73
70
|
|
|
74
71
|
/**
|
|
@@ -80,9 +77,7 @@ const KMS = WebexPlugin.extend({
|
|
|
80
77
|
* @param {Array<Keys>} options.keys
|
|
81
78
|
* @returns {Promise<KMSResourceObject>}
|
|
82
79
|
*/
|
|
83
|
-
createResource({
|
|
84
|
-
userIds, keyUris, key, keys
|
|
85
|
-
}) {
|
|
80
|
+
createResource({userIds, keyUris, key, keys}) {
|
|
86
81
|
keyUris = keyUris || [];
|
|
87
82
|
/* istanbul ignore if */
|
|
88
83
|
if (keys) {
|
|
@@ -109,13 +104,12 @@ const KMS = WebexPlugin.extend({
|
|
|
109
104
|
method: 'create',
|
|
110
105
|
uri: '/resources',
|
|
111
106
|
userIds,
|
|
112
|
-
keyUris
|
|
113
|
-
})
|
|
114
|
-
.
|
|
115
|
-
this.logger.info('kms: created resource');
|
|
107
|
+
keyUris,
|
|
108
|
+
}).then((res) => {
|
|
109
|
+
this.logger.info('kms: created resource');
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
return res.resource;
|
|
112
|
+
});
|
|
119
113
|
},
|
|
120
114
|
|
|
121
115
|
/**
|
|
@@ -127,9 +121,7 @@ const KMS = WebexPlugin.extend({
|
|
|
127
121
|
* @param {string} options.kroUri
|
|
128
122
|
* @returns {Promise<KMSAuthorizationObject>}
|
|
129
123
|
*/
|
|
130
|
-
addAuthorization({
|
|
131
|
-
userIds, authIds, kro, kroUri
|
|
132
|
-
}) {
|
|
124
|
+
addAuthorization({userIds, authIds, kro, kroUri}) {
|
|
133
125
|
userIds = userIds || [];
|
|
134
126
|
kroUri = kroUri || kro.uri;
|
|
135
127
|
|
|
@@ -153,13 +145,12 @@ const KMS = WebexPlugin.extend({
|
|
|
153
145
|
method: 'create',
|
|
154
146
|
uri: '/authorizations',
|
|
155
147
|
resourceUri: kroUri,
|
|
156
|
-
userIds
|
|
157
|
-
})
|
|
158
|
-
.
|
|
159
|
-
this.logger.info('kms: added authorization');
|
|
148
|
+
userIds,
|
|
149
|
+
}).then((res) => {
|
|
150
|
+
this.logger.info('kms: added authorization');
|
|
160
151
|
|
|
161
|
-
|
|
162
|
-
|
|
152
|
+
return res.authorizations;
|
|
153
|
+
});
|
|
163
154
|
},
|
|
164
155
|
|
|
165
156
|
/**
|
|
@@ -178,13 +169,12 @@ const KMS = WebexPlugin.extend({
|
|
|
178
169
|
|
|
179
170
|
return this.request({
|
|
180
171
|
method: 'retrieve',
|
|
181
|
-
uri: `${kroUri}/authorizations
|
|
182
|
-
})
|
|
183
|
-
.
|
|
184
|
-
this.logger.info('kms: retrieved authorization list');
|
|
172
|
+
uri: `${kroUri}/authorizations`,
|
|
173
|
+
}).then((res) => {
|
|
174
|
+
this.logger.info('kms: retrieved authorization list');
|
|
185
175
|
|
|
186
|
-
|
|
187
|
-
|
|
176
|
+
return res.authorizations;
|
|
177
|
+
});
|
|
188
178
|
},
|
|
189
179
|
|
|
190
180
|
/**
|
|
@@ -196,9 +186,7 @@ const KMS = WebexPlugin.extend({
|
|
|
196
186
|
* @param {string} options.kroUri
|
|
197
187
|
* @returns {Promise<KMSAuthorizationObject>}
|
|
198
188
|
*/
|
|
199
|
-
removeAuthorization({
|
|
200
|
-
authId, userId, kro, kroUri
|
|
201
|
-
}) {
|
|
189
|
+
removeAuthorization({authId, userId, kro, kroUri}) {
|
|
202
190
|
authId = authId || userId;
|
|
203
191
|
kroUri = kroUri || kro.uri;
|
|
204
192
|
|
|
@@ -216,13 +204,12 @@ const KMS = WebexPlugin.extend({
|
|
|
216
204
|
|
|
217
205
|
return this.request({
|
|
218
206
|
method: 'delete',
|
|
219
|
-
uri: `${kroUri}/authorizations?${querystring.stringify({authId})}
|
|
220
|
-
})
|
|
221
|
-
.
|
|
222
|
-
this.logger.info('kms: removed authorization');
|
|
207
|
+
uri: `${kroUri}/authorizations?${querystring.stringify({authId})}`,
|
|
208
|
+
}).then((res) => {
|
|
209
|
+
this.logger.info('kms: removed authorization');
|
|
223
210
|
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
return res.authorizations;
|
|
212
|
+
});
|
|
226
213
|
},
|
|
227
214
|
|
|
228
215
|
/**
|
|
@@ -242,21 +229,20 @@ const KMS = WebexPlugin.extend({
|
|
|
242
229
|
return this.request({
|
|
243
230
|
method: 'create',
|
|
244
231
|
uri: '/keys',
|
|
245
|
-
count
|
|
246
|
-
})
|
|
247
|
-
.
|
|
248
|
-
this.logger.info('kms: received unbound keys');
|
|
232
|
+
count,
|
|
233
|
+
}).then((res) => {
|
|
234
|
+
this.logger.info('kms: received unbound keys');
|
|
249
235
|
|
|
250
|
-
|
|
251
|
-
|
|
236
|
+
return Promise.all(res.keys.map(this.asKey));
|
|
237
|
+
});
|
|
252
238
|
},
|
|
253
239
|
|
|
254
240
|
/**
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
241
|
+
* @typedef {Object} FetchPublicKeyResponse
|
|
242
|
+
* @property {number} status 200,400(Bad Request: Request payload missing info),404(Not Found: HSM Public Key not found),501(Not Implemented: This KMS does not support BYOK),502(Bad Gateway: KMS could not communicate with HSM)
|
|
243
|
+
* @property {UUID} requestId this is should be unique, used for debug.
|
|
244
|
+
* @property {string} publicKey
|
|
245
|
+
*/
|
|
260
246
|
/**
|
|
261
247
|
* get public key from kms
|
|
262
248
|
* @param {Object} options
|
|
@@ -269,38 +255,38 @@ const KMS = WebexPlugin.extend({
|
|
|
269
255
|
return this.request({
|
|
270
256
|
method: 'retrieve',
|
|
271
257
|
uri: '/publicKey',
|
|
272
|
-
assignedOrgId
|
|
273
|
-
})
|
|
274
|
-
.
|
|
275
|
-
this.logger.info('kms: received public key');
|
|
258
|
+
assignedOrgId,
|
|
259
|
+
}).then((res) => {
|
|
260
|
+
this.logger.info('kms: received public key');
|
|
276
261
|
|
|
277
|
-
|
|
278
|
-
|
|
262
|
+
return res.publicKey;
|
|
263
|
+
});
|
|
279
264
|
},
|
|
280
265
|
|
|
281
266
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
267
|
+
* @typedef {Object} UploadCmkResponse
|
|
268
|
+
* @property {number} status
|
|
269
|
+
* @property {UUID} requestId
|
|
270
|
+
* @property {string} uri
|
|
271
|
+
* @property {string} keysState
|
|
272
|
+
*/
|
|
288
273
|
/**
|
|
289
274
|
* upload master key for one org.
|
|
290
275
|
* @param {Object} options
|
|
291
276
|
* @param {UUID} options.assignedOrgId the orgId
|
|
292
277
|
* @param {string} options.customerMasterKey the master key
|
|
278
|
+
* @param {boolean} options.awsKms enable amazon aws keys
|
|
293
279
|
* @returns {Promise.<UploadCmkResponse>} response of upload CMK api
|
|
294
280
|
*/
|
|
295
|
-
uploadCustomerMasterKey({assignedOrgId, customerMasterKey}) {
|
|
281
|
+
uploadCustomerMasterKey({assignedOrgId, customerMasterKey, awsKms = false}) {
|
|
296
282
|
this.logger.info('kms: upload customer master key for byok');
|
|
297
283
|
|
|
298
284
|
return this.request({
|
|
299
285
|
method: 'create',
|
|
300
|
-
uri: '/cmk',
|
|
286
|
+
uri: awsKms ? '/awsKmsCmk' : '/cmk',
|
|
301
287
|
assignedOrgId,
|
|
302
288
|
customerMasterKey,
|
|
303
|
-
requestId: uuid.v4()
|
|
289
|
+
requestId: uuid.v4(),
|
|
304
290
|
}).then((res) => {
|
|
305
291
|
this.logger.info('kms: finish to upload customer master key');
|
|
306
292
|
|
|
@@ -312,16 +298,17 @@ const KMS = WebexPlugin.extend({
|
|
|
312
298
|
* get all customer master keys for one org.
|
|
313
299
|
* @param {Object} options
|
|
314
300
|
* @param {UUID} options.assignedOrgId the orgId
|
|
301
|
+
* @param {boolean} options.awsKms enable amazon aws keys
|
|
315
302
|
* @returns {Promise.<ActivateCmkResponse>} response of list CMKs api
|
|
316
303
|
*/
|
|
317
|
-
listAllCustomerMasterKey({assignedOrgId}) {
|
|
304
|
+
listAllCustomerMasterKey({assignedOrgId, awsKms = false}) {
|
|
318
305
|
this.logger.info('kms: get all customer master keys for byok');
|
|
319
306
|
|
|
320
307
|
return this.request({
|
|
321
308
|
method: 'retrieve',
|
|
322
|
-
uri: '/cmk',
|
|
309
|
+
uri: awsKms ? '/awsKmsCmk' : '/cmk',
|
|
323
310
|
assignedOrgId,
|
|
324
|
-
requestId: uuid.v4()
|
|
311
|
+
requestId: uuid.v4(),
|
|
325
312
|
}).then((res) => {
|
|
326
313
|
this.logger.info('kms: finish to get all customer master keys');
|
|
327
314
|
|
|
@@ -330,11 +317,11 @@ const KMS = WebexPlugin.extend({
|
|
|
330
317
|
},
|
|
331
318
|
|
|
332
319
|
/**
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
320
|
+
* @typedef {Object} ActivateCmkResponse
|
|
321
|
+
* @property {number} status
|
|
322
|
+
* @property {UUID} requestId
|
|
323
|
+
* @property {Array<CMK>} customerMasterKeys
|
|
324
|
+
*/
|
|
338
325
|
/**
|
|
339
326
|
*
|
|
340
327
|
* @typedef {Object} CMK
|
|
@@ -364,7 +351,7 @@ const KMS = WebexPlugin.extend({
|
|
|
364
351
|
uri: keyId,
|
|
365
352
|
keyState,
|
|
366
353
|
assignedOrgId,
|
|
367
|
-
requestId: uuid.v4()
|
|
354
|
+
requestId: uuid.v4(),
|
|
368
355
|
}).then((res) => {
|
|
369
356
|
this.logger.info('kms: finish to change the customer master key state to {}', keyState);
|
|
370
357
|
|
|
@@ -376,16 +363,17 @@ const KMS = WebexPlugin.extend({
|
|
|
376
363
|
* this is for test case. it will delete all CMKs, no matter what their status is. This is mainly for test purpose
|
|
377
364
|
* @param {Object} options
|
|
378
365
|
* @param {UUID} options.assignedOrgId the orgId
|
|
366
|
+
* @param {boolean} options.awsKms enable amazon aws keys
|
|
379
367
|
* @returns {Promise.<{status, requestId}>}
|
|
380
368
|
*/
|
|
381
|
-
deleteAllCustomerMasterKeys({assignedOrgId}) {
|
|
369
|
+
deleteAllCustomerMasterKeys({assignedOrgId, awsKms = false}) {
|
|
382
370
|
this.logger.info('kms: delete all customer master keys at the same time');
|
|
383
371
|
|
|
384
372
|
return this.request({
|
|
385
373
|
method: 'delete',
|
|
386
|
-
uri: '/cmk',
|
|
374
|
+
uri: awsKms ? '/awsKmsCmk' : '/cmk',
|
|
387
375
|
assignedOrgId,
|
|
388
|
-
requestId: uuid.v4()
|
|
376
|
+
requestId: uuid.v4(),
|
|
389
377
|
}).then((res) => {
|
|
390
378
|
this.logger.info('kms: finish to delete all customer master keys');
|
|
391
379
|
|
|
@@ -407,7 +395,7 @@ const KMS = WebexPlugin.extend({
|
|
|
407
395
|
uri: 'default',
|
|
408
396
|
keyState: 'ACTIVE',
|
|
409
397
|
assignedOrgId,
|
|
410
|
-
requestId: uuid.v4()
|
|
398
|
+
requestId: uuid.v4(),
|
|
411
399
|
}).then((res) => {
|
|
412
400
|
this.logger.info('kms: finish to return to global master key');
|
|
413
401
|
|
|
@@ -427,7 +415,7 @@ const KMS = WebexPlugin.extend({
|
|
|
427
415
|
// request. as such, we need the batcher to group requests, but one flight to
|
|
428
416
|
// make sure we don't make the same request multiple times.
|
|
429
417
|
@oneFlight({
|
|
430
|
-
keyFactory: ({uri, onBehalfOf}) => `${uri}/${onBehalfOf}
|
|
418
|
+
keyFactory: ({uri, onBehalfOf}) => `${uri}/${onBehalfOf}`,
|
|
431
419
|
})
|
|
432
420
|
fetchKey({uri, onBehalfOf}) {
|
|
433
421
|
/* istanbul ignore if */
|
|
@@ -437,15 +425,17 @@ const KMS = WebexPlugin.extend({
|
|
|
437
425
|
|
|
438
426
|
this.logger.info('kms: fetching key');
|
|
439
427
|
|
|
440
|
-
return this.request(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
428
|
+
return this.request(
|
|
429
|
+
{
|
|
430
|
+
method: 'retrieve',
|
|
431
|
+
uri,
|
|
432
|
+
},
|
|
433
|
+
{onBehalfOf}
|
|
434
|
+
).then((res) => {
|
|
435
|
+
this.logger.info('kms: fetched key');
|
|
446
436
|
|
|
447
|
-
|
|
448
|
-
|
|
437
|
+
return this.asKey(res.key);
|
|
438
|
+
});
|
|
449
439
|
},
|
|
450
440
|
|
|
451
441
|
/**
|
|
@@ -455,7 +445,7 @@ const KMS = WebexPlugin.extend({
|
|
|
455
445
|
ping() {
|
|
456
446
|
return this.request({
|
|
457
447
|
method: 'update',
|
|
458
|
-
uri: '/ping'
|
|
448
|
+
uri: '/ping',
|
|
459
449
|
});
|
|
460
450
|
},
|
|
461
451
|
|
|
@@ -465,12 +455,11 @@ const KMS = WebexPlugin.extend({
|
|
|
465
455
|
* @returns {Promise<Key>}
|
|
466
456
|
*/
|
|
467
457
|
asKey(key) {
|
|
468
|
-
return jose.JWK.asKey(key.jwk)
|
|
469
|
-
.
|
|
470
|
-
key.jwk = jwk;
|
|
458
|
+
return jose.JWK.asKey(key.jwk).then((jwk) => {
|
|
459
|
+
key.jwk = jwk;
|
|
471
460
|
|
|
472
|
-
|
|
473
|
-
|
|
461
|
+
return key;
|
|
462
|
+
});
|
|
474
463
|
},
|
|
475
464
|
|
|
476
465
|
/**
|
|
@@ -482,8 +471,8 @@ const KMS = WebexPlugin.extend({
|
|
|
482
471
|
prepareRequest(payload, onBehalfOf) {
|
|
483
472
|
const isECDHRequest = payload.method === 'create' && payload.uri.includes('/ecdhe');
|
|
484
473
|
|
|
485
|
-
return Promise.resolve(isECDHRequest ? partialContexts.get(this) : this._getContext())
|
|
486
|
-
|
|
474
|
+
return Promise.resolve(isECDHRequest ? partialContexts.get(this) : this._getContext()).then(
|
|
475
|
+
(context) => {
|
|
487
476
|
this.logger.info(`kms: wrapping ${isECDHRequest ? 'ephemeral key' : 'kms'} request`);
|
|
488
477
|
const req = new Request(payload);
|
|
489
478
|
let requestContext = context;
|
|
@@ -492,16 +481,19 @@ const KMS = WebexPlugin.extend({
|
|
|
492
481
|
requestContext = this._contextOnBehalfOf(context, onBehalfOf);
|
|
493
482
|
}
|
|
494
483
|
|
|
495
|
-
return req.wrap(requestContext, {serverKey: isECDHRequest})
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
484
|
+
return req.wrap(requestContext, {serverKey: isECDHRequest}).then(() => {
|
|
485
|
+
/* istanbul ignore else */
|
|
486
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
487
|
+
this.logger.info(
|
|
488
|
+
'kms: request payload',
|
|
489
|
+
util.inspect(omit(JSON.parse(JSON.stringify(req)), 'wrapped'), {depth: null})
|
|
490
|
+
);
|
|
491
|
+
}
|
|
501
492
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
493
|
+
return req;
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
);
|
|
505
497
|
},
|
|
506
498
|
|
|
507
499
|
/**
|
|
@@ -512,25 +504,35 @@ const KMS = WebexPlugin.extend({
|
|
|
512
504
|
processKmsMessageEvent(event) {
|
|
513
505
|
this.logger.info('kms: received kms message');
|
|
514
506
|
|
|
515
|
-
return Promise.all(
|
|
516
|
-
.
|
|
517
|
-
this.
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
507
|
+
return Promise.all(
|
|
508
|
+
event.encryption.kmsMessages.map((kmsMessage, index) =>
|
|
509
|
+
this._isECDHEMessage(kmsMessage).then((isECDHMessage) => {
|
|
510
|
+
this.logger.info(`kms: received ${isECDHMessage ? 'ecdhe' : 'normal'} message`);
|
|
511
|
+
const res = new Response(kmsMessage);
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
Promise.resolve(isECDHMessage ? partialContexts.get(this) : contexts.get(this))
|
|
515
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
516
|
+
.then((context) => res.unwrap(context))
|
|
517
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
518
|
+
.then(() => {
|
|
519
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
520
|
+
this.logger.info(
|
|
521
|
+
'kms: response payload',
|
|
522
|
+
util.inspect(omit(JSON.parse(JSON.stringify(res)), 'wrapped'), {depth: null})
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
})
|
|
526
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
527
|
+
.then(() => {
|
|
528
|
+
event.encryption.kmsMessages[index] = res;
|
|
529
|
+
})
|
|
530
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
531
|
+
.then(() => res)
|
|
532
|
+
);
|
|
533
|
+
})
|
|
534
|
+
)
|
|
535
|
+
)
|
|
534
536
|
.then(() => this.batcher.processKmsMessageEvent(event))
|
|
535
537
|
.catch((reason) => {
|
|
536
538
|
this.logger.error('kms: decrypt failed', reason.stack);
|
|
@@ -548,7 +550,8 @@ const KMS = WebexPlugin.extend({
|
|
|
548
550
|
decryptKmsMessage(kmsMessage) {
|
|
549
551
|
const res = new Response(kmsMessage);
|
|
550
552
|
|
|
551
|
-
return contexts
|
|
553
|
+
return contexts
|
|
554
|
+
.get(this)
|
|
552
555
|
.then((context) => res.unwrap(context))
|
|
553
556
|
.then(() => res.body);
|
|
554
557
|
},
|
|
@@ -559,18 +562,17 @@ const KMS = WebexPlugin.extend({
|
|
|
559
562
|
* @returns {Promise<boolean>}
|
|
560
563
|
*/
|
|
561
564
|
_isECDHEMessage(kmsMessage) {
|
|
562
|
-
return this._getKMSStaticPubKey()
|
|
563
|
-
.
|
|
564
|
-
const fields = kmsMessage.split('.');
|
|
565
|
+
return this._getKMSStaticPubKey().then((kmsStaticPubKey) => {
|
|
566
|
+
const fields = kmsMessage.split('.');
|
|
565
567
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
568
|
+
if (fields.length !== 3) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
569
571
|
|
|
570
|
-
|
|
572
|
+
const header = JSON.parse(jose.util.base64url.decode(fields[0]));
|
|
571
573
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
+
return header.kid === kmsStaticPubKey.kid;
|
|
575
|
+
});
|
|
574
576
|
},
|
|
575
577
|
|
|
576
578
|
/**
|
|
@@ -586,69 +588,80 @@ const KMS = WebexPlugin.extend({
|
|
|
586
588
|
|
|
587
589
|
// Note: this should only happen when we're using the async kms batcher;
|
|
588
590
|
// once we implement the sync batcher, this'll need to be smarter.
|
|
589
|
-
return
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
591
|
+
return (
|
|
592
|
+
this.webex.internal.mercury
|
|
593
|
+
.connect()
|
|
594
|
+
.then(() => this.prepareRequest(payload, onBehalfOf))
|
|
595
|
+
.then((req) => {
|
|
596
|
+
req[TIMEOUT_SYMBOL] = timeout;
|
|
597
|
+
|
|
598
|
+
return this.batcher.request(req);
|
|
599
|
+
})
|
|
600
|
+
// High complexity is due to attempt at test mode resiliency
|
|
601
|
+
// eslint-disable-next-line complexity
|
|
602
|
+
.catch((reason) => {
|
|
603
|
+
if (
|
|
604
|
+
process.env.NODE_ENV === 'test' &&
|
|
605
|
+
(reason.status === 403 || reason.statusCode === 403) &&
|
|
606
|
+
reason.message.match(
|
|
607
|
+
/Failed to resolve authorization token in KmsMessage request for user/
|
|
608
|
+
)
|
|
609
|
+
) {
|
|
610
|
+
this.logger.warn('kms: rerequested key due to test-mode kms auth failure');
|
|
611
|
+
|
|
612
|
+
return this.request(payload, {onBehalfOf});
|
|
613
|
+
}
|
|
601
614
|
|
|
602
|
-
|
|
603
|
-
|
|
615
|
+
// KMS Error. Notify the user
|
|
616
|
+
if (reason instanceof KMSError) {
|
|
617
|
+
this.webex.trigger('client:InvalidRequestError');
|
|
604
618
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
this.webex.trigger('client:InvalidRequestError');
|
|
619
|
+
return Promise.reject(reason);
|
|
620
|
+
}
|
|
608
621
|
|
|
609
|
-
|
|
610
|
-
|
|
622
|
+
// Ideally, most or all of the code below would go in kms-batcher, but
|
|
623
|
+
// but batching needs at least one more round of refactoring for that to
|
|
624
|
+
// work.
|
|
625
|
+
if (!reason.statusCode && !reason.status) {
|
|
626
|
+
/* istanbul ignore else */
|
|
627
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
628
|
+
/* istanbul ignore next: reason.stack vs stack difficult to control in test */
|
|
629
|
+
this.logger.info('kms: request error', reason.stack || reason);
|
|
630
|
+
}
|
|
611
631
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
// work.
|
|
615
|
-
if (!reason.statusCode && !reason.status) {
|
|
616
|
-
/* istanbul ignore else */
|
|
617
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
618
|
-
/* istanbul ignore next: reason.stack vs stack difficult to control in test */
|
|
619
|
-
this.logger.info('kms: request error', reason.stack || reason);
|
|
620
|
-
}
|
|
632
|
+
consoleDebug(`timeout ${timeout}`);
|
|
633
|
+
timeout *= 2;
|
|
621
634
|
|
|
622
|
-
|
|
623
|
-
|
|
635
|
+
if (timeout >= this.config.ecdhMaxTimeout) {
|
|
636
|
+
this.logger.info('kms: exceeded maximum KMS request retries');
|
|
624
637
|
|
|
625
|
-
|
|
626
|
-
|
|
638
|
+
return Promise.reject(reason);
|
|
639
|
+
}
|
|
627
640
|
|
|
628
|
-
|
|
629
|
-
|
|
641
|
+
// Peek ahead to make sure we don't reset the timeout if the next timeout
|
|
642
|
+
// will exceed the maximum timeout for renegotiating ECDH keys.
|
|
643
|
+
const nextTimeout = timeout * 2;
|
|
630
644
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
645
|
+
if (timeout >= this.config.kmsMaxTimeout && nextTimeout < this.config.ecdhMaxTimeout) {
|
|
646
|
+
this.logger.info(
|
|
647
|
+
'kms: exceeded maximum KMS request retries; negotiating new ecdh key'
|
|
648
|
+
);
|
|
634
649
|
|
|
635
|
-
|
|
636
|
-
|
|
650
|
+
/* istanbul ignore else */
|
|
651
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
652
|
+
this.logger.info('kms: timeout/maxtimeout', timeout, this.config.kmsMaxTimeout);
|
|
653
|
+
}
|
|
637
654
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
this.logger.info('kms: timeout/maxtimeout', timeout, this.config.kmsMaxTimeout);
|
|
655
|
+
contexts.delete(this);
|
|
656
|
+
timeout = 0;
|
|
641
657
|
}
|
|
642
658
|
|
|
643
|
-
|
|
644
|
-
timeout = 0;
|
|
659
|
+
return this.request(payload, {timeout, onBehalfOf});
|
|
645
660
|
}
|
|
646
661
|
|
|
647
|
-
return
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return Promise.reject(reason);
|
|
651
|
-
});
|
|
662
|
+
return Promise.reject(reason);
|
|
663
|
+
})
|
|
664
|
+
);
|
|
652
665
|
},
|
|
653
666
|
|
|
654
667
|
/**
|
|
@@ -656,8 +669,7 @@ const KMS = WebexPlugin.extend({
|
|
|
656
669
|
* @returns {Promise<string>}
|
|
657
670
|
*/
|
|
658
671
|
_getAuthorization() {
|
|
659
|
-
return this.webex.credentials.getUserToken('spark:kms')
|
|
660
|
-
.then((token) => token.access_token);
|
|
672
|
+
return this.webex.credentials.getUserToken('spark:kms').then((token) => token.access_token);
|
|
661
673
|
},
|
|
662
674
|
|
|
663
675
|
@oneFlight
|
|
@@ -679,15 +691,11 @@ const KMS = WebexPlugin.extend({
|
|
|
679
691
|
});
|
|
680
692
|
}
|
|
681
693
|
|
|
682
|
-
return Promise.all([
|
|
683
|
-
|
|
684
|
-
this._getAuthorization()
|
|
685
|
-
])
|
|
686
|
-
.then(([context, authorization]) => {
|
|
687
|
-
context.clientInfo.credential.bearer = authorization;
|
|
694
|
+
return Promise.all([promise, this._getAuthorization()]).then(([context, authorization]) => {
|
|
695
|
+
context.clientInfo.credential.bearer = authorization;
|
|
688
696
|
|
|
689
|
-
|
|
690
|
-
|
|
697
|
+
return context;
|
|
698
|
+
});
|
|
691
699
|
},
|
|
692
700
|
|
|
693
701
|
/**
|
|
@@ -697,8 +705,7 @@ const KMS = WebexPlugin.extend({
|
|
|
697
705
|
_getKMSCluster() {
|
|
698
706
|
this.logger.info('kms: retrieving KMS cluster');
|
|
699
707
|
|
|
700
|
-
return this._getKMSDetails()
|
|
701
|
-
.then(({kmsCluster}) => kmsCluster);
|
|
708
|
+
return this._getKMSDetails().then(({kmsCluster}) => kmsCluster);
|
|
702
709
|
},
|
|
703
710
|
|
|
704
711
|
/**
|
|
@@ -710,10 +717,11 @@ const KMS = WebexPlugin.extend({
|
|
|
710
717
|
|
|
711
718
|
if (!details) {
|
|
712
719
|
this.logger.info('kms: fetching KMS details');
|
|
713
|
-
details = this.webex
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
720
|
+
details = this.webex
|
|
721
|
+
.request({
|
|
722
|
+
service: 'encryption',
|
|
723
|
+
resource: `/kms/${this.webex.internal.device.userId}`,
|
|
724
|
+
})
|
|
717
725
|
.then((res) => {
|
|
718
726
|
this.logger.info('kms: fetched KMS details');
|
|
719
727
|
const {body} = res;
|
|
@@ -741,8 +749,7 @@ const KMS = WebexPlugin.extend({
|
|
|
741
749
|
_getKMSStaticPubKey() {
|
|
742
750
|
this.logger.info('kms: retrieving KMS static public key');
|
|
743
751
|
|
|
744
|
-
return this._getKMSDetails()
|
|
745
|
-
.then(({rsaPublicKey}) => rsaPublicKey);
|
|
752
|
+
return this._getKMSDetails().then(({rsaPublicKey}) => rsaPublicKey);
|
|
746
753
|
},
|
|
747
754
|
|
|
748
755
|
/**
|
|
@@ -755,19 +762,19 @@ const KMS = WebexPlugin.extend({
|
|
|
755
762
|
|
|
756
763
|
return Promise.all([
|
|
757
764
|
this._getKMSStaticPubKey().then(validateKMS(this.config.caroots)),
|
|
758
|
-
this._getAuthorization()
|
|
765
|
+
this._getAuthorization(),
|
|
759
766
|
])
|
|
760
767
|
.then(([kmsStaticPubKey, authorization]) => {
|
|
761
768
|
context.clientInfo = {
|
|
762
769
|
clientId: this.webex.internal.device.url,
|
|
763
770
|
credential: {
|
|
764
771
|
userId: this.webex.internal.device.userId,
|
|
765
|
-
bearer: authorization
|
|
766
|
-
}
|
|
772
|
+
bearer: authorization,
|
|
773
|
+
},
|
|
767
774
|
};
|
|
768
775
|
|
|
769
776
|
context.serverInfo = {
|
|
770
|
-
key: kmsStaticPubKey
|
|
777
|
+
key: kmsStaticPubKey,
|
|
771
778
|
};
|
|
772
779
|
|
|
773
780
|
this.logger.info('kms: creating local ephemeral key');
|
|
@@ -786,7 +793,7 @@ const KMS = WebexPlugin.extend({
|
|
|
786
793
|
return this.request({
|
|
787
794
|
uri: `${cluster}/ecdhe`,
|
|
788
795
|
method: 'create',
|
|
789
|
-
jwk: localECDHKey.toJSON()
|
|
796
|
+
jwk: localECDHKey.toJSON(),
|
|
790
797
|
});
|
|
791
798
|
})
|
|
792
799
|
.then((res) => {
|
|
@@ -828,14 +835,14 @@ const KMS = WebexPlugin.extend({
|
|
|
828
835
|
credential: {
|
|
829
836
|
userId: onBehalfOf,
|
|
830
837
|
onBehalfOf, // Supports running onBehalfOf self. i.e. A CO which calls onBehalfOf with CO.id.
|
|
831
|
-
bearer: originalContext.clientInfo.credential.bearer
|
|
832
|
-
}
|
|
838
|
+
bearer: originalContext.clientInfo.credential.bearer,
|
|
839
|
+
},
|
|
833
840
|
};
|
|
834
841
|
context.serverInfo = originalContext.serverInfo;
|
|
835
842
|
context.ephemeralKey = originalContext.ephemeralKey;
|
|
836
843
|
|
|
837
844
|
return context;
|
|
838
|
-
}
|
|
845
|
+
},
|
|
839
846
|
});
|
|
840
847
|
|
|
841
848
|
export default KMS;
|