Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a81dc9a

Browse filesBrowse files
jasnellcodebytere
authored andcommitted
src,crypto: refactoring of crypto_context, SecureContext
Cleaup and improvement of crypto_context and SecureContext. Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #35665 Reviewed-By: Alba Mendez <me@alba.sh> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
1 parent 9c6be3c commit a81dc9a
Copy full SHA for a81dc9a

File tree

Expand file treeCollapse file tree

3 files changed

+309
-287
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+309
-287
lines changed
Open diff view settings
Collapse file

‎lib/_tls_common.js‎

Copy file name to clipboardExpand all lines: lib/_tls_common.js
+172-102Lines changed: 172 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323

2424
const {
2525
ArrayIsArray,
26+
ArrayPrototypeFilter,
27+
ArrayPrototypeJoin,
2628
ObjectCreate,
29+
StringPrototypeSplit,
30+
StringPrototypeStartsWith,
2731
} = primordials;
2832

2933
const { parseCertString } = require('internal/tls');
@@ -44,8 +48,15 @@ const {
4448
TLS1_3_VERSION,
4549
} = internalBinding('constants').crypto;
4650

47-
// Lazily loaded from internal/crypto/util.
48-
let toBuf = null;
51+
const {
52+
validateString,
53+
validateInteger,
54+
validateInt32,
55+
} = require('internal/validators');
56+
57+
const {
58+
toBuf
59+
} = require('internal/crypto/util');
4960

5061
function toV(which, v, def) {
5162
if (v == null) v = def;
@@ -75,7 +86,10 @@ function SecureContext(secureProtocol, secureOptions, minVersion, maxVersion) {
7586
toV('minimum', minVersion, tls.DEFAULT_MIN_VERSION),
7687
toV('maximum', maxVersion, tls.DEFAULT_MAX_VERSION));
7788

78-
if (secureOptions) this.context.setOptions(secureOptions);
89+
if (secureOptions) {
90+
validateInteger(secureOptions, 'secureOptions');
91+
this.context.setOptions(secureOptions);
92+
}
7993
}
8094

