23
23
24
24
const {
25
25
ArrayIsArray,
26
+ ArrayPrototypeFilter,
27
+ ArrayPrototypeJoin,
26
28
ObjectCreate,
29
+ StringPrototypeSplit,
30
+ StringPrototypeStartsWith,
27
31
} = primordials ;
28
32
29
33
const { parseCertString } = require ( 'internal/tls' ) ;
@@ -44,8 +48,15 @@ const {
44
48
TLS1_3_VERSION ,
45
49
} = internalBinding ( 'constants' ) . crypto ;
46
50
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' ) ;
49
60
50
61
function toV ( which , v , def ) {
51
62
if ( v == null ) v = def ;
@@ -75,7 +86,10 @@ function SecureContext(secureProtocol, secureOptions, minVersion, maxVersion) {
75
86
toV ( 'minimum' , minVersion , tls . DEFAULT_MIN_VERSION ) ,
76
87
toV ( 'maximum' , maxVersion , tls . DEFAULT_MAX_VERSION ) ) ;
77
88
78
- if ( secureOptions ) this . context . setOptions ( secureOptions ) ;
89
+ if ( secureOptions ) {
90
+ validateInteger ( secureOptions , 'secureOptions' ) ;
91
+ this . context . setOptions ( secureOptions ) ;
92
+ }
79
93
}
80
94
81
95
function validateKeyOrCertOption ( name , value ) {
@@ -90,80 +104,136 @@ function validateKeyOrCertOption(name, value) {
90
104
91
105
exports . SecureContext = SecureContext ;
92
106
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
+ }
93
156
94
157
exports . createSecureContext = function createSecureContext ( options ) {
95
158
if ( ! options ) options = { } ;
96
159
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 )
99
187
secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE ;
100
188
101
- const c = new SecureContext ( options . secureProtocol , secureOptions ,
102
- options . minVersion , options . maxVersion ) ;
189
+ const c = new SecureContext ( secureProtocol , secureOptions ,
190
+ minVersion , maxVersion ) ;
103
191
104
192
// 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.
106
195
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 ) ;
116
200
} else {
117
201
c . context . addRootCerts ( ) ;
118
202
}
119
203
120
- const { cert } = options ;
121
204
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 ) ;
131
209
}
132
210
133
211
// Set the key after the cert.
134
212
// `ssl_set_pkey` returns `0` when the key does not match the cert, but
135
213
// `ssl_set_cert` returns `1` and nullifies the key in the SSL structure
136
214
// which leads to the crash later on.
137
- const key = options . key ;
138
- const passphrase = options . passphrase ;
139
215
if ( key ) {
140
216
if ( ArrayIsArray ( key ) ) {
141
217
for ( const val of key ) {
142
218
// eslint-disable-next-line eqeqeq
143
219
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 ) ;
146
221
}
147
222
} else {
148
- validateKeyOrCertOption ( 'key' , key ) ;
149
- c . context . setKey ( key , passphrase ) ;
223
+ setKey ( c . context , key , passphrase ) ;
150
224
}
151
225
}
152
226
153
- const sigalgs = options . sigalgs ;
154
227
if ( sigalgs !== undefined ) {
155
- if ( typeof sigalgs !== 'string' ) {
228
+ if ( typeof sigalgs !== 'string' )
156
229
throw new ERR_INVALID_ARG_TYPE ( 'options.sigalgs' , 'string' , sigalgs ) ;
157
- }
158
230
159
- if ( sigalgs === '' ) {
231
+ if ( sigalgs === '' )
160
232
throw new ERR_INVALID_ARG_VALUE ( 'options.sigalgs' , sigalgs ) ;
161
- }
162
233
163
234
c . context . setSigalgs ( sigalgs ) ;
164
235
}
165
236
166
- const { privateKeyIdentifier, privateKeyEngine } = options ;
167
237
if ( privateKeyIdentifier !== undefined ) {
168
238
if ( privateKeyEngine === undefined ) {
169
239
// Engine is required when privateKeyIdentifier is present
@@ -193,113 +263,113 @@ exports.createSecureContext = function createSecureContext(options) {
193
263
}
194
264
}
195
265
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' ) ;
200
268
201
269
// Work around an OpenSSL API quirk. cipherList is for TLSv1.2 and below,
202
270
// cipherSuites is for TLSv1.3 (and presumably any later versions). TLSv1.3
203
271
// cipher suites all have a standard name format beginning with TLS_, so split
204
272
// the ciphers and pass them to the appropriate API.
205
- const ciphers = ( options . ciphers || tls . DEFAULT_CIPHERS ) . split ( ':' ) ;
206
- const cipherList = ciphers . filter ( ( _ ) => ! _ . match ( / ^ T L S _ / ) &&
207
- _ . length > 0 ) . join ( ':' ) ;
208
- const cipherSuites = ciphers . filter ( ( _ ) => _ . match ( / ^ T L S _ / ) ) . 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 ) ;
215
274
216
275
c . context . setCipherSuites ( cipherSuites ) ;
217
276
c . context . setCiphers ( cipherList ) ;
218
277
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 ) {
221
281
c . context . setMaxProto ( TLS1_2_VERSION ) ;
282
+ }
222
283
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 ) {
225
287
c . context . setMinProto ( TLS1_3_VERSION ) ;
288
+ }
226
289
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 ) ;
231
292
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 ) ;
234
296
if ( warning )
235
297
process . emitWarning ( warning , 'SecurityWarning' ) ;
236
298
}
237
299
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 ) ;
242
305
}
243
306
} else {
244
- c . context . addCRL ( options . crl ) ;
307
+ validateKeyOrCertOption ( 'crl' , crl ) ;
308
+ c . context . addCRL ( crl ) ;
245
309
}
246
310
}
247
311
248
- if ( options . sessionIdContext ) {
249
- c . context . setSessionIdContext ( options . sessionIdContext ) ;
312
+ if ( sessionIdContext !== undefined ) {
313
+ validateString ( sessionIdContext , 'options.sessionIdContext' ) ;
314
+ c . context . setSessionIdContext ( sessionIdContext ) ;
250
315
}
251
316
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 ) ) ;
263
324
} else {
264
- c . context . loadPKCS12 ( buf ) ;
325
+ c . context . loadPKCS12 ( toBuf ( raw ) ) ;
265
326
}
266
327
}
328
+ } else if ( passphrase ) {
329
+ c . context . loadPKCS12 ( toBuf ( pfx ) , toBuf ( passphrase ) ) ;
267
330
} 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 ) ) ;
275
332
}
276
333
}
277
334
278
335
// Do not keep read/write buffers in free list for OpenSSL < 1.1.0. (For
279
336
// OpenSSL 1.1.0, buffers are malloced and freed without the use of a
280
337
// freelist.)
281
- if ( options . singleUse ) {
338
+ if ( singleUse ) {
282
339
c . singleUse = true ;
283
340
c . context . setFreeListLength ( 0 ) ;
284
341
}
285
342
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' )
290
345
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 ) ;
295
352
}
296
353
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 ) ;
299
368
}
300
369
301
- if ( options . sessionTimeout ) {
302
- c . context . setSessionTimeout ( options . sessionTimeout ) ;
370
+ if ( sessionTimeout !== undefined ) {
371
+ validateInt32 ( sessionTimeout , 'options.sessionTimeout' ) ;
372
+ c . context . setSessionTimeout ( sessionTimeout ) ;
303
373
}
304
374
305
375
return c ;
0 commit comments