Skip to content

Commit a81dc9a

Browse 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

3 files changed

Lines changed: 309 additions & 287 deletions

File tree

‎lib/_tls_common.js‎

Lines 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-
lettoBuf=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
functiontoV(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
functionvalidateKeyOrCertOption(name,value){
@@ -90,80 +104,136 @@ function validateKeyOrCertOption(name, value) {
90104

91105
exports.SecureContext=SecureContext;
92106

107+
functionsetKey(context,key,passphrase){
108+
validateKeyOrCertOption('key',key);
109+
if(passphrase!=null)
110+
validateString(passphrase,'options.passphrase');
111+
context.setKey(key,passphrase);
112+
}
113+
114+
functionprocessCiphers(ciphers){
115+
ciphers=StringPrototypeSplit(ciphers||tls.DEFAULT_CIPHERS,':');
116+
117+
constcipherList=
118+
ArrayPrototypeJoin(
119+
ArrayPrototypeFilter(
120+
ciphers,
121+
(cipher)=>{
122+
returncipher.length>0&&
123+
!StringPrototypeStartsWith(cipher,'TLS_');
124+
}),':');
125+
126+
constcipherSuites=
127+
ArrayPrototypeJoin(
128+
ArrayPrototypeFilter(
129+
ciphers,
130+
(cipher)=>{
131+
returncipher.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+
thrownewERR_INVALID_ARG_VALUE('options.ciphers',ciphers);
139+
140+
return{ cipherList, cipherSuites };
141+
}
142+
143+
functionaddCACerts(context, ...certs){
144+
for(constcertofcerts){
145+
validateKeyOrCertOption('ca',cert);
146+
context.addCACert(cert);
147+
}
148+
}
149+
150+
functionsetCerts(context, ...certs){
151+
for(constcertofcerts){
152+
validateKeyOrCertOption('cert',cert);
153+
context.setCert(cert);
154+
}
155+
}
93156

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

97-
letsecureOptions=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-
constc=newSecureContext(options.secureProtocol,secureOptions,
102-
options.minVersion,options.maxVersion);
189+
constc=newSecureContext(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(constvalofca){
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(constvalofcert){
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-
constkey=options.key;
138-
constpassphrase=options.passphrase;
139215
if(key){
140216
if(ArrayIsArray(key)){
141217
for(constvalofkey){
142218
// eslint-disable-next-line eqeqeq
143219
constpem=(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-
constsigalgs=options.sigalgs;
154227
if(sigalgs!==undefined){
155-
if(typeofsigalgs!=='string'){
228+
if(typeofsigalgs!=='string')
156229
thrownewERR_INVALID_ARG_TYPE('options.sigalgs','string',sigalgs);
157-
}
158230

159-
if(sigalgs===''){
231+
if(sigalgs==='')
160232
thrownewERR_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&&typeofoptions.ciphers!=='string'){
197-
thrownewERR_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-
constciphers=(options.ciphers||tls.DEFAULT_CIPHERS).split(':');
206-
constcipherList=ciphers.filter((_)=>!_.match(/^TLS_/)&&
207-
_.length>0).join(':');
208-
constcipherSuites=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-
thrownewERR_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-
elseif(options.ecdhCurve)
230-
c.context.setECDHCurve(options.ecdhCurve);
290+
validateString(ecdhCurve,'options.ecdhCurve');
291+
c.context.setECDHCurve(ecdhCurve);
231292

232-
if(options.dhparam){
233-
constwarning=c.context.setDHParam(options.dhparam);
293+
if(dhparam!==undefined){
294+
validateKeyOrCertOption('dhparam',dhparam);
295+
constwarning=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(constcrlofoptions.crl){
241-
c.context.addCRL(crl);
300+
if(crl!==undefined){
301+
if(ArrayIsArray(crl)){
302+
for(constvalofcrl){
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(constpfxofoptions.pfx){
258-
constraw=pfx.buf ? pfx.buf : pfx;
259-
constbuf=toBuf(raw);
260-
constpassphrase=pfx.passphrase||options.passphrase;
261-
if(passphrase){
262-
c.context.loadPKCS12(buf,toBuf(passphrase));
317+
if(pfx!==undefined){
318+
if(ArrayIsArray(pfx)){
319+
for(constvalofpfx){
320+
constraw=val.buf ? val.buf : val;
321+
constpass=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+
}elseif(passphrase){
329+
c.context.loadPKCS12(toBuf(pfx),toBuf(passphrase));
267330
}else{
268-
constbuf=toBuf(options.pfx);
269-
constpassphrase=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(typeofoptions.clientCertEngine==='string'){
287-
if(c.context.setClientCertEngine)
288-
c.context.setClientCertEngine(options.clientCertEngine);
289-
else
343+
if(clientCertEngine!==undefined){
344+
if(typeofc.context.setClientCertEngine!=='function')
290345
thrownewERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED();
291-
}elseif(options.clientCertEngine!=null){
292-
thrownewERR_INVALID_ARG_TYPE('options.clientCertEngine',
293-
['string','null','undefined'],
294-
options.clientCertEngine);
346+
if(typeofclientCertEngine!=='string'){
347+
thrownewERR_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+
thrownewERR_INVALID_ARG_TYPE(
357+
'options.ticketKeys',
358+
['Buffer','TypedArray','DataView'],
359+
ticketKeys);
360+
}
361+
if(ticketKeys.byteLength!==48){
362+
thrownewERR_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
returnc;

0 commit comments

Comments
 (0)