8195
function validateKeyOrCertOption(name, value) {
@@ -90,80 +104,136 @@ function validateKeyOrCertOption(name, value) {
90104

91105
exports.SecureContext = SecureContext;
92106

107+
function setKey(context, key, passphrase) {
108+
validateKeyOrCertOption('key', key);
109+
if (passphrase != null)
110+
validateString(passphrase, 'options.passphrase');
111+
context.setKey(key, passphrase);
112+
}
113+
114+
function processCiphers(ciphers) {
115+
ciphers = StringPrototypeSplit(ciphers || tls.DEFAULT_CIPHERS, ':');
116+
117+
const cipherList =
118+
ArrayPrototypeJoin(
119+
ArrayPrototypeFilter(
120+
ciphers,
121+
(cipher) => {
122+
return cipher.length > 0 &&
123+
!StringPrototypeStartsWith(cipher, 'TLS_');
124+
}), ':');
125+
126+
const cipherSuites =
127+
ArrayPrototypeJoin(
128+
ArrayPrototypeFilter(
129+
ciphers,
130+
(cipher) => {
131+
return cipher.length > 0 &&
132+
StringPrototypeStartsWith(cipher, 'TLS_');
133+
}), ':');
134+
135+
// Specifying empty cipher suites for both TLS1.2 and TLS1.3 is invalid, its
136+
// not possible to handshake with no suites.
137+
if (cipherSuites === '' && cipherList === '')
138+
throw new ERR_INVALID_ARG_VALUE('options.ciphers', ciphers);
139+
140+
return { cipherList, cipherSuites };
141+
}
142+
143+
function addCACerts(context, ...certs) {
144+
for (const cert of certs) {
145+
validateKeyOrCertOption('ca', cert);
146+
context.addCACert(cert);
147+
}
148+
}
149+
150+
function setCerts(context, ...certs) {
151+
for (const cert of certs) {
152+
validateKeyOrCertOption('cert', cert);
153+
context.setCert(cert);
154+
}
155+
}
93156

94157
exports.createSecureContext = function createSecureContext(options) {
95158
if (!options) options = {};
96159

97-
let secureOptions = options.secureOptions;
98-
if (options.honorCipherOrder)
160+
const {
161+
ca,
162+
cert,
163+
ciphers,
164+
clientCertEngine,
165+
crl,
166+
dhparam,
167+
ecdhCurve = tls.DEFAULT_ECDH_CURVE,
168+
honorCipherOrder,
169+
key,
170+
minVersion,
171+
maxVersion,
172+
passphrase,
173+
pfx,
174+
privateKeyIdentifier,
175+
privateKeyEngine,
176+
secureProtocol,
177+
sessionIdContext,
178+
sessionTimeout,
179+
sigalgs,
180+
singleUse,
181+
ticketKeys,
182+
} = options;
183+
184+
let { secureOptions } = options;
185+
186+
if (honorCipherOrder)
99187
secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE;
100188

101-
const c = new SecureContext(options.secureProtocol, secureOptions,
102-
options.minVersion, options.maxVersion);
189+
const c = new SecureContext(secureProtocol, secureOptions,
190+
minVersion, maxVersion);
103191

104192
// Add CA before the cert to be able to load cert's issuer in C++ code.
105-
const { ca } = options;
193+
// NOTE(@jasnell): ca, cert, and key are permitted to be falsy, so do not
194+
// change the checks to !== undefined checks.
106195
if (ca) {
107-
if (ArrayIsArray(ca)) {
108-
for (const val of ca) {
109-
validateKeyOrCertOption('ca', val);
110-
c.context.addCACert(val);
111-
}
112-
} else {
113-
validateKeyOrCertOption('ca', ca);
114-
c.context.addCACert(ca);
115-
}
196+
if (ArrayIsArray(ca))
197+
addCACerts(c.context, ...ca);
198+
else
199+
addCACerts(c.context, ca);
116200
} else {
117201
c.context.addRootCerts();
118202
}
119203

120-
const { cert } = options;
121204
if (cert) {
122-
if (ArrayIsArray(cert)) {
123-
for (const val of cert) {
124-
validateKeyOrCertOption('cert', val);
125-
c.context.setCert(val);
126-
}
127-
} else {
128-
validateKeyOrCertOption('cert', cert);
129-
c.context.setCert(cert);
130-
}
205+
if (ArrayIsArray(cert))
206+
setCerts(c.context, ...cert);
207+
else
208+
setCerts(c.context, cert);
131209
}
132210

133211
// Set the key after the cert.
134212
// `ssl_set_pkey` returns `0` when the key does not match the cert, but
135213
// `ssl_set_cert` returns `1` and nullifies the key in the SSL structure
136214
// which leads to the crash later on.
137-
const key = options.key;
138-
const passphrase = options.passphrase;
139215
if (key) {
140216
if (ArrayIsArray(key)) {
141217
for (const val of key) {
142218
// eslint-disable-next-line eqeqeq
143219
const pem = (val != undefined && val.pem !== undefined ? val.pem : val);
144-
validateKeyOrCertOption('key', pem);
145-
c.context.setKey(pem, val.passphrase || passphrase);
220+
setKey(c.context, pem, val.passphrase || passphrase);
146221
}
147222
} else {
148-
validateKeyOrCertOption('key', key);
149-
c.context.setKey(key, passphrase);
223+
setKey(c.context, key, passphrase);
150224
}
151225
}
152226

153-
const sigalgs = options.sigalgs;
154227
if (sigalgs !== undefined) {
155-
if (typeof sigalgs !== 'string') {
228+
if (typeof sigalgs !== 'string')
156229
throw new ERR_INVALID_ARG_TYPE('options.sigalgs', 'string', sigalgs);
157-
}
158230

159-
if (sigalgs === '') {
231+
if (sigalgs === '')
160232
throw new ERR_INVALID_ARG_VALUE('options.sigalgs', sigalgs);
161-
}
162233

163234
c.context.setSigalgs(sigalgs);
164235
}
165236

166-
const { privateKeyIdentifier, privateKeyEngine } = options;
167237
if (privateKeyIdentifier !== undefined) {
168238
if (privateKeyEngine === undefined) {
169239
// Engine is required when privateKeyIdentifier is present
@@ -193,113 +263,113 @@ exports.createSecureContext = function createSecureContext(options) {
193263
}
194264
}
195265

196-
if (options.ciphers && typeof options.ciphers !== 'string') {
197-
throw new ERR_INVALID_ARG_TYPE(
198-
'options.ciphers', 'string', options.ciphers);
199-
}
266+
if (ciphers !== undefined)
267+
validateString(ciphers, 'options.ciphers');
200268

201269
// Work around an OpenSSL API quirk. cipherList is for TLSv1.2 and below,
202270
// cipherSuites is for TLSv1.3 (and presumably any later versions). TLSv1.3
203271
// cipher suites all have a standard name format beginning with TLS_, so split
204272
// the ciphers and pass them to the appropriate API.
205-
const ciphers = (options.ciphers || tls.DEFAULT_CIPHERS).split(':');
206-
const cipherList = ciphers.filter((_) => !_.match(/^TLS_/) &&
207-
_.length > 0).join(':');
208-
const cipherSuites = ciphers.filter((_) => _.match(/^TLS_/)).join(':');
209-
210-
if (cipherSuites === '' && cipherList === '') {
211-
// Specifying empty cipher suites for both TLS1.2 and TLS1.3 is invalid, its
212-
// not possible to handshake with no suites.
213-
throw new ERR_INVALID_ARG_VALUE('options.ciphers', ciphers);
214-
}
273+
const { cipherList, cipherSuites } = processCiphers(ciphers);
215274

216275
c.context.setCipherSuites(cipherSuites);
217276
c.context.setCiphers(cipherList);
218277

219-
if (cipherSuites === '' && c.context.getMaxProto() > TLS1_2_VERSION &&
220-
c.context.getMinProto() < TLS1_3_VERSION)
278+
if (cipherSuites === '' &&
279+
c.context.getMaxProto() > TLS1_2_VERSION &&
280+
c.context.getMinProto() < TLS1_3_VERSION) {
221281
c.context.setMaxProto(TLS1_2_VERSION);
282+
}
222283

223-
if (cipherList === '' && c.context.getMinProto() < TLS1_3_VERSION &&
224-
c.context.getMaxProto() > TLS1_2_VERSION)
284+
if (cipherList === '' &&
285+
c.context.getMinProto() < TLS1_3_VERSION &&
286+
c.context.getMaxProto() > TLS1_2_VERSION) {
225287
c.context.setMinProto(TLS1_3_VERSION);
288+
}
226289

227-
if (options.ecdhCurve === undefined)
228-
c.context.setECDHCurve(tls.DEFAULT_ECDH_CURVE);
229-
else if (options.ecdhCurve)
230-
c.context.setECDHCurve(options.ecdhCurve);
290+
validateString(ecdhCurve, 'options.ecdhCurve');
291+
c.context.setECDHCurve(ecdhCurve);
231292

232-
if (options.dhparam) {
233-
const warning = c.context.setDHParam(options.dhparam);
293+
if (dhparam !== undefined) {
294+
validateKeyOrCertOption('dhparam', dhparam);
295+
const warning = c.context.setDHParam(dhparam);
234296
if (warning)
235297
process.emitWarning(warning, 'SecurityWarning');
236298
}
237299

238-
if (options.crl) {
239-
if (ArrayIsArray(options.crl)) {
240-
for (const crl of options.crl) {
241-
c.context.addCRL(crl);
300+
if (crl !== undefined) {
301+
if (ArrayIsArray(crl)) {
302+
for (const val of crl) {
303+
validateKeyOrCertOption('crl', val);
304+
c.context.addCRL(val);
242305
}
243306
} else {
244-
c.context.addCRL(options.crl);
307+
validateKeyOrCertOption('crl', crl);
308+
c.context.addCRL(crl);
245309
}
246310
}
247311

248-
if (options.sessionIdContext) {
249-
c.context.setSessionIdContext(options.sessionIdContext);
312+
if (sessionIdContext !== undefined) {
313+
validateString(sessionIdContext, 'options.sessionIdContext');
314+
c.context.setSessionIdContext(sessionIdContext);
250315
}
251316

252-
if (options.pfx) {
253-
if (!toBuf)
254-
toBuf = require('internal/crypto/util').toBuf;
255-
256-
if (ArrayIsArray(options.pfx)) {
257-
for (const pfx of options.pfx) {
258-
const raw = pfx.buf ? pfx.buf : pfx;
259-
const buf = toBuf(raw);
260-
const passphrase = pfx.passphrase || options.passphrase;
261-
if (passphrase) {
262-
c.context.loadPKCS12(buf, toBuf(passphrase));
317+
if (pfx !== undefined) {
318+
if (ArrayIsArray(pfx)) {
319+
for (const val of pfx) {
320+
const raw = val.buf ? val.buf : val;
321+
const pass = val.passphrase || passphrase;
322+
if (pass !== undefined) {
323+
c.context.loadPKCS12(toBuf(raw), toBuf(pass));
263324
} else {
264-
c.context.loadPKCS12(buf);
325+
c.context.loadPKCS12(toBuf(raw));
265326
}
266327
}
328+
} else if (passphrase) {
329+
c.context.loadPKCS12(toBuf(pfx), toBuf(passphrase));
267330
} else {
268-
const buf = toBuf(options.pfx);
269-
const passphrase = options.passphrase;
270-
if (passphrase) {
271-
c.context.loadPKCS12(buf, toBuf(passphrase));
272-
} else {
273-
c.context.loadPKCS12(buf);
274-
}
331+
c.context.loadPKCS12(toBuf(pfx));
275332
}
276333
}
277334

278335
// Do not keep read/write buffers in free list for OpenSSL < 1.1.0. (For
279336
// OpenSSL 1.1.0, buffers are malloced and freed without the use of a
280337
// freelist.)
281-
if (options.singleUse) {
338+
if (singleUse) {
282339
c.singleUse = true;
283340
c.context.setFreeListLength(0);
284341
}
285342

286-
if (typeof options.clientCertEngine === 'string') {
287-
if (c.context.setClientCertEngine)
288-
c.context.setClientCertEngine(options.clientCertEngine);
289-
else
343+
if (clientCertEngine !== undefined) {
344+
if (typeof c.context.setClientCertEngine !== 'function')
290345
throw new ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED();
291-
} else if (options.clientCertEngine != null) {
292-
throw new ERR_INVALID_ARG_TYPE('options.clientCertEngine',
293-
['string', 'null', 'undefined'],
294-
options.clientCertEngine);
346+
if (typeof clientCertEngine !== 'string') {
347+
throw new ERR_INVALID_ARG_TYPE('options.clientCertEngine',
348+
['string', 'null', 'undefined'],
349+
clientCertEngine);
350+
}
351+
c.context.setClientCertEngine(clientCertEngine);
295352
}
296353

297-
if (options.ticketKeys) {
298-
c.context.setTicketKeys(options.ticketKeys);
354+
if (ticketKeys !== undefined) {
355+
if (!isArrayBufferView(ticketKeys)) {
356+
throw new ERR_INVALID_ARG_TYPE(
357+
'options.ticketKeys',
358+
['Buffer', 'TypedArray', 'DataView'],
359+
ticketKeys);
360+
}
361+
if (ticketKeys.byteLength !== 48) {
362+
throw new ERR_INVALID_ARG_VALUE(
363+
'options.ticketKeys',
364+
ticketKeys.byteLenth,
365+
'must be exactly 48 bytes');
366+
}
367+
c.context.setTicketKeys(ticketKeys);
299368
}
300369

301-
if (options.sessionTimeout) {
302-
c.context.setSessionTimeout(options.sessionTimeout);
370+
if (sessionTimeout !== undefined) {
371+
validateInt32(sessionTimeout, 'options.sessionTimeout');
372+
c.context.setSessionTimeout(sessionTimeout);
303373
}
304374

305375
return c;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.