@webex/internal-plugin-encryption 2.59.1 → 2.59.3-next.1
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/.eslintrc.js +6 -6
- package/README.md +42 -42
- package/babel.config.js +3 -3
- package/dist/config.js +21 -21
- package/dist/config.js.map +1 -1
- package/dist/encryption.js +57 -57
- package/dist/encryption.js.map +1 -1
- package/dist/ensure-buffer.browser.js +7 -7
- package/dist/ensure-buffer.browser.js.map +1 -1
- package/dist/ensure-buffer.js +7 -7
- package/dist/ensure-buffer.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/kms-batcher.js +38 -38
- package/dist/kms-batcher.js.map +1 -1
- package/dist/kms-certificate-validation.js +50 -50
- package/dist/kms-certificate-validation.js.map +1 -1
- package/dist/kms-dry-error-interceptor.js +15 -15
- package/dist/kms-dry-error-interceptor.js.map +1 -1
- package/dist/kms-errors.js +16 -16
- package/dist/kms-errors.js.map +1 -1
- package/dist/kms.js +171 -171
- package/dist/kms.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +20 -19
- package/process +1 -1
- package/src/config.js +50 -50
- package/src/encryption.js +257 -257
- package/src/ensure-buffer.browser.js +37 -37
- package/src/ensure-buffer.js +20 -20
- package/src/index.js +159 -159
- package/src/kms-batcher.js +158 -158
- package/src/kms-certificate-validation.js +232 -232
- package/src/kms-dry-error-interceptor.js +65 -65
- package/src/kms-errors.js +147 -147
- package/src/kms.js +848 -848
- package/test/integration/spec/encryption.js +448 -448
- package/test/integration/spec/kms.js +800 -800
- package/test/integration/spec/payload-transfom.js +97 -97
- package/test/unit/spec/encryption.js +82 -82
- package/test/unit/spec/kms-certificate-validation.js +165 -165
- package/test/unit/spec/kms.js +103 -103
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
import {parse as parseUrl} from 'url';
|
|
2
|
-
|
|
3
|
-
import {isUri} from 'valid-url';
|
|
4
|
-
import {fromBER} from 'asn1js';
|
|
5
|
-
import {
|
|
6
|
-
Certificate,
|
|
7
|
-
RSAPublicKey,
|
|
8
|
-
CertificateChainValidationEngine,
|
|
9
|
-
CryptoEngine,
|
|
10
|
-
setEngine,
|
|
11
|
-
} from 'pkijs';
|
|
12
|
-
import {isArray} from 'lodash';
|
|
13
|
-
import jose from 'node-jose';
|
|
14
|
-
import crypto from 'isomorphic-webcrypto';
|
|
15
|
-
import {Buffer} from 'safe-buffer';
|
|
16
|
-
|
|
17
|
-
setEngine(
|
|
18
|
-
'newEngine',
|
|
19
|
-
crypto,
|
|
20
|
-
new CryptoEngine({
|
|
21
|
-
name: '',
|
|
22
|
-
crypto,
|
|
23
|
-
subtle: crypto.subtle,
|
|
24
|
-
})
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const VALID_KTY = 'RSA';
|
|
28
|
-
const VALID_KID_PROTOCOL = 'kms:';
|
|
29
|
-
|
|
30
|
-
const X509_COMMON_NAME_KEY = '2.5.4.3';
|
|
31
|
-
|
|
32
|
-
const X509_SUBJECT_ALT_NAME_KEY = '2.5.29.17';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Customize Error so the SDK knows to quit retrying and notify
|
|
36
|
-
* the user
|
|
37
|
-
*/
|
|
38
|
-
export class KMSError extends Error {
|
|
39
|
-
/**
|
|
40
|
-
* add kmsError field to notify
|
|
41
|
-
* @param {string} message
|
|
42
|
-
*/
|
|
43
|
-
constructor(message) {
|
|
44
|
-
super(message);
|
|
45
|
-
this.kmsError = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const throwError = (err) => {
|
|
50
|
-
throw new KMSError(`INVALID KMS: ${err}`);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Converts the PEM string to a pkijs certificate object
|
|
55
|
-
* @param {string} pem PEM representation of a certificate
|
|
56
|
-
* @returns {Certificate} pkijs object of the certificate
|
|
57
|
-
*/
|
|
58
|
-
const decodeCert = (pem) => {
|
|
59
|
-
if (typeof pem !== 'string') {
|
|
60
|
-
throwError('certificate needs to be a string');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const der = Buffer.from(pem, 'base64');
|
|
64
|
-
const ber = new Uint8Array(der).buffer;
|
|
65
|
-
|
|
66
|
-
const asn1 = fromBER(ber);
|
|
67
|
-
|
|
68
|
-
return new Certificate({schema: asn1.result});
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Validate the 'kty' property of the KMS credentials
|
|
73
|
-
* @param {Object} JWT KMS credentials
|
|
74
|
-
* @param {string} JWT.kty type of certificate
|
|
75
|
-
* @throws {KMSError} if kty is not a valid type
|
|
76
|
-
* @returns {void}
|
|
77
|
-
*/
|
|
78
|
-
const validateKtyHeader = ({kty}) => {
|
|
79
|
-
if (kty !== VALID_KTY) {
|
|
80
|
-
throwError(`'kty' header must be '${VALID_KTY}'`);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const validateKidHeader = ({kid}) => {
|
|
85
|
-
if (!isUri(kid)) {
|
|
86
|
-
throwError("'kid' is not a valid URI");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (parseUrl(kid).protocol !== VALID_KID_PROTOCOL) {
|
|
90
|
-
throwError(`'kid' protocol must be '${VALID_KID_PROTOCOL}'`);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Checks the first certificate matches the 'kid' in the JWT.
|
|
96
|
-
* It first checks the Subject Alternative Name then it checks
|
|
97
|
-
* the Common Name
|
|
98
|
-
* @param {Certificate} certificate represents the KMS
|
|
99
|
-
* @param {Object} JWT KMS credentials
|
|
100
|
-
* @param {string} JWT.kid the uri of the KMS
|
|
101
|
-
* @throws {KMSError} if unable to validate certificate against KMS credentials
|
|
102
|
-
* @returns {void}
|
|
103
|
-
*/
|
|
104
|
-
const validateCommonName = ([certificate], {kid}) => {
|
|
105
|
-
const kidHostname = parseUrl(kid).hostname;
|
|
106
|
-
let validationSuccessful = false;
|
|
107
|
-
|
|
108
|
-
if (certificate.extensions) {
|
|
109
|
-
// Subject Alt Names are in here
|
|
110
|
-
for (const extension of certificate.extensions) {
|
|
111
|
-
if (extension.extnID === X509_SUBJECT_ALT_NAME_KEY) {
|
|
112
|
-
const {altNames} = extension.parsedValue;
|
|
113
|
-
|
|
114
|
-
for (const entry of altNames) {
|
|
115
|
-
const san = entry.value;
|
|
116
|
-
|
|
117
|
-
validationSuccessful = san === kidHostname;
|
|
118
|
-
if (validationSuccessful) {
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (validationSuccessful) {
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!validationSuccessful) {
|
|
131
|
-
// Didn't match kid in the Subject Alt Names, checking the Common Name
|
|
132
|
-
const subjectAttributes = certificate.subject.typesAndValues;
|
|
133
|
-
|
|
134
|
-
for (const attribute of subjectAttributes) {
|
|
135
|
-
if (attribute.type === X509_COMMON_NAME_KEY) {
|
|
136
|
-
const commonName = attribute.value.valueBlock.value;
|
|
137
|
-
|
|
138
|
-
validationSuccessful = commonName === kidHostname;
|
|
139
|
-
if (validationSuccessful) {
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!validationSuccessful) {
|
|
147
|
-
throwError("hostname of the 1st certificate does not match 'kid'");
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Validate the first KMS certificate against the information
|
|
153
|
-
* provided in the JWT
|
|
154
|
-
* @param {Certificate} certificate first certificate the identifies the KMS
|
|
155
|
-
* @param {Object} JWT credentials of the KMS
|
|
156
|
-
* @param {string} JWT.e Public exponent of the first certificate
|
|
157
|
-
* @param {string} KWT.n Modulus of the first certificate
|
|
158
|
-
* @throws {KMSError} if e or n doesn't match the first certificate
|
|
159
|
-
* @returns {void}
|
|
160
|
-
*/
|
|
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
|
-
};
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Validates the list of certificates against the CAs provided
|
|
180
|
-
* @param {certificate[]} certificates list of certificates provided
|
|
181
|
-
* by the KMS to certify itself
|
|
182
|
-
* @param {string[]} [caroots=[]] list of Certificate Authorities used to
|
|
183
|
-
* validate the KMS's certificates
|
|
184
|
-
* @returns {Promise} rejects if unable to validate the certificates
|
|
185
|
-
*/
|
|
186
|
-
const validateCertificatesSignature = (certificates, caroots = []) => {
|
|
187
|
-
const certificateEngine = new CertificateChainValidationEngine({
|
|
188
|
-
trustedCerts: caroots.map(decodeCert),
|
|
189
|
-
certs: certificates,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
return certificateEngine.verify().then(({result, resultCode, resultMessage}) => {
|
|
193
|
-
if (!result) {
|
|
194
|
-
throwError(`Certificate Validation failed [${resultCode}]: ${resultMessage}`);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Validates the information provided by the KMS. This is a curried function.
|
|
201
|
-
* The first function takes the caroots param and returns a second function.
|
|
202
|
-
* The second function takes the credentials of the KMS and validates it
|
|
203
|
-
* @param {string[]} caroots PEM encoded certificates that will be used
|
|
204
|
-
* as Certificate Authorities
|
|
205
|
-
* @param {Object} jwt Object containing the fields necessary to
|
|
206
|
-
* validate the KMS
|
|
207
|
-
* @returns {Promise} when resolved will return the jwt
|
|
208
|
-
*/
|
|
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);
|
|
220
|
-
|
|
221
|
-
validateCommonName(certificates, jwt);
|
|
222
|
-
validatePublicCertificate(certificates, jwt);
|
|
223
|
-
|
|
224
|
-
// Skip validating signatures if no CA roots were provided
|
|
225
|
-
const promise = caroots
|
|
226
|
-
? validateCertificatesSignature(certificates, caroots)
|
|
227
|
-
: Promise.resolve();
|
|
228
|
-
|
|
229
|
-
return promise.then(() => jwt);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
export default validateKMS;
|
|
1
|
+
import {parse as parseUrl} from 'url';
|
|
2
|
+
|
|
3
|
+
import {isUri} from 'valid-url';
|
|
4
|
+
import {fromBER} from 'asn1js';
|
|
5
|
+
import {
|
|
6
|
+
Certificate,
|
|
7
|
+
RSAPublicKey,
|
|
8
|
+
CertificateChainValidationEngine,
|
|
9
|
+
CryptoEngine,
|
|
10
|
+
setEngine,
|
|
11
|
+
} from 'pkijs';
|
|
12
|
+
import {isArray} from 'lodash';
|
|
13
|
+
import jose from 'node-jose';
|
|
14
|
+
import crypto from 'isomorphic-webcrypto';
|
|
15
|
+
import {Buffer} from 'safe-buffer';
|
|
16
|
+
|
|
17
|
+
setEngine(
|
|
18
|
+
'newEngine',
|
|
19
|
+
crypto,
|
|
20
|
+
new CryptoEngine({
|
|
21
|
+
name: '',
|
|
22
|
+
crypto,
|
|
23
|
+
subtle: crypto.subtle,
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const VALID_KTY = 'RSA';
|
|
28
|
+
const VALID_KID_PROTOCOL = 'kms:';
|
|
29
|
+
|
|
30
|
+
const X509_COMMON_NAME_KEY = '2.5.4.3';
|
|
31
|
+
|
|
32
|
+
const X509_SUBJECT_ALT_NAME_KEY = '2.5.29.17';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Customize Error so the SDK knows to quit retrying and notify
|
|
36
|
+
* the user
|
|
37
|
+
*/
|
|
38
|
+
export class KMSError extends Error {
|
|
39
|
+
/**
|
|
40
|
+
* add kmsError field to notify
|
|
41
|
+
* @param {string} message
|
|
42
|
+
*/
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.kmsError = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const throwError = (err) => {
|
|
50
|
+
throw new KMSError(`INVALID KMS: ${err}`);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Converts the PEM string to a pkijs certificate object
|
|
55
|
+
* @param {string} pem PEM representation of a certificate
|
|
56
|
+
* @returns {Certificate} pkijs object of the certificate
|
|
57
|
+
*/
|
|
58
|
+
const decodeCert = (pem) => {
|
|
59
|
+
if (typeof pem !== 'string') {
|
|
60
|
+
throwError('certificate needs to be a string');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const der = Buffer.from(pem, 'base64');
|
|
64
|
+
const ber = new Uint8Array(der).buffer;
|
|
65
|
+
|
|
66
|
+
const asn1 = fromBER(ber);
|
|
67
|
+
|
|
68
|
+
return new Certificate({schema: asn1.result});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate the 'kty' property of the KMS credentials
|
|
73
|
+
* @param {Object} JWT KMS credentials
|
|
74
|
+
* @param {string} JWT.kty type of certificate
|
|
75
|
+
* @throws {KMSError} if kty is not a valid type
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
const validateKtyHeader = ({kty}) => {
|
|
79
|
+
if (kty !== VALID_KTY) {
|
|
80
|
+
throwError(`'kty' header must be '${VALID_KTY}'`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const validateKidHeader = ({kid}) => {
|
|
85
|
+
if (!isUri(kid)) {
|
|
86
|
+
throwError("'kid' is not a valid URI");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (parseUrl(kid).protocol !== VALID_KID_PROTOCOL) {
|
|
90
|
+
throwError(`'kid' protocol must be '${VALID_KID_PROTOCOL}'`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks the first certificate matches the 'kid' in the JWT.
|
|
96
|
+
* It first checks the Subject Alternative Name then it checks
|
|
97
|
+
* the Common Name
|
|
98
|
+
* @param {Certificate} certificate represents the KMS
|
|
99
|
+
* @param {Object} JWT KMS credentials
|
|
100
|
+
* @param {string} JWT.kid the uri of the KMS
|
|
101
|
+
* @throws {KMSError} if unable to validate certificate against KMS credentials
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
const validateCommonName = ([certificate], {kid}) => {
|
|
105
|
+
const kidHostname = parseUrl(kid).hostname;
|
|
106
|
+
let validationSuccessful = false;
|
|
107
|
+
|
|
108
|
+
if (certificate.extensions) {
|
|
109
|
+
// Subject Alt Names are in here
|
|
110
|
+
for (const extension of certificate.extensions) {
|
|
111
|
+
if (extension.extnID === X509_SUBJECT_ALT_NAME_KEY) {
|
|
112
|
+
const {altNames} = extension.parsedValue;
|
|
113
|
+
|
|
114
|
+
for (const entry of altNames) {
|
|
115
|
+
const san = entry.value;
|
|
116
|
+
|
|
117
|
+
validationSuccessful = san === kidHostname;
|
|
118
|
+
if (validationSuccessful) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (validationSuccessful) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!validationSuccessful) {
|
|
131
|
+
// Didn't match kid in the Subject Alt Names, checking the Common Name
|
|
132
|
+
const subjectAttributes = certificate.subject.typesAndValues;
|
|
133
|
+
|
|
134
|
+
for (const attribute of subjectAttributes) {
|
|
135
|
+
if (attribute.type === X509_COMMON_NAME_KEY) {
|
|
136
|
+
const commonName = attribute.value.valueBlock.value;
|
|
137
|
+
|
|
138
|
+
validationSuccessful = commonName === kidHostname;
|
|
139
|
+
if (validationSuccessful) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!validationSuccessful) {
|
|
147
|
+
throwError("hostname of the 1st certificate does not match 'kid'");
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Validate the first KMS certificate against the information
|
|
153
|
+
* provided in the JWT
|
|
154
|
+
* @param {Certificate} certificate first certificate the identifies the KMS
|
|
155
|
+
* @param {Object} JWT credentials of the KMS
|
|
156
|
+
* @param {string} JWT.e Public exponent of the first certificate
|
|
157
|
+
* @param {string} KWT.n Modulus of the first certificate
|
|
158
|
+
* @throws {KMSError} if e or n doesn't match the first certificate
|
|
159
|
+
* @returns {void}
|
|
160
|
+
*/
|
|
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
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Validates the list of certificates against the CAs provided
|
|
180
|
+
* @param {certificate[]} certificates list of certificates provided
|
|
181
|
+
* by the KMS to certify itself
|
|
182
|
+
* @param {string[]} [caroots=[]] list of Certificate Authorities used to
|
|
183
|
+
* validate the KMS's certificates
|
|
184
|
+
* @returns {Promise} rejects if unable to validate the certificates
|
|
185
|
+
*/
|
|
186
|
+
const validateCertificatesSignature = (certificates, caroots = []) => {
|
|
187
|
+
const certificateEngine = new CertificateChainValidationEngine({
|
|
188
|
+
trustedCerts: caroots.map(decodeCert),
|
|
189
|
+
certs: certificates,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return certificateEngine.verify().then(({result, resultCode, resultMessage}) => {
|
|
193
|
+
if (!result) {
|
|
194
|
+
throwError(`Certificate Validation failed [${resultCode}]: ${resultMessage}`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Validates the information provided by the KMS. This is a curried function.
|
|
201
|
+
* The first function takes the caroots param and returns a second function.
|
|
202
|
+
* The second function takes the credentials of the KMS and validates it
|
|
203
|
+
* @param {string[]} caroots PEM encoded certificates that will be used
|
|
204
|
+
* as Certificate Authorities
|
|
205
|
+
* @param {Object} jwt Object containing the fields necessary to
|
|
206
|
+
* validate the KMS
|
|
207
|
+
* @returns {Promise} when resolved will return the jwt
|
|
208
|
+
*/
|
|
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);
|
|
220
|
+
|
|
221
|
+
validateCommonName(certificates, jwt);
|
|
222
|
+
validatePublicCertificate(certificates, jwt);
|
|
223
|
+
|
|
224
|
+
// Skip validating signatures if no CA roots were provided
|
|
225
|
+
const promise = caroots
|
|
226
|
+
? validateCertificatesSignature(certificates, caroots)
|
|
227
|
+
: Promise.resolve();
|
|
228
|
+
|
|
229
|
+
return promise.then(() => jwt);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export default validateKMS;
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {Interceptor} from '@webex/http-core';
|
|
6
|
-
|
|
7
|
-
import {DryError} from './kms-errors';
|
|
8
|
-
/**
|
|
9
|
-
* Interceptor (only to be used in test mode) intended to replay requests that
|
|
10
|
-
* fail as a result of the test-user incompatibility in KMS.
|
|
11
|
-
* @class
|
|
12
|
-
*/
|
|
13
|
-
export default class KmsDryErrorInterceptor extends Interceptor {
|
|
14
|
-
/**
|
|
15
|
-
* @returns {KmsDryErrorInterceptor}
|
|
16
|
-
*/
|
|
17
|
-
static create() {
|
|
18
|
-
return new KmsDryErrorInterceptor({webex: this});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param {Object} options
|
|
23
|
-
* @param {Exception} reason
|
|
24
|
-
* @returns {Promise}
|
|
25
|
-
*/
|
|
26
|
-
onResponseError(options, reason) {
|
|
27
|
-
if (
|
|
28
|
-
reason instanceof DryError &&
|
|
29
|
-
reason.message.match(/Failed to resolve authorization token in KmsMessage request for user/)
|
|
30
|
-
) {
|
|
31
|
-
this.webex.logger.error('DRY Request Failed due to kms/test-user flakiness');
|
|
32
|
-
this.webex.logger.error(reason);
|
|
33
|
-
|
|
34
|
-
return this.replay(options, reason);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return Promise.reject(reason);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Replays the request
|
|
42
|
-
* @param {Object} options
|
|
43
|
-
* @param {DryError} reason
|
|
44
|
-
* @returns {Object}
|
|
45
|
-
*/
|
|
46
|
-
replay(options, reason) {
|
|
47
|
-
if (options.replayCount) {
|
|
48
|
-
options.replayCount += 1;
|
|
49
|
-
} else {
|
|
50
|
-
options.replayCount = 1;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (options.replayCount > this.webex.config.maxAuthenticationReplays) {
|
|
54
|
-
this.webex.logger.error(
|
|
55
|
-
`kms: failed after ${this.webex.config.maxAuthenticationReplays} replay attempts`
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return Promise.reject(reason);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.webex.logger.info(`kms: replaying request ${options.replayCount} time`);
|
|
62
|
-
|
|
63
|
-
return this.webex.request(options);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {Interceptor} from '@webex/http-core';
|
|
6
|
+
|
|
7
|
+
import {DryError} from './kms-errors';
|
|
8
|
+
/**
|
|
9
|
+
* Interceptor (only to be used in test mode) intended to replay requests that
|
|
10
|
+
* fail as a result of the test-user incompatibility in KMS.
|
|
11
|
+
* @class
|
|
12
|
+
*/
|
|
13
|
+
export default class KmsDryErrorInterceptor extends Interceptor {
|
|
14
|
+
/**
|
|
15
|
+
* @returns {KmsDryErrorInterceptor}
|
|
16
|
+
*/
|
|
17
|
+
static create() {
|
|
18
|
+
return new KmsDryErrorInterceptor({webex: this});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} options
|
|
23
|
+
* @param {Exception} reason
|
|
24
|
+
* @returns {Promise}
|
|
25
|
+
*/
|
|
26
|
+
onResponseError(options, reason) {
|
|
27
|
+
if (
|
|
28
|
+
reason instanceof DryError &&
|
|
29
|
+
reason.message.match(/Failed to resolve authorization token in KmsMessage request for user/)
|
|
30
|
+
) {
|
|
31
|
+
this.webex.logger.error('DRY Request Failed due to kms/test-user flakiness');
|
|
32
|
+
this.webex.logger.error(reason);
|
|
33
|
+
|
|
34
|
+
return this.replay(options, reason);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return Promise.reject(reason);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Replays the request
|
|
42
|
+
* @param {Object} options
|
|
43
|
+
* @param {DryError} reason
|
|
44
|
+
* @returns {Object}
|
|
45
|
+
*/
|
|
46
|
+
replay(options, reason) {
|
|
47
|
+
if (options.replayCount) {
|
|
48
|
+
options.replayCount += 1;
|
|
49
|
+
} else {
|
|
50
|
+
options.replayCount = 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.replayCount > this.webex.config.maxAuthenticationReplays) {
|
|
54
|
+
this.webex.logger.error(
|
|
55
|
+
`kms: failed after ${this.webex.config.maxAuthenticationReplays} replay attempts`
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return Promise.reject(reason);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.webex.logger.info(`kms: replaying request ${options.replayCount} time`);
|
|
62
|
+
|
|
63
|
+
return this.webex.request(options);
|
|
64
|
+
}
|
|
65
|
+
}
|