@webex/internal-plugin-encryption 3.0.0-beta.9 → 3.0.0-bnr.2
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/encryption.js +9 -60
- 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 +6 -30
- package/dist/kms-batcher.js.map +1 -1
- package/dist/kms-certificate-validation.js +20 -88
- 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 +3 -50
- package/dist/kms-errors.js.map +1 -1
- package/dist/kms.js +74 -213
- package/dist/kms.js.map +1 -1
- package/dist/types/config.d.ts +16 -0
- package/dist/types/encryption.d.ts +2 -0
- package/dist/types/ensure-buffer.browser.d.ts +10 -0
- package/dist/types/ensure-buffer.d.ts +7 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/kms-batcher.d.ts +6 -0
- package/dist/types/kms-certificate-validation.d.ts +24 -0
- package/dist/types/kms-dry-error-interceptor.d.ts +25 -0
- package/dist/types/kms-errors.d.ts +33 -0
- package/dist/types/kms.d.ts +5 -0
- package/package.json +15 -15
- package/src/config.js +3 -3
- package/src/encryption.js +66 -56
- 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 +50 -44
- package/src/kms-certificate-validation.js +45 -47
- package/src/kms-dry-error-interceptor.js +8 -4
- package/src/kms-errors.js +19 -16
- package/src/kms.js +210 -206
- package/test/integration/spec/encryption.js +311 -230
- package/test/integration/spec/kms.js +532 -404
- package/test/integration/spec/payload-transfom.js +69 -69
- package/test/unit/spec/encryption.js +16 -13
- package/test/unit/spec/kms-certificate-validation.js +41 -32
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
RSAPublicKey,
|
|
8
8
|
CertificateChainValidationEngine,
|
|
9
9
|
CryptoEngine,
|
|
10
|
-
setEngine
|
|
10
|
+
setEngine,
|
|
11
11
|
} from 'pkijs';
|
|
12
12
|
import {isArray} from 'lodash';
|
|
13
13
|
import jose from 'node-jose';
|
|
@@ -20,7 +20,7 @@ setEngine(
|
|
|
20
20
|
new CryptoEngine({
|
|
21
21
|
name: '',
|
|
22
22
|
crypto,
|
|
23
|
-
subtle: crypto.subtle
|
|
23
|
+
subtle: crypto.subtle,
|
|
24
24
|
})
|
|
25
25
|
);
|
|
26
26
|
|
|
@@ -83,7 +83,7 @@ const validateKtyHeader = ({kty}) => {
|
|
|
83
83
|
|
|
84
84
|
const validateKidHeader = ({kid}) => {
|
|
85
85
|
if (!isUri(kid)) {
|
|
86
|
-
throwError('
|
|
86
|
+
throwError("'kid' is not a valid URI");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if (parseUrl(kid).protocol !== VALID_KID_PROTOCOL) {
|
|
@@ -144,7 +144,7 @@ const validateCommonName = ([certificate], {kid}) => {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
if (!validationSuccessful) {
|
|
147
|
-
throwError(
|
|
147
|
+
throwError("hostname of the 1st certificate does not match 'kid'");
|
|
148
148
|
}
|
|
149
149
|
};
|
|
150
150
|
|
|
@@ -158,23 +158,22 @@ const validateCommonName = ([certificate], {kid}) => {
|
|
|
158
158
|
* @throws {KMSError} if e or n doesn't match the first certificate
|
|
159
159
|
* @returns {void}
|
|
160
160
|
*/
|
|
161
|
-
const validatePublicCertificate =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
};
|
|
161
|
+
const validatePublicCertificate = ([certificate], {e: publicExponent, n: modulus}) => {
|
|
162
|
+
const {encode} = jose.util.base64url;
|
|
163
|
+
|
|
164
|
+
const publicKey = certificate.subjectPublicKeyInfo.subjectPublicKey;
|
|
165
|
+
const asn1PublicCert = fromBER(publicKey.valueBlock.valueHex);
|
|
166
|
+
const publicCert = new RSAPublicKey({schema: asn1PublicCert.result});
|
|
167
|
+
const publicExponentHex = publicCert.publicExponent.valueBlock.valueHex;
|
|
168
|
+
const modulusHex = publicCert.modulus.valueBlock.valueHex;
|
|
169
|
+
|
|
170
|
+
if (publicExponent !== encode(publicExponentHex)) {
|
|
171
|
+
throwError('Public exponent is invalid');
|
|
172
|
+
}
|
|
173
|
+
if (modulus !== encode(modulusHex)) {
|
|
174
|
+
throwError('Modulus is invalid');
|
|
175
|
+
}
|
|
176
|
+
};
|
|
178
177
|
|
|
179
178
|
/**
|
|
180
179
|
* Validates the list of certificates against the CAs provided
|
|
@@ -187,17 +186,14 @@ const validatePublicCertificate =
|
|
|
187
186
|
const validateCertificatesSignature = (certificates, caroots = []) => {
|
|
188
187
|
const certificateEngine = new CertificateChainValidationEngine({
|
|
189
188
|
trustedCerts: caroots.map(decodeCert),
|
|
190
|
-
certs: certificates
|
|
189
|
+
certs: certificates,
|
|
191
190
|
});
|
|
192
191
|
|
|
193
|
-
return certificateEngine.verify()
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
192
|
+
return certificateEngine.verify().then(({result, resultCode, resultMessage}) => {
|
|
193
|
+
if (!result) {
|
|
194
|
+
throwError(`Certificate Validation failed [${resultCode}]: ${resultMessage}`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
201
197
|
};
|
|
202
198
|
|
|
203
199
|
/**
|
|
@@ -210,25 +206,27 @@ const validateCertificatesSignature = (certificates, caroots = []) => {
|
|
|
210
206
|
* validate the KMS
|
|
211
207
|
* @returns {Promise} when resolved will return the jwt
|
|
212
208
|
*/
|
|
213
|
-
const validateKMS =
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
209
|
+
const validateKMS =
|
|
210
|
+
(caroots) =>
|
|
211
|
+
(jwt = {}) =>
|
|
212
|
+
Promise.resolve().then(() => {
|
|
213
|
+
validateKtyHeader(jwt);
|
|
214
|
+
validateKidHeader(jwt);
|
|
215
|
+
|
|
216
|
+
if (!(isArray(jwt.x5c) && jwt.x5c.length > 0)) {
|
|
217
|
+
throwError('JWK does not contain a list of certificates');
|
|
218
|
+
}
|
|
219
|
+
const certificates = jwt.x5c.map(decodeCert);
|
|
222
220
|
|
|
223
|
-
|
|
224
|
-
|
|
221
|
+
validateCommonName(certificates, jwt);
|
|
222
|
+
validatePublicCertificate(certificates, jwt);
|
|
225
223
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
// Skip validating signatures if no CA roots were provided
|
|
225
|
+
const promise = caroots
|
|
226
|
+
? validateCertificatesSignature(certificates, caroots)
|
|
227
|
+
: Promise.resolve();
|
|
229
228
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
});
|
|
229
|
+
return promise.then(() => jwt);
|
|
230
|
+
});
|
|
233
231
|
|
|
234
232
|
export default validateKMS;
|
|
@@ -24,7 +24,10 @@ export default class KmsDryErrorInterceptor extends Interceptor {
|
|
|
24
24
|
* @returns {Promise}
|
|
25
25
|
*/
|
|
26
26
|
onResponseError(options, reason) {
|
|
27
|
-
if (
|
|
27
|
+
if (
|
|
28
|
+
reason instanceof DryError &&
|
|
29
|
+
reason.message.match(/Failed to resolve authorization token in KmsMessage request for user/)
|
|
30
|
+
) {
|
|
28
31
|
this.webex.logger.error('DRY Request Failed due to kms/test-user flakiness');
|
|
29
32
|
this.webex.logger.error(reason);
|
|
30
33
|
|
|
@@ -43,13 +46,14 @@ export default class KmsDryErrorInterceptor extends Interceptor {
|
|
|
43
46
|
replay(options, reason) {
|
|
44
47
|
if (options.replayCount) {
|
|
45
48
|
options.replayCount += 1;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
49
|
+
} else {
|
|
48
50
|
options.replayCount = 1;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
if (options.replayCount > this.webex.config.maxAuthenticationReplays) {
|
|
52
|
-
this.webex.logger.error(
|
|
54
|
+
this.webex.logger.error(
|
|
55
|
+
`kms: failed after ${this.webex.config.maxAuthenticationReplays} replay attempts`
|
|
56
|
+
);
|
|
53
57
|
|
|
54
58
|
return Promise.reject(reason);
|
|
55
59
|
}
|
package/src/kms-errors.js
CHANGED
|
@@ -9,7 +9,8 @@ import {WebexHttpError} from '@webex/webex-core';
|
|
|
9
9
|
* Error class for KMS errors
|
|
10
10
|
*/
|
|
11
11
|
export class KmsError extends Exception {
|
|
12
|
-
static defaultMessage =
|
|
12
|
+
static defaultMessage =
|
|
13
|
+
'An unknown error occurred while communicating with the kms. This implies we received an error response without a body.';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {HttpResponse} body
|
|
@@ -21,20 +22,20 @@ export class KmsError extends Exception {
|
|
|
21
22
|
Object.defineProperties(this, {
|
|
22
23
|
body: {
|
|
23
24
|
enumerable: false,
|
|
24
|
-
value: body
|
|
25
|
+
value: body,
|
|
25
26
|
},
|
|
26
27
|
reason: {
|
|
27
28
|
enumerable: false,
|
|
28
|
-
value: body.reason
|
|
29
|
+
value: body.reason,
|
|
29
30
|
},
|
|
30
31
|
requestId: {
|
|
31
32
|
enumerable: false,
|
|
32
|
-
value: body.requestId
|
|
33
|
+
value: body.requestId,
|
|
33
34
|
},
|
|
34
35
|
status: {
|
|
35
36
|
enumerable: false,
|
|
36
|
-
value: body.status
|
|
37
|
-
}
|
|
37
|
+
value: body.status,
|
|
38
|
+
},
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
let message = typeof body === 'string' ? body : body.reason;
|
|
@@ -63,7 +64,9 @@ export class KmsTimeoutError extends KmsError {
|
|
|
63
64
|
* @returns {string}
|
|
64
65
|
*/
|
|
65
66
|
parse({request = {}, timeout} = {}) {
|
|
66
|
-
let message = `The KMS did not respond within ${
|
|
67
|
+
let message = `The KMS did not respond within ${
|
|
68
|
+
timeout ? `${timeout} milliseconds` : 'a timely fashion'
|
|
69
|
+
}`;
|
|
67
70
|
|
|
68
71
|
if (request) {
|
|
69
72
|
if (request.method && request.uri) {
|
|
@@ -100,12 +103,12 @@ export class DryError extends WebexHttpError {
|
|
|
100
103
|
}
|
|
101
104
|
if (this.options.url) {
|
|
102
105
|
message += `\n${this.options.method} ${this.options.url}`;
|
|
103
|
-
}
|
|
104
|
-
else if (this.options.uri) {
|
|
106
|
+
} else if (this.options.uri) {
|
|
105
107
|
message += `\n${this.options.method} ${this.options.uri}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
} else {
|
|
109
|
+
message += `\n${this.options.method} ${this.options.service.toUpperCase()}/${
|
|
110
|
+
this.options.resource
|
|
111
|
+
}`;
|
|
109
112
|
}
|
|
110
113
|
message += `\nWEBEX_TRACKING_ID: ${this.options.headers.trackingid}`;
|
|
111
114
|
|
|
@@ -119,16 +122,16 @@ export class DryError extends WebexHttpError {
|
|
|
119
122
|
Object.defineProperties(this, {
|
|
120
123
|
reason: {
|
|
121
124
|
enumerable: false,
|
|
122
|
-
value: body.reason
|
|
125
|
+
value: body.reason,
|
|
123
126
|
},
|
|
124
127
|
requestId: {
|
|
125
128
|
enumerable: false,
|
|
126
|
-
value: body.requestId
|
|
129
|
+
value: body.requestId,
|
|
127
130
|
},
|
|
128
131
|
status: {
|
|
129
132
|
enumerable: false,
|
|
130
|
-
value: body.status
|
|
131
|
-
}
|
|
133
|
+
value: body.status,
|
|
134
|
+
},
|
|
132
135
|
});
|
|
133
136
|
|
|
134
137
|
return message;
|