mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
tls: support TLSv1.3
This introduces TLS1.3 support and makes it the default max protocol, but also supports CLI/NODE_OPTIONS switches to disable it if necessary. TLS1.3 is a major update to the TLS protocol, with many security enhancements. It should be preferred over TLS1.2 whenever possible. TLS1.3 is different enough that even though the OpenSSL APIs are technically API/ABI compatible, that when TLS1.3 is negotiated, the timing of protocol records and of callbacks broke assumptions hard-coded into the 'tls' module. This change introduces no API incompatibilities when TLS1.2 is negotiated. It is the intention that it be backported to current and LTS release lines with the default maximum TLS protocol reset to 'TLSv1.2'. This will allow users of those lines to explicitly enable TLS1.3 if they want. API incompatibilities between TLS1.2 and TLS1.3 are: - Renegotiation is not supported by TLS1.3 protocol, attempts to call `.renegotiate()` will always fail. - Compiling against a system OpenSSL lower than 1.1.1 is no longer supported (OpenSSL-1.1.0 used to be supported with configure flags). - Variations of `conn.write('data'); conn.destroy()` have undefined behaviour according to the streams API. They may or may not send the 'data', and may or may not cause a ERR_STREAM_DESTROYED error to be emitted. This has always been true, but conditions under which the write suceeds is slightly but observably different when TLS1.3 is negotiated vs when TLS1.2 or below is negotiated. - If TLS1.3 is negotiated, and a server calls `conn.end()` in its 'secureConnection' listener without any data being written, the client will not receive session tickets (no 'session' events will be emitted, and `conn.getSession()` will never return a resumable session). - The return value of `conn.getSession()` API may not return a resumable session if called right after the handshake. The effect will be that clients using the legacy `getSession()` API will resume sessions if TLS1.2 is negotiated, but will do full handshakes if TLS1.3 is negotiated. See https://github.com/nodejs/node/pull/25831 for more information. PR-URL: https://github.com/nodejs/node/pull/26209 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org>
This commit is contained in:
parent
4306300b5e
commit
42dbaed460
@ -443,21 +443,43 @@ added: v4.0.0
|
||||
Specify an alternative default TLS cipher list. Requires Node.js to be built
|
||||
with crypto support (default).
|
||||
|
||||
### `--tls-v1.0`
|
||||
### `--tls-max-v1.2`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Enable TLSv1.0 and greater in default [secureProtocol][]. Use for compatibility
|
||||
with old TLS clients or servers.
|
||||
Set default [`maxVersion`][] to `'TLSv1.2'`. Use to disable support for TLSv1.3.
|
||||
|
||||
### `--tls-v1.1`
|
||||
### `--tls-max-v1.3`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Enable TLSv1.1 and greater in default [secureProtocol][]. Use for compatibility
|
||||
with old TLS clients or servers.
|
||||
Set default [`maxVersion`][] to `'TLSv1.3'`. Use to enable support for TLSv1.3.
|
||||
|
||||
### `--tls-min-v1.0`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Set default [`minVersion`][] to `'TLSv1'`. Use for compatibility with old TLS
|
||||
clients or servers.
|
||||
|
||||
### `--tls-min-v1.1`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Set default [`minVersion`][] to `'TLSv1.1'`. Use for compatibility with old TLS
|
||||
clients or servers.
|
||||
|
||||
### `--tls-min-v1.3`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Set default [`minVersion`][] to `'TLSv1.3'`. Use to disable support for TLSv1.2
|
||||
in favour of TLSv1.3, which is more secure.
|
||||
|
||||
### `--trace-deprecation`
|
||||
<!-- YAML
|
||||
@ -896,6 +918,8 @@ greater than `4` (its current default value). For more information, see the
|
||||
[`--openssl-config`]: #cli_openssl_config_file
|
||||
[`Buffer`]: buffer.html#buffer_class_buffer
|
||||
[`SlowBuffer`]: buffer.html#buffer_class_slowbuffer
|
||||
[`maxVersion`]: tls.html#tls_tls_createsecurecontext_options
|
||||
[`minVersion`]: tls.html#tls_tls_createsecurecontext_options
|
||||
[`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn
|
||||
[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
|
||||
[REPL]: repl.html
|
||||
@ -907,4 +931,3 @@ greater than `4` (its current default value). For more information, see the
|
||||
[experimental ECMAScript Module]: esm.html#esm_loader_hooks
|
||||
[libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html
|
||||
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
|
||||
[secureProtocol]: tls.html#tls_tls_createsecurecontext_options
|
||||
|
119
doc/api/tls.md
119
doc/api/tls.md
@ -104,6 +104,9 @@ not required and a default ECDHE curve will be used. The `ecdhCurve` property
|
||||
can be used when creating a TLS Server to specify the list of names of supported
|
||||
curves to use, see [`tls.createServer()`] for more info.
|
||||
|
||||
Perfect Forward Secrecy was optional up to TLSv1.2, but it is not optional for
|
||||
TLSv1.3, because all TLSv1.3 cipher suites use ECDHE.
|
||||
|
||||
### ALPN and SNI
|
||||
|
||||
<!-- type=misc -->
|
||||
@ -136,6 +139,8 @@ threshold is exceeded. The limits are configurable:
|
||||
The default renegotiation limits should not be modified without a full
|
||||
understanding of the implications and risks.
|
||||
|
||||
TLSv1.3 does not support renegotiation.
|
||||
|
||||
### Session Resumption
|
||||
|
||||
Establishing a TLS session can be relatively slow. The process can be sped
|
||||
@ -176,6 +181,10 @@ as for resumption with session tickets. For debugging, if
|
||||
[`tls.TLSSocket.getTLSTicket()`][] returns a value, the session data contains a
|
||||
ticket, otherwise it contains client-side session state.
|
||||
|
||||
With TLSv1.3, be aware that multiple tickets may be sent by the server,
|
||||
resulting in multiple `'session'` events, see [`'session'`][] for more
|
||||
information.
|
||||
|
||||
Single process servers need no specific implementation to use session tickets.
|
||||
To use session tickets across server restarts or load balancers, servers must
|
||||
all have the same ticket keys. There are three 16-byte keys internally, but the
|
||||
@ -230,6 +239,9 @@ Node.js is built with a default suite of enabled and disabled TLS ciphers.
|
||||
Currently, the default cipher suite is:
|
||||
|
||||
```txt
|
||||
TLS_AES_256_GCM_SHA384:
|
||||
TLS_CHACHA20_POLY1305_SHA256:
|
||||
TLS_AES_128_GCM_SHA256:
|
||||
ECDHE-RSA-AES128-GCM-SHA256:
|
||||
ECDHE-ECDSA-AES128-GCM-SHA256:
|
||||
ECDHE-RSA-AES256-GCM-SHA384:
|
||||
@ -270,7 +282,19 @@ The default can also be replaced on a per client or server basis using the
|
||||
in [`tls.createServer()`], [`tls.connect()`], and when creating new
|
||||
[`tls.TLSSocket`]s.
|
||||
|
||||
Consult [OpenSSL cipher list format documentation][] for details on the format.
|
||||
The ciphers list can contain a mixture of TLSv1.3 cipher suite names, the ones
|
||||
that start with `'TLS_'`, and specifications for TLSv1.2 and below cipher
|
||||
suites. The TLSv1.2 ciphers support a legacy specification format, consult
|
||||
the OpenSSL [cipher list format][] documentation for details, but those
|
||||
specifications do *not* apply to TLSv1.3 ciphers. The TLSv1.3 suites can only
|
||||
be enabled by including their full name in the cipher list. They cannot, for
|
||||
example, be enabled or disabled by using the legacy TLSv1.2 `'EECDH'` or
|
||||
`'!EECDH'` specification.
|
||||
|
||||
Despite the relative order of TLSv1.3 and TLSv1.2 cipher suites, the TLSv1.3
|
||||
protocol is significantly more secure than TLSv1.2, and will always be chosen
|
||||
over TLSv1.2 if the handshake indicates it is supported, and if any TLSv1.3
|
||||
cipher suites are enabled.
|
||||
|
||||
The default cipher suite included within Node.js has been carefully
|
||||
selected to reflect current security best practices and risk mitigation.
|
||||
@ -289,7 +313,18 @@ Old clients that rely on insecure and deprecated RC4 or DES-based ciphers
|
||||
(like Internet Explorer 6) cannot complete the handshaking process with
|
||||
the default configuration. If these clients _must_ be supported, the
|
||||
[TLS recommendations] may offer a compatible cipher suite. For more details
|
||||
on the format, see the [OpenSSL cipher list format documentation].
|
||||
on the format, see the OpenSSL [cipher list format][] documentation.
|
||||
|
||||
There are only 5 TLSv1.3 cipher suites:
|
||||
- `'TLS_AES_256_GCM_SHA384'`
|
||||
- `'TLS_CHACHA20_POLY1305_SHA256'`
|
||||
- `'TLS_AES_128_GCM_SHA256'`
|
||||
- `'TLS_AES_128_CCM_SHA256'`
|
||||
- `'TLS_AES_128_CCM_8_SHA256'`
|
||||
|
||||
The first 3 are enabled by default. The last 2 `CCM`-based suites are supported
|
||||
by TLSv1.3 because they may be more performant on constrained systems, but they
|
||||
are not enabled by default since they offer less security.
|
||||
|
||||
## Class: tls.Server
|
||||
<!-- YAML
|
||||
@ -634,11 +669,11 @@ On the client, the `session` can be provided to the `session` option of
|
||||
|
||||
See [Session Resumption][] for more information.
|
||||
|
||||
Note: For TLS1.2 and below, [`tls.TLSSocket.getSession()`][] can be called once
|
||||
the handshake is complete. For TLS1.3, only ticket based resumption is allowed
|
||||
Note: For TLSv1.2 and below, [`tls.TLSSocket.getSession()`][] can be called once
|
||||
the handshake is complete. For TLSv1.3, only ticket based resumption is allowed
|
||||
by the protocol, multiple tickets are sent, and the tickets aren't sent until
|
||||
later, after the handshake completes, so it is necessary to wait for the
|
||||
`'session'` event to get a resumable session. Future-proof applications are
|
||||
`'session'` event to get a resumable session. Applications are
|
||||
recommended to use the `'session'` event instead of `getSession()` to ensure
|
||||
they will work for all TLS protocol versions. Applications that only expect to
|
||||
get or use 1 session should listen for this event only once:
|
||||
@ -731,7 +766,7 @@ changes:
|
||||
|
||||
Returns an object containing information on the negotiated cipher suite.
|
||||
|
||||
For example: `{ name: 'AES256-SHA', version: 'TLSv1/SSLv3' }`.
|
||||
For example: `{ name: 'AES256-SHA', version: 'TLSv1.2' }`.
|
||||
|
||||
See
|
||||
[OpenSSL](https://www.openssl.org/docs/man1.1.1/man3/SSL_CIPHER_get_name.html)
|
||||
@ -904,12 +939,13 @@ be returned for server sockets or disconnected client sockets.
|
||||
|
||||
Protocol versions are:
|
||||
|
||||
* `'SSLv3'`
|
||||
* `'TLSv1'`
|
||||
* `'TLSv1.1'`
|
||||
* `'TLSv1.2'`
|
||||
* `'SSLv3'`
|
||||
* `'TLSv1.3'`
|
||||
|
||||
See <https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_version.html> for more
|
||||
See <https://www.openssl.org/docs/man1.1.1/man3/SSL_get_version.html> for more
|
||||
information.
|
||||
|
||||
### tlsSocket.getSession()
|
||||
@ -926,8 +962,8 @@ for debugging.
|
||||
|
||||
See [Session Resumption][] for more information.
|
||||
|
||||
Note: `getSession()` works only for TLS1.2 and below. Future-proof applications
|
||||
should use the [`'session'`][] event.
|
||||
Note: `getSession()` works only for TLSv1.2 and below. For TLSv1.3, applications
|
||||
must use the [`'session'`][] event (it also works for TLSv1.2 and below).
|
||||
|
||||
### tlsSocket.getTLSTicket()
|
||||
<!-- YAML
|
||||
@ -1009,8 +1045,12 @@ added: v0.11.8
|
||||
verification fails; `err.code` contains the OpenSSL error code. **Default:**
|
||||
`true`.
|
||||
* `requestCert`
|
||||
* `callback` {Function} A function that will be called when the renegotiation
|
||||
request has been completed.
|
||||
* `callback` {Function} If `renegotiate()` returned `true`, callback is
|
||||
attached once to the `'secure'` event. If it returned `false`, it will be
|
||||
called in the next tick with `ERR_TLS_RENEGOTIATE`, unless the `tlsSocket`
|
||||
has been destroyed, in which case it will not be called at all.
|
||||
|
||||
* Returns: {boolean} `true` if renegotiation was initiated, `false` otherwise.
|
||||
|
||||
The `tlsSocket.renegotiate()` method initiates a TLS renegotiation process.
|
||||
Upon completion, the `callback` function will be passed a single argument
|
||||
@ -1022,6 +1062,9 @@ connection has been established.
|
||||
When running as the server, the socket will be destroyed with an error after
|
||||
`handshakeTimeout` timeout.
|
||||
|
||||
For TLSv1.3, renegotiation cannot be initiated, it is not supported by the
|
||||
protocol.
|
||||
|
||||
### tlsSocket.setMaxSendFragment(size)
|
||||
<!-- YAML
|
||||
added: v0.11.11
|
||||
@ -1220,6 +1263,9 @@ argument.
|
||||
<!-- YAML
|
||||
added: v0.11.13
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/26209
|
||||
description: TLSv1.3 support added.
|
||||
- version: v11.5.0
|
||||
pr-url: https://github.com/nodejs/node/pull/24733
|
||||
description: The `ca:` option now supports `BEGIN TRUSTED CERTIFICATE`.
|
||||
@ -1310,15 +1356,22 @@ changes:
|
||||
`object.passphrase` is optional. Encrypted keys will be decrypted with
|
||||
`object.passphrase` if provided, or `options.passphrase` if it is not.
|
||||
* `maxVersion` {string} Optionally set the maximum TLS version to allow. One
|
||||
of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the
|
||||
`secureProtocol` option, use one or the other. **Default:** `'TLSv1.2'`.
|
||||
of `TLSv1.3`, `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified
|
||||
along with the `secureProtocol` option, use one or the other.
|
||||
**Default:** `'TLSv1.3'`, unless changed using CLI options. Using
|
||||
`--tls-max-v1.2` sets the default to `'TLSv1.2`'. Using `--tls-max-v1.3`
|
||||
sets the default to `'TLSv1.3'`. If multiple of the options are provided,
|
||||
the highest maximum is used.
|
||||
* `minVersion` {string} Optionally set the minimum TLS version to allow. One
|
||||
of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the
|
||||
`secureProtocol` option, use one or the other. It is not recommended to use
|
||||
less than TLSv1.2, but it may be required for interoperability.
|
||||
of `TLSv1.3`, `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified
|
||||
along with the `secureProtocol` option, use one or the other. It is not
|
||||
recommended to use less than TLSv1.2, but it may be required for
|
||||
interoperability.
|
||||
**Default:** `'TLSv1.2'`, unless changed using CLI options. Using
|
||||
`--tls-v1.0` changes the default to `'TLSv1'`. Using `--tls-v1.1` changes
|
||||
the default to `'TLSv1.1'`.
|
||||
`--tls-min-v1.0` sets the default to `'TLSv1'`. Using `--tls-min-v1.1` sets
|
||||
the default to `'TLSv1.1'`. Using `--tls-min-v1.3` sets the default to
|
||||
`'TLSv1.3'`. If multiple of the options are provided, the lowest minimum is
|
||||
used.
|
||||
* `passphrase` {string} Shared passphrase used for a single private key and/or
|
||||
a PFX.
|
||||
* `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded
|
||||
@ -1334,12 +1387,15 @@ changes:
|
||||
which is not usually necessary. This should be used carefully if at all!
|
||||
Value is a numeric bitmask of the `SSL_OP_*` options from
|
||||
[OpenSSL Options][].
|
||||
* `secureProtocol` {string} The TLS protocol version to use. The possible
|
||||
values are listed as [SSL_METHODS][], use the function names as strings. For
|
||||
example, use `'TLSv1_1_method'` to force TLS version 1.1, or `'TLS_method'`
|
||||
to allow any TLS protocol version. It is not recommended to use TLS versions
|
||||
less than 1.2, but it may be required for interoperability. **Default:**
|
||||
none, see `minVersion`.
|
||||
* `secureProtocol` {string} Legacy mechanism to select the TLS protocol
|
||||
version to use, it does not support independent control of the minimum and
|
||||
maximum version, and does not support limiting the protocol to TLSv1.3. Use
|
||||
`minVersion` and `maxVersion` instead. The possible values are listed as
|
||||
[SSL_METHODS][], use the function names as strings. For example, use
|
||||
`'TLSv1_1_method'` to force TLS version 1.1, or `'TLS_method'` to allow any
|
||||
TLS protocol version up to TLSv1.3. It is not recommended to use TLS
|
||||
versions less than 1.2, but it may be required for interoperability.
|
||||
**Default:** none, see `minVersion`.
|
||||
* `sessionIdContext` {string} Opaque identifier used by servers to ensure
|
||||
session state is not shared between applications. Unused by clients.
|
||||
|
||||
@ -1457,10 +1513,15 @@ added: v0.10.2
|
||||
|
||||
* Returns: {string[]}
|
||||
|
||||
Returns an array with the names of the supported SSL ciphers.
|
||||
Returns an array with the names of the supported TLS ciphers. The names are
|
||||
lower-case for historical reasons, but must be uppercased to be used in
|
||||
the `ciphers` option of [`tls.createSecureContext()`][].
|
||||
|
||||
Cipher names that start with `'tls_'` are for TLSv1.3, all the others are for
|
||||
TLSv1.2 and below.
|
||||
|
||||
```js
|
||||
console.log(tls.getCiphers()); // ['AES128-SHA', 'AES256-SHA', ...]
|
||||
console.log(tls.getCiphers()); // ['aes128-gcm-sha256', 'aes128-sha', ...]
|
||||
```
|
||||
|
||||
## tls.DEFAULT_ECDH_CURVE
|
||||
@ -1619,16 +1680,16 @@ where `secureSocket` has the same API as `pair.cleartext`.
|
||||
[Forward secrecy]: https://en.wikipedia.org/wiki/Perfect_forward_secrecy
|
||||
[OCSP request]: https://en.wikipedia.org/wiki/OCSP_stapling
|
||||
[OpenSSL Options]: crypto.html#crypto_openssl_options
|
||||
[OpenSSL cipher list format documentation]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html#CIPHER-LIST-FORMAT
|
||||
[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy
|
||||
[RFC 2246]: https://www.ietf.org/rfc/rfc2246.txt
|
||||
[RFC 5077]: https://tools.ietf.org/html/rfc5077
|
||||
[RFC 5929]: https://tools.ietf.org/html/rfc5929
|
||||
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html#Dealing-with-Protocol-Methods
|
||||
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.1/man7/ssl.html#Dealing-with-Protocol-Methods
|
||||
[Session Resumption]: #tls_session_resumption
|
||||
[Stream]: stream.html#stream_stream
|
||||
[TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS
|
||||
[asn1.js]: https://www.npmjs.com/package/asn1.js
|
||||
[certificate object]: #tls_certificate_object
|
||||
[cipher list format]: https://www.openssl.org/docs/man1.1.1/man1/ciphers.html#CIPHER-LIST-FORMAT
|
||||
[modifying the default cipher suite]: #tls_modifying_the_default_tls_cipher_suite
|
||||
[specific attacks affecting larger AES key sizes]: https://www.schneier.com/blog/archives/2009/07/another_new_aes.html
|
||||
|
22
doc/node.1
22
doc/node.1
@ -236,13 +236,23 @@ Specify process.title on startup.
|
||||
Specify an alternative default TLS cipher list.
|
||||
Requires Node.js to be built with crypto support. (Default)
|
||||
.
|
||||
.It Fl -tls-v1.0
|
||||
Enable TLSv1.0 and greater in default secureProtocol. Use for compatibility
|
||||
with old TLS clients or servers.
|
||||
.It Fl -tls-max-v1.2
|
||||
Set default maxVersion to 'TLSv1.2'. Use to disable support for TLSv1.3.
|
||||
.
|
||||
.It Fl -tls-v1.1
|
||||
Enable TLSv1.1 and greater in default secureProtocol. Use for compatibility
|
||||
with old TLS clients or servers.
|
||||
.It Fl -tls-max-v1.3
|
||||
Set default maxVersion to 'TLSv1.3'. Use to enable support for TLSv1.3.
|
||||
.
|
||||
.It Fl -tls-min-v1.0
|
||||
Set default minVersion to 'TLSv1'. Use for compatibility with old TLS clients
|
||||
or servers.
|
||||
.
|
||||
.It Fl -tls-min-v1.1
|
||||
Set default minVersion to 'TLSv1.1'. Use for compatibility with old TLS clients
|
||||
or servers.
|
||||
.
|
||||
.It Fl -tls-min-v1.3
|
||||
Set default minVersion to 'TLSv1.3'. Use to disable support for TLSv1.2 in
|
||||
favour of TLSv1.3, which is more secure.
|
||||
.
|
||||
.It Fl -trace-deprecation
|
||||
Print stack traces for deprecations.
|
||||
|
@ -27,6 +27,7 @@ const tls = require('tls');
|
||||
const {
|
||||
ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_INVALID_OPT_VALUE,
|
||||
ERR_TLS_INVALID_PROTOCOL_VERSION,
|
||||
ERR_TLS_PROTOCOL_VERSION_CONFLICT,
|
||||
} = require('internal/errors').codes;
|
||||
@ -35,6 +36,7 @@ const {
|
||||
TLS1_VERSION,
|
||||
TLS1_1_VERSION,
|
||||
TLS1_2_VERSION,
|
||||
TLS1_3_VERSION,
|
||||
} = internalBinding('constants').crypto;
|
||||
|
||||
// Lazily loaded from internal/crypto/util.
|
||||
@ -45,6 +47,7 @@ function toV(which, v, def) {
|
||||
if (v === 'TLSv1') return TLS1_VERSION;
|
||||
if (v === 'TLSv1.1') return TLS1_1_VERSION;
|
||||
if (v === 'TLSv1.2') return TLS1_2_VERSION;
|
||||
if (v === 'TLSv1.3') return TLS1_3_VERSION;
|
||||
throw new ERR_TLS_INVALID_PROTOCOL_VERSION(v, which);
|
||||
}
|
||||
|
||||
@ -148,10 +151,35 @@ exports.createSecureContext = function createSecureContext(options) {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.ciphers)
|
||||
c.context.setCiphers(options.ciphers);
|
||||
else
|
||||
c.context.setCiphers(tls.DEFAULT_CIPHERS);
|
||||
if (options.ciphers && typeof options.ciphers !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
'options.ciphers', 'string', options.ciphers);
|
||||
}
|
||||
|
||||
// Work around an OpenSSL API quirk. cipherList is for TLSv1.2 and below,
|
||||
// cipherSuites is for TLSv1.3 (and presumably any later versions). TLSv1.3
|
||||
// cipher suites all have a standard name format beginning with TLS_, so split
|
||||
// the ciphers and pass them to the appropriate API.
|
||||
const ciphers = (options.ciphers || tls.DEFAULT_CIPHERS).split(':');
|
||||
const cipherList = ciphers.filter((_) => !_.match(/^TLS_/)).join(':');
|
||||
const cipherSuites = ciphers.filter((_) => _.match(/^TLS_/)).join(':');
|
||||
|
||||
if (cipherSuites === '' && cipherList === '') {
|
||||
// Specifying empty cipher suites for both TLS1.2 and TLS1.3 is invalid, its
|
||||
// not possible to handshake with no suites.
|
||||
throw ERR_INVALID_OPT_VALUE('ciphers', ciphers);
|
||||
}
|
||||
|
||||
c.context.setCipherSuites(cipherSuites);
|
||||
c.context.setCiphers(cipherList);
|
||||
|
||||
if (cipherSuites === '' && c.context.getMaxProto() > TLS1_2_VERSION &&
|
||||
c.context.getMinProto() < TLS1_3_VERSION)
|
||||
c.context.setMaxProto(TLS1_2_VERSION);
|
||||
|
||||
if (cipherList === '' && c.context.getMinProto() < TLS1_3_VERSION &&
|
||||
c.context.getMaxProto() > TLS1_2_VERSION)
|
||||
c.context.setMinProto(TLS1_3_VERSION);
|
||||
|
||||
if (options.ecdhCurve === undefined)
|
||||
c.context.setECDHCurve(tls.DEFAULT_ECDH_CURVE);
|
||||
|
@ -65,7 +65,7 @@ let ipServernameWarned = false;
|
||||
// Server side times how long a handshake is taking to protect against slow
|
||||
// handshakes being used for DoS.
|
||||
function onhandshakestart(now) {
|
||||
debug('onhandshakestart');
|
||||
debug('server onhandshakestart');
|
||||
|
||||
const { lastHandshakeTime } = this;
|
||||
assert(now >= lastHandshakeTime,
|
||||
@ -83,6 +83,9 @@ function onhandshakestart(now) {
|
||||
this.handshakes++;
|
||||
|
||||
const owner = this[owner_symbol];
|
||||
|
||||
assert(owner._tlsOptions.isServer);
|
||||
|
||||
if (this.handshakes > tls.CLIENT_RENEG_LIMIT) {
|
||||
owner._emitTLSError(new ERR_TLS_SESSION_ATTACK());
|
||||
return;
|
||||
@ -93,9 +96,10 @@ function onhandshakestart(now) {
|
||||
}
|
||||
|
||||
function onhandshakedone() {
|
||||
debug('onhandshakedone');
|
||||
debug('server onhandshakedone');
|
||||
|
||||
const owner = this[owner_symbol];
|
||||
assert(owner._tlsOptions.isServer);
|
||||
|
||||
// `newSession` callback wasn't called yet
|
||||
if (owner._newSessionPending) {
|
||||
@ -108,10 +112,15 @@ function onhandshakedone() {
|
||||
|
||||
|
||||
function loadSession(hello) {
|
||||
debug('server onclienthello',
|
||||
'sessionid.len', hello.sessionId.length,
|
||||
'ticket?', hello.tlsTicket
|
||||
);
|
||||
const owner = this[owner_symbol];
|
||||
|
||||
var once = false;
|
||||
function onSession(err, session) {
|
||||
debug('server resumeSession callback(err %j, sess? %s)', err, !!session);
|
||||
if (once)
|
||||
return owner.destroy(new ERR_MULTIPLE_CALLBACK());
|
||||
once = true;
|
||||
@ -193,6 +202,8 @@ function requestOCSP(socket, info) {
|
||||
|
||||
let once = false;
|
||||
const onOCSP = (err, response) => {
|
||||
debug('server OCSPRequest done', 'handle?', !!socket._handle, 'once?', once,
|
||||
'response?', !!response, 'err?', err);
|
||||
if (once)
|
||||
return socket.destroy(new ERR_MULTIPLE_CALLBACK());
|
||||
once = true;
|
||||
@ -208,6 +219,7 @@ function requestOCSP(socket, info) {
|
||||
requestOCSPDone(socket);
|
||||
};
|
||||
|
||||
debug('server oncertcb emit OCSPRequest');
|
||||
socket.server.emit('OCSPRequest',
|
||||
ctx.getCertificate(),
|
||||
ctx.getIssuer(),
|
||||
@ -215,16 +227,17 @@ function requestOCSP(socket, info) {
|
||||
}
|
||||
|
||||
function requestOCSPDone(socket) {
|
||||
debug('server certcb done');
|
||||
try {
|
||||
socket._handle.certCbDone();
|
||||
} catch (e) {
|
||||
debug('server certcb done errored', e);
|
||||
socket.destroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onnewsessionclient(sessionId, session) {
|
||||
debug('client onnewsessionclient', sessionId, session);
|
||||
debug('client emit session');
|
||||
const owner = this[owner_symbol];
|
||||
owner.emit('session', session);
|
||||
}
|
||||
@ -233,8 +246,9 @@ function onnewsession(sessionId, session) {
|
||||
debug('onnewsession');
|
||||
const owner = this[owner_symbol];
|
||||
|
||||
// XXX(sam) no server to emit the event on, but handshake won't continue
|
||||
// unless newSessionDone() is called, should it be?
|
||||
// TODO(@sam-github) no server to emit the event on, but handshake won't
|
||||
// continue unless newSessionDone() is called, should it be, or is that
|
||||
// situation unreachable, or only occurring during shutdown?
|
||||
if (!owner.server)
|
||||
return;
|
||||
|
||||
@ -263,11 +277,15 @@ function onnewsession(sessionId, session) {
|
||||
|
||||
|
||||
function onocspresponse(resp) {
|
||||
debug('client onocspresponse');
|
||||
this[owner_symbol].emit('OCSPResponse', resp);
|
||||
}
|
||||
|
||||
function onerror(err) {
|
||||
const owner = this[owner_symbol];
|
||||
debug('%s onerror %s had? %j',
|
||||
owner._tlsOptions.isServer ? 'server' : 'client', err,
|
||||
owner._hadError);
|
||||
|
||||
if (owner._hadError)
|
||||
return;
|
||||
@ -285,7 +303,7 @@ function onerror(err) {
|
||||
// Ignore server's authorization errors
|
||||
owner.destroy();
|
||||
} else {
|
||||
// Throw error
|
||||
// Emit error
|
||||
owner._emitTLSError(err);
|
||||
}
|
||||
}
|
||||
@ -293,6 +311,11 @@ function onerror(err) {
|
||||
// Used by both client and server TLSSockets to start data flowing from _handle,
|
||||
// read(0) causes a StreamBase::ReadStart, via Socket._read.
|
||||
function initRead(tlsSocket, socket) {
|
||||
debug('%s initRead',
|
||||
tlsSocket._tlsOptions.isServer ? 'server' : 'client',
|
||||
'handle?', !!tlsSocket._handle,
|
||||
'buffered?', !!socket && socket.readableLength
|
||||
);
|
||||
// If we were destroyed already don't bother reading
|
||||
if (!tlsSocket._handle)
|
||||
return;
|
||||
@ -493,12 +516,17 @@ TLSSocket.prototype._destroySSL = function _destroySSL() {
|
||||
this.ssl = null;
|
||||
};
|
||||
|
||||
// Constructor guts, arbitrarily factored out.
|
||||
TLSSocket.prototype._init = function(socket, wrap) {
|
||||
var options = this._tlsOptions;
|
||||
var ssl = this._handle;
|
||||
|
||||
this.server = options.server;
|
||||
|
||||
debug('%s _init',
|
||||
options.isServer ? 'server' : 'client',
|
||||
'handle?', !!ssl
|
||||
);
|
||||
|
||||
// Clients (!isServer) always request a cert, servers request a client cert
|
||||
// only on explicit configuration.
|
||||
const requestCert = !!options.requestCert || !options.isServer;
|
||||
@ -529,7 +557,10 @@ TLSSocket.prototype._init = function(socket, wrap) {
|
||||
}
|
||||
} else {
|
||||
ssl.onhandshakestart = noop;
|
||||
ssl.onhandshakedone = this._finishInit.bind(this);
|
||||
ssl.onhandshakedone = () => {
|
||||
debug('client onhandshakedone');
|
||||
this._finishInit();
|
||||
};
|
||||
ssl.onocspresponse = onocspresponse;
|
||||
|
||||
if (options.session)
|
||||
@ -600,6 +631,11 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
|
||||
if (callback !== undefined && typeof callback !== 'function')
|
||||
throw new ERR_INVALID_CALLBACK();
|
||||
|
||||
debug('%s renegotiate()',
|
||||
this._tlsOptions.isServer ? 'server' : 'client',
|
||||
'destroyed?', this.destroyed
|
||||
);
|
||||
|
||||
if (this.destroyed)
|
||||
return;
|
||||
|
||||
@ -667,9 +703,25 @@ TLSSocket.prototype._releaseControl = function() {
|
||||
};
|
||||
|
||||
TLSSocket.prototype._finishInit = function() {
|
||||
debug('secure established');
|
||||
// Guard against getting onhandshakedone() after .destroy().
|
||||
// * 1.2: If destroy() during onocspresponse(), then write of next handshake
|
||||
// record fails, the handshake done info callbacks does not occur, and the
|
||||
// socket closes.
|
||||
// * 1.3: The OCSP response comes in the same record that finishes handshake,
|
||||
// so even after .destroy(), the handshake done info callback occurs
|
||||
// immediately after onocspresponse(). Ignore it.
|
||||
if (!this._handle)
|
||||
return;
|
||||
|
||||
this.alpnProtocol = this._handle.getALPNNegotiatedProtocol();
|
||||
this.servername = this._handle.getServername();
|
||||
|
||||
debug('%s _finishInit',
|
||||
this._tlsOptions.isServer ? 'server' : 'client',
|
||||
'handle?', !!this._handle,
|
||||
'alpn', this.alpnProtocol,
|
||||
'servername', this.servername);
|
||||
|
||||
this._secureEstablished = true;
|
||||
if (this._tlsOptions.handshakeTimeout > 0)
|
||||
this.setTimeout(0, this._handleTimeout);
|
||||
@ -677,6 +729,12 @@ TLSSocket.prototype._finishInit = function() {
|
||||
};
|
||||
|
||||
TLSSocket.prototype._start = function() {
|
||||
debug('%s _start',
|
||||
this._tlsOptions.isServer ? 'server' : 'client',
|
||||
'handle?', !!this._handle,
|
||||
'connecting?', this.connecting,
|
||||
'requestOCSP?', !!this._tlsOptions.requestOCSP,
|
||||
);
|
||||
if (this.connecting) {
|
||||
this.once('connect', this._start);
|
||||
return;
|
||||
@ -686,7 +744,6 @@ TLSSocket.prototype._start = function() {
|
||||
if (!this._handle)
|
||||
return;
|
||||
|
||||
debug('start');
|
||||
if (this._tlsOptions.requestOCSP)
|
||||
this._handle.requestOCSP();
|
||||
this._handle.start();
|
||||
@ -765,13 +822,16 @@ function onServerSocketSecure() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.destroyed && this._releaseControl())
|
||||
if (!this.destroyed && this._releaseControl()) {
|
||||
debug('server emit secureConnection');
|
||||
this._tlsOptions.server.emit('secureConnection', this);
|
||||
}
|
||||
}
|
||||
|
||||
function onSocketTLSError(err) {
|
||||
if (!this._controlReleased && !this[kErrorEmitted]) {
|
||||
this[kErrorEmitted] = true;
|
||||
debug('server emit tlsClientError:', err);
|
||||
this._tlsOptions.server.emit('tlsClientError', err, this);
|
||||
}
|
||||
}
|
||||
@ -792,6 +852,7 @@ function onSocketClose(err) {
|
||||
}
|
||||
|
||||
function tlsConnectionListener(rawSocket) {
|
||||
debug('net.Server.on(connection): new TLSSocket');
|
||||
const socket = new TLSSocket(rawSocket, {
|
||||
secureContext: this._sharedCreds,
|
||||
isServer: true,
|
||||
@ -1180,6 +1241,7 @@ function onConnectSecure() {
|
||||
const ekeyinfo = this.getEphemeralKeyInfo();
|
||||
if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
|
||||
const err = new ERR_TLS_DH_PARAM_SIZE(ekeyinfo.size);
|
||||
debug('client emit:', err);
|
||||
this.emit('error', err);
|
||||
this.destroy();
|
||||
return;
|
||||
@ -1206,10 +1268,12 @@ function onConnectSecure() {
|
||||
this.destroy(verifyError);
|
||||
return;
|
||||
} else {
|
||||
debug('client emit secureConnect');
|
||||
this.emit('secureConnect');
|
||||
}
|
||||
} else {
|
||||
this.authorized = true;
|
||||
debug('client emit secureConnect');
|
||||
this.emit('secureConnect');
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ const kAfterAsyncWrite = Symbol('kAfterAsyncWrite');
|
||||
const kHandle = Symbol('kHandle');
|
||||
const kSession = Symbol('kSession');
|
||||
|
||||
const debug = require('util').debuglog('stream');
|
||||
|
||||
function handleWriteReq(req, data, encoding) {
|
||||
const { handle } = req;
|
||||
|
||||
@ -66,6 +68,8 @@ function handleWriteReq(req, data, encoding) {
|
||||
}
|
||||
|
||||
function onWriteComplete(status) {
|
||||
debug('onWriteComplete', status, this.error);
|
||||
|
||||
const stream = this.handle[owner_symbol];
|
||||
|
||||
if (stream.destroyed) {
|
||||
|
16
lib/tls.js
16
lib/tls.js
@ -54,15 +54,25 @@ exports.DEFAULT_CIPHERS =
|
||||
|
||||
exports.DEFAULT_ECDH_CURVE = 'auto';
|
||||
|
||||
exports.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
exports.DEFAULT_MAX_VERSION = 'TLSv1.3';
|
||||
|
||||
if (getOptionValue('--tls-v1.0'))
|
||||
if (getOptionValue('--tls-min-v1.0'))
|
||||
exports.DEFAULT_MIN_VERSION = 'TLSv1';
|
||||
else if (getOptionValue('--tls-v1.1'))
|
||||
else if (getOptionValue('--tls-min-v1.1'))
|
||||
exports.DEFAULT_MIN_VERSION = 'TLSv1.1';
|
||||
else if (getOptionValue('--tls-min-v1.3'))
|
||||
exports.DEFAULT_MIN_VERSION = 'TLSv1.3';
|
||||
else
|
||||
exports.DEFAULT_MIN_VERSION = 'TLSv1.2';
|
||||
|
||||
if (getOptionValue('--tls-max-v1.3'))
|
||||
exports.DEFAULT_MAX_VERSION = 'TLSv1.3';
|
||||
else if (getOptionValue('--tls-max-v1.2'))
|
||||
exports.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
else
|
||||
exports.DEFAULT_MAX_VERSION = 'TLSv1.3'; // Will depend on node version.
|
||||
|
||||
|
||||
exports.getCiphers = internalUtil.cachedResult(
|
||||
() => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true)
|
||||
);
|
||||
|
@ -1245,6 +1245,7 @@ void DefineCryptoConstants(Local<Object> target) {
|
||||
NODE_DEFINE_CONSTANT(target, TLS1_VERSION);
|
||||
NODE_DEFINE_CONSTANT(target, TLS1_1_VERSION);
|
||||
NODE_DEFINE_CONSTANT(target, TLS1_2_VERSION);
|
||||
NODE_DEFINE_CONSTANT(target, TLS1_3_VERSION);
|
||||
#endif
|
||||
NODE_DEFINE_CONSTANT(target, INT_MAX);
|
||||
}
|
||||
|
@ -41,7 +41,13 @@
|
||||
#define RSA_PSS_SALTLEN_AUTO -2
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \
|
||||
// TLSv1.3 suites start with TLS_, and are the OpenSSL defaults, see:
|
||||
// https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_ciphersuites.html
|
||||
#define DEFAULT_CIPHER_LIST_CORE \
|
||||
"TLS_AES_256_GCM_SHA384:" \
|
||||
"TLS_CHACHA20_POLY1305_SHA256:" \
|
||||
"TLS_AES_128_GCM_SHA256:" \
|
||||
"ECDHE-RSA-AES128-GCM-SHA256:" \
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256:" \
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:" \
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384:" \
|
||||
|
@ -339,9 +339,14 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
|
||||
env->SetProtoMethod(t, "addCACert", AddCACert);
|
||||
env->SetProtoMethod(t, "addCRL", AddCRL);
|
||||
env->SetProtoMethod(t, "addRootCerts", AddRootCerts);
|
||||
env->SetProtoMethod(t, "setCipherSuites", SetCipherSuites);
|
||||
env->SetProtoMethod(t, "setCiphers", SetCiphers);
|
||||
env->SetProtoMethod(t, "setECDHCurve", SetECDHCurve);
|
||||
env->SetProtoMethod(t, "setDHParam", SetDHParam);
|
||||
env->SetProtoMethod(t, "setMaxProto", SetMaxProto);
|
||||
env->SetProtoMethod(t, "setMinProto", SetMinProto);
|
||||
env->SetProtoMethod(t, "getMaxProto", GetMaxProto);
|
||||
env->SetProtoMethod(t, "getMinProto", GetMinProto);
|
||||
env->SetProtoMethod(t, "setOptions", SetOptions);
|
||||
env->SetProtoMethod(t, "setSessionIdContext", SetSessionIdContext);
|
||||
env->SetProtoMethod(t, "setSessionTimeout", SetSessionTimeout);
|
||||
@ -392,6 +397,9 @@ void SecureContext::New(const FunctionCallbackInfo<Value>& args) {
|
||||
new SecureContext(env, args.This());
|
||||
}
|
||||
|
||||
// A maxVersion of 0 means "any", but OpenSSL may support TLS versions that
|
||||
// Node.js doesn't, so pin the max to what we do support.
|
||||
const int MAX_SUPPORTED_VERSION = TLS1_3_VERSION;
|
||||
|
||||
void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
@ -406,13 +414,16 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
int max_version = args[2].As<Int32>()->Value();
|
||||
const SSL_METHOD* method = TLS_method();
|
||||
|
||||
if (max_version == 0)
|
||||
max_version = MAX_SUPPORTED_VERSION;
|
||||
|
||||
if (args[0]->IsString()) {
|
||||
const node::Utf8Value sslmethod(env->isolate(), args[0]);
|
||||
|
||||
// Note that SSLv2 and SSLv3 are disallowed but SSLv23_method and friends
|
||||
// are still accepted. They are OpenSSL's way of saying that all known
|
||||
// protocols are supported unless explicitly disabled (which we do below
|
||||
// for SSLv2 and SSLv3.)
|
||||
// protocols below TLS 1.3 are supported unless explicitly disabled (which
|
||||
// we do below for SSLv2 and SSLv3.)
|
||||
if (strcmp(*sslmethod, "SSLv2_method") == 0) {
|
||||
THROW_ERR_TLS_INVALID_PROTOCOL_METHOD(env, "SSLv2 methods disabled");
|
||||
return;
|
||||
@ -432,21 +443,23 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
THROW_ERR_TLS_INVALID_PROTOCOL_METHOD(env, "SSLv3 methods disabled");
|
||||
return;
|
||||
} else if (strcmp(*sslmethod, "SSLv23_method") == 0) {
|
||||
// noop
|
||||
max_version = TLS1_2_VERSION;
|
||||
} else if (strcmp(*sslmethod, "SSLv23_server_method") == 0) {
|
||||
max_version = TLS1_2_VERSION;
|
||||
method = TLS_server_method();
|
||||
} else if (strcmp(*sslmethod, "SSLv23_client_method") == 0) {
|
||||
max_version = TLS1_2_VERSION;
|
||||
method = TLS_client_method();
|
||||
} else if (strcmp(*sslmethod, "TLS_method") == 0) {
|
||||
min_version = 0;
|
||||
max_version = 0;
|
||||
max_version = MAX_SUPPORTED_VERSION;
|
||||
} else if (strcmp(*sslmethod, "TLS_server_method") == 0) {
|
||||
min_version = 0;
|
||||
max_version = 0;
|
||||
max_version = MAX_SUPPORTED_VERSION;
|
||||
method = TLS_server_method();
|
||||
} else if (strcmp(*sslmethod, "TLS_client_method") == 0) {
|
||||
min_version = 0;
|
||||
max_version = 0;
|
||||
max_version = MAX_SUPPORTED_VERSION;
|
||||
method = TLS_client_method();
|
||||
} else if (strcmp(*sslmethod, "TLSv1_method") == 0) {
|
||||
min_version = TLS1_VERSION;
|
||||
@ -510,12 +523,6 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
SSL_SESS_CACHE_NO_AUTO_CLEAR);
|
||||
|
||||
SSL_CTX_set_min_proto_version(sc->ctx_.get(), min_version);
|
||||
|
||||
if (max_version == 0) {
|
||||
// Selecting some secureProtocol methods allows the TLS version to be "any
|
||||
// supported", but we don't support TLSv1.3, even if OpenSSL does.
|
||||
max_version = TLS1_2_VERSION;
|
||||
}
|
||||
SSL_CTX_set_max_proto_version(sc->ctx_.get(), max_version);
|
||||
|
||||
// OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was
|
||||
@ -936,42 +943,54 @@ void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetCipherSuites(const FunctionCallbackInfo<Value>& args) {
|
||||
// BoringSSL doesn't allow API config of TLS1.3 cipher suites.
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
Environment* env = sc->env();
|
||||
ClearErrorOnReturn clear_error_on_return;
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsString());
|
||||
|
||||
const node::Utf8Value ciphers(args.GetIsolate(), args[0]);
|
||||
if (!SSL_CTX_set_ciphersuites(sc->ctx_.get(), *ciphers)) {
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
if (!err) {
|
||||
// This would be an OpenSSL bug if it happened.
|
||||
return env->ThrowError("Failed to set ciphers");
|
||||
}
|
||||
return ThrowCryptoError(env, err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetCiphers(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
Environment* env = sc->env();
|
||||
ClearErrorOnReturn clear_error_on_return;
|
||||
|
||||
if (args.Length() != 1) {
|
||||
return THROW_ERR_MISSING_ARGS(env, "Ciphers argument is mandatory");
|
||||
}
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsString());
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Ciphers");
|
||||
|
||||
// Note: set_ciphersuites() is for TLSv1.3 and was introduced in openssl
|
||||
// 1.1.1, set_cipher_list() is for TLSv1.2 and earlier.
|
||||
//
|
||||
// In openssl 1.1.0, set_cipher_list() would error if it resulted in no
|
||||
// TLSv1.2 (and earlier) cipher suites, and there is no TLSv1.3 support.
|
||||
//
|
||||
// In openssl 1.1.1, set_cipher_list() will not error if it results in no
|
||||
// TLSv1.2 cipher suites if there are any TLSv1.3 cipher suites, which there
|
||||
// are by default. There will be an error later, during the handshake, but
|
||||
// that results in an async error event, rather than a sync error thrown,
|
||||
// which is a semver-major change for the tls API.
|
||||
//
|
||||
// Since we don't currently support TLSv1.3, work around this by removing the
|
||||
// TLSv1.3 cipher suites, so we get backwards compatible synchronous errors.
|
||||
const node::Utf8Value ciphers(args.GetIsolate(), args[0]);
|
||||
if (
|
||||
#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL)
|
||||
!SSL_CTX_set_ciphersuites(sc->ctx_.get(), "") ||
|
||||
#endif
|
||||
!SSL_CTX_set_cipher_list(sc->ctx_.get(), *ciphers)) {
|
||||
if (!SSL_CTX_set_cipher_list(sc->ctx_.get(), *ciphers)) {
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
if (!err) {
|
||||
// This would be an OpenSSL bug if it happened.
|
||||
return env->ThrowError("Failed to set ciphers");
|
||||
}
|
||||
|
||||
if (strlen(*ciphers) == 0 && ERR_GET_REASON(err) == SSL_R_NO_CIPHER_MATCH) {
|
||||
// TLS1.2 ciphers were deliberately cleared, so don't consider
|
||||
// SSL_R_NO_CIPHER_MATCH to be an error (this is how _set_cipher_suites()
|
||||
// works). If the user actually sets a value (like "no-such-cipher"), then
|
||||
// that's actually an error.
|
||||
return;
|
||||
}
|
||||
return ThrowCryptoError(env, err);
|
||||
}
|
||||
}
|
||||
@ -1040,6 +1059,56 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetMinProto(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsInt32());
|
||||
|
||||
int version = args[0].As<Int32>()->Value();
|
||||
|
||||
CHECK(SSL_CTX_set_min_proto_version(sc->ctx_.get(), version));
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetMaxProto(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsInt32());
|
||||
|
||||
int version = args[0].As<Int32>()->Value();
|
||||
|
||||
CHECK(SSL_CTX_set_max_proto_version(sc->ctx_.get(), version));
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::GetMinProto(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
|
||||
CHECK_EQ(args.Length(), 0);
|
||||
|
||||
long version = // NOLINT(runtime/int)
|
||||
SSL_CTX_get_min_proto_version(sc->ctx_.get());
|
||||
args.GetReturnValue().Set(static_cast<uint32_t>(version));
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::GetMaxProto(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
|
||||
CHECK_EQ(args.Length(), 0);
|
||||
|
||||
long version = // NOLINT(runtime/int)
|
||||
SSL_CTX_get_max_proto_version(sc->ctx_.get());
|
||||
args.GetReturnValue().Set(static_cast<uint32_t>(version));
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
|
||||
SecureContext* sc;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
|
||||
@ -1259,6 +1328,7 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
|
||||
Environment* env = wrap->env();
|
||||
|
||||
// TODO(@sam-github) Move type and len check to js, and CHECK() in C++.
|
||||
if (args.Length() < 1) {
|
||||
return THROW_ERR_MISSING_ARGS(env, "Ticket keys argument is mandatory");
|
||||
}
|
||||
@ -2118,6 +2188,7 @@ void SSLWrap<Base>::LoadSession(const FunctionCallbackInfo<Value>& args) {
|
||||
Base* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||
|
||||
// TODO(@sam-github) check arg length and types in js, and CHECK in c++
|
||||
if (args.Length() >= 1 && Buffer::HasInstance(args[0])) {
|
||||
ArrayBufferViewContents<unsigned char> sbuf(args[0]);
|
||||
|
||||
@ -2154,7 +2225,7 @@ void SSLWrap<Base>::Renegotiate(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
ClearErrorOnReturn clear_error_on_return;
|
||||
|
||||
// XXX(sam) Return/throw an error, don't discard the SSL error reason.
|
||||
// TODO(@sam-github) Return/throw an error, don't discard the SSL error info.
|
||||
bool yes = SSL_renegotiate(w->ssl_.get()) == 1;
|
||||
args.GetReturnValue().Set(yes);
|
||||
}
|
||||
@ -2269,8 +2340,12 @@ void SSLWrap<Base>::GetEphemeralKeyInfo(
|
||||
EVP_PKEY_bits(key.get()))).FromJust();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO(@sam-github) semver-major: else return ThrowCryptoError(env,
|
||||
// ERR_get_error())
|
||||
|
||||
return args.GetReturnValue().Set(info);
|
||||
}
|
||||
@ -2484,7 +2559,10 @@ int SSLWrap<Base>::TLSExtStatusCallback(SSL* s, void* arg) {
|
||||
|
||||
w->MakeCallback(env->onocspresponse_string(), 1, &arg);
|
||||
|
||||
// Somehow, client is expecting different return value here
|
||||
// No async acceptance is possible, so always return 1 to accept the
|
||||
// response. The listener for 'OCSPResponse' event has no control over
|
||||
// return value, but it can .destroy() the connection if the response is not
|
||||
// acceptable.
|
||||
return 1;
|
||||
} else {
|
||||
// Outgoing response
|
||||
@ -2526,6 +2604,8 @@ int SSLWrap<Base>::SSLCertCallback(SSL* s, void* arg) {
|
||||
return 1;
|
||||
|
||||
if (w->cert_cb_running_)
|
||||
// Not an error. Suspend handshake with SSL_ERROR_WANT_X509_LOOKUP, and
|
||||
// handshake will continue after certcb is done.
|
||||
return -1;
|
||||
|
||||
Environment* env = w->env();
|
||||
@ -2601,6 +2681,8 @@ void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) {
|
||||
if (rv)
|
||||
rv = w->SetCACerts(sc);
|
||||
if (!rv) {
|
||||
// Not clear why sometimes we throw error, and sometimes we call
|
||||
// onerror(). Both cause .destroy(), but onerror does a bit more.
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
if (!err)
|
||||
return env->ThrowError("CertCbDone");
|
||||
@ -5954,6 +6036,24 @@ void GetSSLCiphers(const FunctionCallbackInfo<Value>& args) {
|
||||
SSL_CIPHER_get_name(cipher))).FromJust();
|
||||
}
|
||||
|
||||
// TLSv1.3 ciphers aren't listed by EVP. There are only 5, we could just
|
||||
// document them, but since there are only 5, easier to just add them manually
|
||||
// and not have to explain their absence in the API docs. They are lower-cased
|
||||
// because the docs say they will be.
|
||||
static const char* TLS13_CIPHERS[] = {
|
||||
"tls_aes_256_gcm_sha384",
|
||||
"tls_chacha20_poly1305_sha256",
|
||||
"tls_aes_128_gcm_sha256",
|
||||
"tls_aes_128_ccm_8_sha256",
|
||||
"tls_aes_128_ccm_sha256"
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < arraysize(TLS13_CIPHERS); ++i) {
|
||||
const char* name = TLS13_CIPHERS[i];
|
||||
arr->Set(env->context(),
|
||||
arr->Length(), OneByteString(args.GetIsolate(), name)).FromJust();
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(arr);
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,7 @@ class SecureContext : public BaseObject {
|
||||
static void AddCACert(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void AddCRL(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetCipherSuites(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetECDHCurve(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetDHParam(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
@ -155,6 +156,10 @@ class SecureContext : public BaseObject {
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetSessionTimeout(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetMinProto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetMaxProto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetMinProto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetMaxProto(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void LoadPKCS12(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
|
@ -86,6 +86,8 @@ void ClientHelloParser::ParseHeader(const uint8_t* data, size_t avail) {
|
||||
// (3,2) TLS v1.1
|
||||
// (3,3) TLS v1.2
|
||||
//
|
||||
// Note that TLS v1.3 uses a TLS v1.2 handshake so requires no specific
|
||||
// support here.
|
||||
if (data[body_offset_ + 4] != 0x03 ||
|
||||
data[body_offset_ + 5] < 0x01 ||
|
||||
data[body_offset_ + 5] > 0x03) {
|
||||
|
@ -33,6 +33,12 @@ namespace crypto {
|
||||
// Parse the client hello so we can do async session resumption. OpenSSL's
|
||||
// session resumption uses synchronous callbacks, see SSL_CTX_sess_set_get_cb
|
||||
// and get_session_cb.
|
||||
//
|
||||
// TLS1.3 handshakes masquerade as TLS1.2 session resumption, and to do this,
|
||||
// they always include a session_id in the ClientHello, making up a bogus value
|
||||
// if necessary. The parser can't know if its a bogus id, and will cause a
|
||||
// 'newSession' event to be emitted. This should do no harm, the id won't be
|
||||
// found, and the handshake will continue.
|
||||
class ClientHelloParser {
|
||||
public:
|
||||
inline ClientHelloParser();
|
||||
|
@ -335,16 +335,30 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
||||
|
||||
AddOption("--napi-modules", "", NoOp{}, kAllowedInEnvironment);
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
AddOption("--tls-v1.0",
|
||||
"enable TLSv1.0 and greater by default",
|
||||
&EnvironmentOptions::tls_v1_0,
|
||||
AddOption("--tls-min-v1.0",
|
||||
"set default TLS minimum to TLSv1.0 (default: TLSv1.2)",
|
||||
&EnvironmentOptions::tls_min_v1_0,
|
||||
kAllowedInEnvironment);
|
||||
AddOption("--tls-v1.1",
|
||||
"enable TLSv1.1 and greater by default",
|
||||
&EnvironmentOptions::tls_v1_1,
|
||||
AddOption("--tls-min-v1.1",
|
||||
"set default TLS minimum to TLSv1.1 (default: TLSv1.2)",
|
||||
&EnvironmentOptions::tls_min_v1_1,
|
||||
kAllowedInEnvironment);
|
||||
AddOption("--tls-min-v1.3",
|
||||
"set default TLS minimum to TLSv1.3 (default: TLSv1.2)",
|
||||
&EnvironmentOptions::tls_min_v1_3,
|
||||
kAllowedInEnvironment);
|
||||
AddOption("--tls-max-v1.2",
|
||||
"set default TLS maximum to TLSv1.2 (default: TLSv1.3)",
|
||||
&EnvironmentOptions::tls_max_v1_2,
|
||||
kAllowedInEnvironment);
|
||||
// Current plan is:
|
||||
// - 11.x and below: TLS1.3 is opt-in with --tls-max-v1.3
|
||||
// - 12.x: TLS1.3 is opt-out with --tls-max-v1.2
|
||||
// In either case, support both options they are uniformly available.
|
||||
AddOption("--tls-max-v1.3",
|
||||
"set default TLS maximum to TLSv1.3 (default: TLSv1.3)",
|
||||
&EnvironmentOptions::tls_max_v1_3,
|
||||
kAllowedInEnvironment);
|
||||
#endif
|
||||
}
|
||||
|
||||
PerIsolateOptionsParser::PerIsolateOptionsParser(
|
||||
|
@ -121,10 +121,11 @@ class EnvironmentOptions : public Options {
|
||||
bool print_eval = false;
|
||||
bool force_repl = false;
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
bool tls_v1_0 = false;
|
||||
bool tls_v1_1 = false;
|
||||
#endif
|
||||
bool tls_min_v1_0 = false;
|
||||
bool tls_min_v1_1 = false;
|
||||
bool tls_min_v1_3 = false;
|
||||
bool tls_max_v1_2 = false;
|
||||
bool tls_max_v1_3 = false;
|
||||
|
||||
std::vector<std::string> preload_modules;
|
||||
|
||||
|
@ -113,6 +113,12 @@ void TLSWrap::InitSSL() {
|
||||
SSL_set_mode(ssl_.get(), SSL_MODE_RELEASE_BUFFERS);
|
||||
#endif // SSL_MODE_RELEASE_BUFFERS
|
||||
|
||||
// This is default in 1.1.1, but set it anyway, Cycle() doesn't currently
|
||||
// re-call ClearIn() if SSL_read() returns SSL_ERROR_WANT_READ, so data can be
|
||||
// left sitting in the incoming enc_in_ and never get processed.
|
||||
// - https://wiki.openssl.org/index.php/TLS1.3#Non-application_data_records
|
||||
SSL_set_mode(ssl_.get(), SSL_MODE_AUTO_RETRY);
|
||||
|
||||
SSL_set_app_data(ssl_.get(), this);
|
||||
// Using InfoCallback isn't how we are supposed to check handshake progress:
|
||||
// https://github.com/openssl/openssl/issues/7199#issuecomment-420915993
|
||||
@ -224,6 +230,8 @@ void TLSWrap::SSLInfoCallback(const SSL* ssl_, int where, int ret) {
|
||||
Local<Object> object = c->object();
|
||||
|
||||
if (where & SSL_CB_HANDSHAKE_START) {
|
||||
// Start is tracked to limit number and frequency of renegotiation attempts,
|
||||
// since excessive renegotiation may be an attack.
|
||||
Local<Value> callback;
|
||||
|
||||
if (object->Get(env->context(), env->onhandshakestart_string())
|
||||
@ -237,6 +245,7 @@ void TLSWrap::SSLInfoCallback(const SSL* ssl_, int where, int ret) {
|
||||
// sending HelloRequest in OpenSSL-1.1.1.
|
||||
// We need to check whether this is in a renegotiation state or not.
|
||||
if (where & SSL_CB_HANDSHAKE_DONE && !SSL_renegotiate_pending(ssl)) {
|
||||
CHECK(!SSL_renegotiate_pending(ssl));
|
||||
Local<Value> callback;
|
||||
|
||||
c->established_ = true;
|
||||
@ -271,8 +280,23 @@ void TLSWrap::EncOut() {
|
||||
|
||||
// No encrypted output ready to write to the underlying stream.
|
||||
if (BIO_pending(enc_out_) == 0) {
|
||||
if (pending_cleartext_input_.empty())
|
||||
InvokeQueued(0);
|
||||
if (pending_cleartext_input_.empty()) {
|
||||
if (!in_dowrite_) {
|
||||
InvokeQueued(0);
|
||||
} else {
|
||||
// TODO(@sam-github, @addaleax) If in_dowrite_ is true, appdata was
|
||||
// passed to SSL_write(). If we are here, the data was not encrypted to
|
||||
// enc_out_ yet. Calling Done() "works", but since the write is not
|
||||
// flushed, its too soon. Just returning and letting the next EncOut()
|
||||
// call Done() passes the test suite, but without more careful analysis,
|
||||
// its not clear if it is always correct. Not calling Done() could block
|
||||
// data flow, so for now continue to call Done(), just do it in the next
|
||||
// tick.
|
||||
env()->SetImmediate([](Environment* env, void* data) {
|
||||
static_cast<TLSWrap*>(data)->InvokeQueued(0);
|
||||
}, this, object());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -534,8 +558,8 @@ void TLSWrap::ClearIn() {
|
||||
Local<Value> arg = GetSSLError(written, &err, &error_str);
|
||||
if (!arg.IsEmpty()) {
|
||||
write_callback_scheduled_ = true;
|
||||
// XXX(sam) Should forward an error object with .code/.function/.etc, if
|
||||
// possible.
|
||||
// TODO(@sam-github) Should forward an error object with
|
||||
// .code/.function/.etc, if possible.
|
||||
InvokeQueued(UV_EPROTO, error_str.c_str());
|
||||
} else {
|
||||
// Push back the not-yet-written pending buffers into their queue.
|
||||
@ -603,6 +627,7 @@ void TLSWrap::ClearError() {
|
||||
|
||||
|
||||
// Called by StreamBase::Write() to request async write of clear text into SSL.
|
||||
// TODO(@sam-github) Should there be a TLSWrap::DoTryWrite()?
|
||||
int TLSWrap::DoWrite(WriteWrap* w,
|
||||
uv_buf_t* bufs,
|
||||
size_t count,
|
||||
@ -688,7 +713,10 @@ int TLSWrap::DoWrite(WriteWrap* w,
|
||||
}
|
||||
|
||||
// Write any encrypted/handshake output that may be ready.
|
||||
// Guard against sync call of current_write_->Done(), its unsupported.
|
||||
in_dowrite_ = true;
|
||||
EncOut();
|
||||
in_dowrite_ = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ class TLSWrap : public AsyncWrap,
|
||||
std::vector<uv_buf_t> pending_cleartext_input_;
|
||||
size_t write_size_ = 0;
|
||||
WriteWrap* current_write_ = nullptr;
|
||||
bool in_dowrite_ = false;
|
||||
WriteWrap* current_empty_write_ = nullptr;
|
||||
bool write_callback_scheduled_ = false;
|
||||
bool started_ = false;
|
||||
|
11
test/async-hooks/test-graph.tls-write-12.js
Normal file
11
test/async-hooks/test-graph.tls-write-12.js
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-graph.tls-write.js');
|
@ -39,8 +39,10 @@ function onlistening() {
|
||||
function onsecureConnection() {}
|
||||
|
||||
function onsecureConnect() {
|
||||
// Destroying client socket
|
||||
this.destroy();
|
||||
// end() client socket, which causes slightly different hook events than
|
||||
// destroy(), but with TLS1.3 destroy() rips the connection down before the
|
||||
// server completes the handshake.
|
||||
this.end();
|
||||
|
||||
// Closing server
|
||||
server.close(common.mustCall(onserverClosed));
|
||||
@ -68,7 +70,8 @@ function onexit() {
|
||||
{ type: 'WRITEWRAP', id: 'write:2', triggerAsyncId: null },
|
||||
{ type: 'WRITEWRAP', id: 'write:3', triggerAsyncId: null },
|
||||
{ type: 'WRITEWRAP', id: 'write:4', triggerAsyncId: null },
|
||||
{ type: 'Immediate', id: 'immediate:1', triggerAsyncId: 'tcp:1' },
|
||||
{ type: 'Immediate', id: 'immediate:2', triggerAsyncId: 'tcp:2' } ]
|
||||
{ type: 'Immediate', id: 'immediate:1', triggerAsyncId: 'tcp:2' },
|
||||
{ type: 'Immediate', id: 'immediate:2', triggerAsyncId: 'tcp:1' },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ const { checkInvocations } = require('./hook-checks');
|
||||
const hooks = initHooks();
|
||||
hooks.enable();
|
||||
|
||||
// TODO(@sam-github) assumes server handshake completes before client, true for
|
||||
// 1.2, not for 1.3. Might need a rewrite for TLS1.3.
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
//
|
||||
// Creating server and listening on port
|
||||
//
|
||||
@ -52,6 +56,7 @@ function onsecureConnection() {
|
||||
//
|
||||
const as = hooks.activitiesOfTypes('TLSWRAP');
|
||||
assert.strictEqual(as.length, 2);
|
||||
// TODO(@sam-github) This happens after onsecureConnect, with TLS1.3.
|
||||
client = as[1];
|
||||
assert.strictEqual(client.type, 'TLSWRAP');
|
||||
assert.strictEqual(typeof client.uid, 'number');
|
||||
@ -78,7 +83,7 @@ function onsecureConnect() {
|
||||
//
|
||||
// Destroying client socket
|
||||
//
|
||||
this.destroy();
|
||||
this.destroy(); // This destroys client before server handshakes, with TLS1.3
|
||||
checkInvocations(svr, { init: 1, before: 2, after: 1 },
|
||||
'server: when destroying client');
|
||||
checkInvocations(client, { init: 1, before: 2, after: 2 },
|
||||
|
@ -129,6 +129,7 @@ validateList(cryptoCiphers);
|
||||
// Assume that we have at least AES256-SHA.
|
||||
const tlsCiphers = tls.getCiphers();
|
||||
assert(tls.getCiphers().includes('aes256-sha'));
|
||||
assert(tls.getCiphers().includes('tls_aes_128_ccm_8_sha256'));
|
||||
// There should be no capital letters in any element.
|
||||
const noCapitals = /^[^A-Z]+$/;
|
||||
assert(tlsCiphers.every((value) => noCapitals.test(value)));
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Flags: --tls-v1.1
|
||||
// Flags: --tls-min-v1.1
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Flags: --tls-v1.0
|
||||
// Flags: --tls-min-v1.0
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
@ -32,6 +32,9 @@ const tls = require('tls');
|
||||
const https = require('https');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
// Renegotiation as a protocol feature was dropped after TLS1.2.
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
// renegotiation limits to test
|
||||
const LIMITS = [0, 1, 2, 3, 5, 10, 16];
|
||||
|
||||
|
@ -55,7 +55,8 @@ server.listen(0, common.mustCall(function() {
|
||||
'\r\n');
|
||||
}));
|
||||
|
||||
client1.on('session', common.mustCall((session) => {
|
||||
// TLS1.2 servers issue 1 ticket, TLS1.3 issues more, but only use the first.
|
||||
client1.once('session', common.mustCall((session) => {
|
||||
console.log('session');
|
||||
|
||||
const opts = {
|
||||
|
@ -64,7 +64,7 @@ function sendClient() {
|
||||
}
|
||||
client.end();
|
||||
}, max_iter));
|
||||
client.write('a');
|
||||
client.write('a', common.mustCall());
|
||||
client.on('error', common.mustNotCall());
|
||||
client.on('close', common.mustCall(function() {
|
||||
clientClosed = true;
|
||||
|
@ -14,7 +14,6 @@ const tls = require('tls');
|
||||
// new and resume session events will never be emitted on the server.
|
||||
|
||||
const options = {
|
||||
maxVersion: 'TLSv1.2',
|
||||
secureOptions: SSL_OP_NO_TICKET,
|
||||
key: fixtures.readSync('test_key.pem'),
|
||||
cert: fixtures.readSync('test_cert.pem')
|
||||
@ -38,6 +37,10 @@ server.on('resumeSession', common.mustCall((id, cb) => {
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const clientOpts = {
|
||||
// Don't send a TLS1.3/1.2 ClientHello, they contain a fake session_id,
|
||||
// which triggers a 'resumeSession' event for client1. TLS1.2 ClientHello
|
||||
// won't have a session_id until client2, which will have a valid session.
|
||||
maxVersion: 'TLSv1.2',
|
||||
port: server.address().port,
|
||||
rejectUnauthorized: false,
|
||||
session: false
|
||||
|
@ -12,7 +12,8 @@ common.expectsError(
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'Ciphers must be a string'
|
||||
message: 'The "options.ciphers" property must be of type string.' +
|
||||
' Received type number'
|
||||
});
|
||||
|
||||
common.expectsError(
|
||||
@ -20,7 +21,8 @@ common.expectsError(
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'Ciphers must be a string'
|
||||
message: 'The "options.ciphers" property must be of type string.' +
|
||||
' Received type number'
|
||||
});
|
||||
|
||||
common.expectsError(
|
||||
|
15
test/parallel/test-tls-cli-max-version-1.2.js
Normal file
15
test/parallel/test-tls-cli-max-version-1.2.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Flags: --tls-max-v1.2
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-max-v1.2` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
15
test/parallel/test-tls-cli-max-version-1.3.js
Normal file
15
test/parallel/test-tls-cli-max-version-1.3.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Flags: --tls-max-v1.3
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-max-v1.3` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
@ -1,4 +1,4 @@
|
||||
// Flags: --tls-v1.0 --tls-v1.1
|
||||
// Flags: --tls-min-v1.0 --tls-min-v1.1
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
@ -8,7 +8,7 @@ if (!common.hasCrypto) common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Flags: --tls-v1.1
|
||||
// Flags: --tls-min-v1.1
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
@ -8,7 +8,7 @@ if (!common.hasCrypto) common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
|
15
test/parallel/test-tls-cli-min-version-1.3.js
Normal file
15
test/parallel/test-tls-cli-min-version-1.3.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Flags: --tls-min-v1.3
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-min-v1.3` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.3');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
@ -1,10 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const {
|
||||
assert, connect, keys
|
||||
assert, connect, keys, tls
|
||||
} = require(fixtures.path('tls-connect'));
|
||||
|
||||
// Use ec10 and agent10, they are the only identities with intermediate CAs.
|
||||
@ -63,8 +63,28 @@ connect({
|
||||
return cleanup();
|
||||
});
|
||||
|
||||
// Request cert from client that doesn't have one.
|
||||
// Request cert from TLS1.2 client that doesn't have one.
|
||||
connect({
|
||||
client: {
|
||||
maxVersion: 'TLSv1.2',
|
||||
ca: server.ca,
|
||||
checkServerIdentity,
|
||||
},
|
||||
server: {
|
||||
key: server.key,
|
||||
cert: server.cert,
|
||||
ca: client.ca,
|
||||
requestCert: true,
|
||||
},
|
||||
}, function(err, pair, cleanup) {
|
||||
assert.strictEqual(pair.server.err.code,
|
||||
'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE');
|
||||
assert.strictEqual(pair.client.err.code, 'ECONNRESET');
|
||||
return cleanup();
|
||||
});
|
||||
|
||||
// Request cert from TLS1.3 client that doesn't have one.
|
||||
if (tls.DEFAULT_MAX_VERSION === 'TLSv1.3') connect({
|
||||
client: {
|
||||
ca: server.ca,
|
||||
checkServerIdentity,
|
||||
@ -76,8 +96,17 @@ connect({
|
||||
requestCert: true,
|
||||
},
|
||||
}, function(err, pair, cleanup) {
|
||||
assert.strictEqual(err.code, 'ECONNRESET');
|
||||
return cleanup();
|
||||
assert.strictEqual(pair.server.err.code,
|
||||
'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE');
|
||||
|
||||
// TLS1.3 client completes handshake before server, and its only after the
|
||||
// server handshakes, requests certs, gets back a zero-length list of certs,
|
||||
// and sends a fatal Alert to the client that the client discovers there has
|
||||
// been a fatal error.
|
||||
pair.client.conn.once('error', common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED');
|
||||
cleanup();
|
||||
}));
|
||||
});
|
||||
|
||||
// Typical configuration error, incomplete cert chains sent, we have to know the
|
||||
|
@ -10,6 +10,9 @@ const tls = require('tls');
|
||||
const key = fixtures.readKey('agent2-key.pem');
|
||||
const cert = fixtures.readKey('agent2-cert.pem');
|
||||
|
||||
// TODO(@sam-github) test works with TLS1.3, rework test to add
|
||||
// 'ECDH' with 'TLS_AES_128_GCM_SHA256',
|
||||
|
||||
function loadDHParam(n) {
|
||||
return fixtures.readKey(`dh${n}.pem`);
|
||||
}
|
||||
|
13
test/parallel/test-tls-client-reject-12.js
Normal file
13
test/parallel/test-tls-client-reject-12.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
// test-tls-client-reject specifically for TLS1.2.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-client-reject.js');
|
@ -35,6 +35,7 @@ const options = {
|
||||
|
||||
const server = tls.createServer(options, function(socket) {
|
||||
socket.pipe(socket);
|
||||
// Pipe already ends... but leaving this here tests .end() after .end().
|
||||
socket.on('end', () => socket.end());
|
||||
}).listen(0, common.mustCall(function() {
|
||||
unauthorized();
|
||||
@ -47,13 +48,19 @@ function unauthorized() {
|
||||
servername: 'localhost',
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(function() {
|
||||
console.log('... unauthorized');
|
||||
let _data;
|
||||
assert(!socket.authorized);
|
||||
socket.on('data', common.mustCall((data) => {
|
||||
assert.strictEqual(data.toString(), 'ok');
|
||||
_data = data;
|
||||
}));
|
||||
socket.on('end', common.mustCall(() => {
|
||||
assert(_data, 'data failed to echo!');
|
||||
}));
|
||||
socket.on('end', () => rejectUnauthorized());
|
||||
}));
|
||||
socket.once('session', common.mustCall(() => {
|
||||
}));
|
||||
socket.on('error', common.mustNotCall());
|
||||
socket.end('ok');
|
||||
}
|
||||
@ -65,7 +72,6 @@ function rejectUnauthorized() {
|
||||
}, common.mustNotCall());
|
||||
socket.on('data', common.mustNotCall());
|
||||
socket.on('error', common.mustCall(function(err) {
|
||||
console.log('... rejected:', err);
|
||||
authorized();
|
||||
}));
|
||||
socket.end('ng');
|
||||
|
37
test/parallel/test-tls-client-renegotiation-13.js
Normal file
37
test/parallel/test-tls-client-renegotiation-13.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
// Confirm that for TLSv1.3, renegotiate() is disallowed.
|
||||
|
||||
const {
|
||||
assert, connect, keys
|
||||
} = require(fixtures.path('tls-connect'));
|
||||
|
||||
const server = keys.agent10;
|
||||
|
||||
connect({
|
||||
client: {
|
||||
ca: server.ca,
|
||||
checkServerIdentity: common.mustCall(),
|
||||
},
|
||||
server: {
|
||||
key: server.key,
|
||||
cert: server.cert,
|
||||
},
|
||||
}, function(err, pair, cleanup) {
|
||||
assert.ifError(err);
|
||||
|
||||
const client = pair.client.conn;
|
||||
|
||||
assert.strictEqual(client.getProtocol(), 'TLSv1.3');
|
||||
|
||||
const ok = client.renegotiate({}, common.mustCall((err) => {
|
||||
assert(err.code, 'ERR_TLS_RENEGOTIATE');
|
||||
assert(err.message, 'Attempt to renegotiate TLS session failed');
|
||||
cleanup();
|
||||
}));
|
||||
|
||||
assert.strictEqual(ok, false);
|
||||
});
|
@ -31,6 +31,9 @@ const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
// Renegotiation as a protocol feature was dropped after TLS1.2.
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
// renegotiation limits to test
|
||||
const LIMITS = [0, 1, 2, 3, 5, 10, 16];
|
||||
|
||||
|
13
test/parallel/test-tls-client-resume-12.js
Normal file
13
test/parallel/test-tls-client-resume-12.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
// test-tls-client-resume specifically for TLS1.2.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-client-resume.js');
|
@ -44,32 +44,59 @@ const server = tls.Server(options, common.mustCall((socket) => {
|
||||
|
||||
// start listening
|
||||
server.listen(0, common.mustCall(function() {
|
||||
|
||||
let sessionx = null;
|
||||
let session1 = null;
|
||||
let sessionx = null; // From right after connect, invalid for TLS1.3
|
||||
let session1 = null; // Delivered by the session event, always valid.
|
||||
let sessions = 0;
|
||||
let tls13;
|
||||
const client1 = tls.connect({
|
||||
port: this.address().port,
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(() => {
|
||||
console.log('connect1');
|
||||
tls13 = client1.getProtocol() === 'TLSv1.3';
|
||||
assert.strictEqual(client1.isSessionReused(), false);
|
||||
sessionx = client1.getSession();
|
||||
assert(sessionx);
|
||||
|
||||
if (session1)
|
||||
reconnect();
|
||||
}));
|
||||
|
||||
client1.on('data', common.mustCall((d) => {
|
||||
}));
|
||||
|
||||
client1.once('session', common.mustCall((session) => {
|
||||
console.log('session1');
|
||||
session1 = session;
|
||||
assert(session1);
|
||||
if (sessionx)
|
||||
reconnect();
|
||||
}));
|
||||
|
||||
client1.on('close', common.mustCall(() => {
|
||||
client1.on('session', () => {
|
||||
console.log('client1 session#', ++sessions);
|
||||
});
|
||||
|
||||
client1.on('close', () => {
|
||||
console.log('client1 close');
|
||||
assert.strictEqual(sessions, tls13 ? 2 : 1);
|
||||
});
|
||||
|
||||
function reconnect() {
|
||||
assert(sessionx);
|
||||
assert(session1);
|
||||
assert.strictEqual(sessionx.compare(session1), 0);
|
||||
if (tls13)
|
||||
// For TLS1.3, the session immediately after handshake is a dummy,
|
||||
// unresumable session. The one delivered later in session event is
|
||||
// resumable.
|
||||
assert.notStrictEqual(sessionx.compare(session1), 0);
|
||||
else
|
||||
// For TLS1.2, they are identical.
|
||||
assert.strictEqual(sessionx.compare(session1), 0);
|
||||
|
||||
const opts = {
|
||||
port: server.address().port,
|
||||
rejectUnauthorized: false,
|
||||
session: session1
|
||||
session: session1,
|
||||
};
|
||||
|
||||
const client2 = tls.connect(opts, common.mustCall(() => {
|
||||
@ -83,7 +110,7 @@ server.listen(0, common.mustCall(function() {
|
||||
}));
|
||||
|
||||
client2.resume();
|
||||
}));
|
||||
}
|
||||
|
||||
client1.resume();
|
||||
}));
|
||||
|
13
test/parallel/test-tls-destroy-stream-12.js
Normal file
13
test/parallel/test-tls-destroy-stream-12.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
// test-tls-destroy-stream specifically for TLS1.2.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-destroy-stream.js');
|
@ -9,6 +9,8 @@ const net = require('net');
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.3';
|
||||
|
||||
// This test ensures that an instance of StreamWrap should emit "end" and
|
||||
// "close" when the socket on the other side call `destroy()` instead of
|
||||
// `end()`.
|
||||
@ -21,10 +23,17 @@ const tlsServer = tls.createServer(
|
||||
ca: [fixtures.readSync('test_ca.pem')],
|
||||
},
|
||||
(socket) => {
|
||||
socket.on('error', common.mustNotCall());
|
||||
socket.on('close', common.mustCall());
|
||||
socket.write(CONTENT);
|
||||
socket.destroy();
|
||||
|
||||
socket.on('error', (err) => {
|
||||
// destroy() is sync, write() is async, whether write completes depends
|
||||
// on the protocol, it is not guaranteed by stream API.
|
||||
if (err.code === 'ERR_STREAM_DESTROYED')
|
||||
return;
|
||||
assert.ifError(err);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@ -57,13 +66,12 @@ const server = net.createServer((conn) => {
|
||||
server.listen(0, () => {
|
||||
const port = server.address().port;
|
||||
const conn = tls.connect({ port, rejectUnauthorized: false }, () => {
|
||||
conn.on('data', common.mustCall((data) => {
|
||||
// Whether the server's write() completed before its destroy() is
|
||||
// indeterminate, but if data was written, we should receive it correctly.
|
||||
conn.on('data', (data) => {
|
||||
assert.strictEqual(data.toString('utf8'), CONTENT);
|
||||
}));
|
||||
});
|
||||
conn.on('error', common.mustNotCall());
|
||||
conn.on(
|
||||
'close',
|
||||
common.mustCall(() => server.close()),
|
||||
);
|
||||
conn.on('close', common.mustCall(() => server.close()));
|
||||
});
|
||||
});
|
||||
|
@ -10,6 +10,9 @@ if (!common.hasCrypto)
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
// Renegotiation as a protocol feature was dropped after TLS1.2.
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('agent1-key.pem'),
|
||||
cert: fixtures.readKey('agent1-cert.pem'),
|
||||
@ -86,5 +89,9 @@ server.listen(0, common.mustCall(() => {
|
||||
}));
|
||||
}));
|
||||
assert.strictEqual(ok, true);
|
||||
client.on('secureConnect', common.mustCall(() => {
|
||||
}));
|
||||
client.on('secure', common.mustCall(() => {
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
|
@ -60,7 +60,7 @@ server.listen(0, '127.0.0.1', common.mustCall(function() {
|
||||
tls.connect({
|
||||
host: '127.0.0.1',
|
||||
port: this.address().port,
|
||||
cipher: 'ECDHE-RSA-AES128-GCM-SHA256',
|
||||
ciphers: 'ECDHE-RSA-AES128-GCM-SHA256',
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(function() {
|
||||
const cipher = this.getCipher();
|
||||
@ -69,3 +69,24 @@ server.listen(0, '127.0.0.1', common.mustCall(function() {
|
||||
this.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
tls.createServer({
|
||||
key: fixtures.readKey('agent2-key.pem'),
|
||||
cert: fixtures.readKey('agent2-cert.pem'),
|
||||
ciphers: 'TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_8_SHA256',
|
||||
maxVersion: 'TLSv1.3',
|
||||
}, common.mustCall(function() {
|
||||
this.close();
|
||||
})).listen(0, common.mustCall(function() {
|
||||
const client = tls.connect({
|
||||
port: this.address().port,
|
||||
ciphers: 'TLS_AES_128_CCM_8_SHA256',
|
||||
maxVersion: 'TLSv1.3',
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(() => {
|
||||
const cipher = client.getCipher();
|
||||
assert.strictEqual(cipher.name, 'TLS_AES_128_CCM_8_SHA256');
|
||||
assert.strictEqual(cipher.version, 'TLSv1.3');
|
||||
client.end();
|
||||
}));
|
||||
}));
|
||||
|
@ -13,6 +13,10 @@ const DEFAULT_MAX_VERSION = tls.DEFAULT_MAX_VERSION;
|
||||
|
||||
function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
|
||||
assert(proto || cerr || serr, 'test missing any expectations');
|
||||
// Report where test was called from. Strip leading garbage from
|
||||
// at Object.<anonymous> (file:line)
|
||||
// from the stack location, we only want the file:line part.
|
||||
const where = (new Error()).stack.split('\n')[2].replace(/[^(]*/, '');
|
||||
connect({
|
||||
client: {
|
||||
checkServerIdentity: (servername, cert) => { },
|
||||
@ -32,6 +36,7 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
|
||||
function u(_) { return _ === undefined ? 'U' : _; }
|
||||
console.log('test:', u(cmin), u(cmax), u(cprot), u(smin), u(smax), u(sprot),
|
||||
'expect', u(proto), u(cerr), u(serr));
|
||||
console.log(' ', where);
|
||||
if (!proto) {
|
||||
console.log('client', pair.client.err ? pair.client.err.code : undefined);
|
||||
console.log('server', pair.server.err ? pair.server.err.code : undefined);
|
||||
@ -64,8 +69,8 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
|
||||
|
||||
const U = undefined;
|
||||
|
||||
// Default protocol is TLSv1.2.
|
||||
test(U, U, U, U, U, U, 'TLSv1.2');
|
||||
// Default protocol is the max version.
|
||||
test(U, U, U, U, U, U, DEFAULT_MAX_VERSION);
|
||||
|
||||
// Insecure or invalid protocols cannot be enabled.
|
||||
test(U, U, U, U, U, 'SSLv2_method',
|
||||
@ -101,7 +106,23 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');
|
||||
|
||||
// SSLv23 also means "any supported protocol" greater than the default
|
||||
// minimum (which is configurable via command line).
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
|
||||
} else {
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
|
||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_INTERNAL_ERROR');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
|
||||
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
|
||||
@ -149,7 +170,11 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
} else {
|
||||
assert(false, 'unreachable');
|
||||
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
|
||||
test(U, U, U, U, U, 'TLSv1_1_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +189,9 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
} else {
|
||||
assert(false, 'unreachable');
|
||||
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,14 +207,32 @@ if (DEFAULT_MIN_VERSION === 'TLSv1') {
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_method', 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_2_method', 'TLSv1.2');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLS_method', 'TLSv1.2');
|
||||
|
||||
test(U, U, 'TLSv1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1');
|
||||
test(U, U, 'TLSv1_1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_2_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.2');
|
||||
|
||||
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
|
||||
|
||||
// v-any client can connect to v-specific server
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.3', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.2', U, 'TLSv1.2');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
|
||||
// v-specific client can connect to v-any server
|
||||
test('TLSv1.3', 'TLSv1.3', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1.2', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
|
||||
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1');
|
||||
|
13
test/parallel/test-tls-net-socket-keepalive-12.js
Normal file
13
test/parallel/test-tls-net-socket-keepalive-12.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
// test-tls-net-socket-keepalive specifically for TLS1.2.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-net-socket-keepalive.js');
|
@ -20,8 +20,11 @@ const options = {
|
||||
};
|
||||
|
||||
const server = tls.createServer(options, common.mustCall((conn) => {
|
||||
conn.write('hello');
|
||||
conn.write('hello', common.mustCall());
|
||||
conn.on('data', common.mustCall());
|
||||
conn.on('end', common.mustCall());
|
||||
conn.on('data', common.mustCall());
|
||||
conn.on('close', common.mustCall());
|
||||
conn.end();
|
||||
})).listen(0, common.mustCall(() => {
|
||||
const netSocket = new net.Socket({
|
||||
@ -42,6 +45,7 @@ const server = tls.createServer(options, common.mustCall((conn) => {
|
||||
address,
|
||||
});
|
||||
|
||||
socket.on('secureConnect', common.mustCall());
|
||||
socket.on('end', common.mustCall());
|
||||
socket.on('data', common.mustCall());
|
||||
socket.on('close', common.mustCall(() => {
|
||||
|
@ -272,6 +272,8 @@ function runTest(port, testIndex) {
|
||||
if (tcase.renegotiate) {
|
||||
serverOptions.secureOptions =
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
|
||||
// Renegotiation as a protocol feature was dropped after TLS1.2.
|
||||
serverOptions.maxVersion = 'TLSv1.2';
|
||||
}
|
||||
|
||||
let renegotiated = false;
|
||||
|
@ -19,4 +19,7 @@ const fixtures = require('../common/fixtures');
|
||||
options.ciphers = 'FOOBARBAZ';
|
||||
assert.throws(() => tls.createServer(options, common.mustNotCall()),
|
||||
/no cipher match/i);
|
||||
options.ciphers = 'TLS_not_a_cipher';
|
||||
assert.throws(() => tls.createServer(options, common.mustNotCall()),
|
||||
/no cipher match/i);
|
||||
}
|
||||
|
@ -1,62 +1,93 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
if (!common.opensslCli)
|
||||
common.skip('node compiled without OpenSSL CLI.');
|
||||
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const exec = require('child_process').exec;
|
||||
const tls = require('tls');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('agent2-key.pem'),
|
||||
cert: fixtures.readKey('agent2-cert.pem'),
|
||||
ciphers: 'AES256-SHA'
|
||||
};
|
||||
// Test cipher: option for TLS.
|
||||
|
||||
const reply = 'I AM THE WALRUS'; // something recognizable
|
||||
let response = '';
|
||||
const {
|
||||
assert, connect, keys
|
||||
} = require(fixtures.path('tls-connect'));
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.ok(response.includes(reply));
|
||||
});
|
||||
|
||||
const server = tls.createServer(options, common.mustCall(function(conn) {
|
||||
conn.end(reply);
|
||||
}));
|
||||
function test(cciphers, sciphers, cipher, cerr, serr) {
|
||||
assert(cipher || cerr || serr, 'test missing any expectations');
|
||||
const where = (new Error()).stack.split('\n')[2].replace(/[^(]*/, '');
|
||||
connect({
|
||||
client: {
|
||||
checkServerIdentity: (servername, cert) => { },
|
||||
ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
|
||||
ciphers: cciphers,
|
||||
},
|
||||
server: {
|
||||
cert: keys.agent6.cert,
|
||||
key: keys.agent6.key,
|
||||
ciphers: sciphers,
|
||||
},
|
||||
}, common.mustCall((err, pair, cleanup) => {
|
||||
function u(_) { return _ === undefined ? 'U' : _; }
|
||||
console.log('test:', u(cciphers), u(sciphers),
|
||||
'expect', u(cipher), u(cerr), u(serr));
|
||||
console.log(' ', where);
|
||||
if (!cipher) {
|
||||
console.log('client', pair.client.err ? pair.client.err.code : undefined);
|
||||
console.log('server', pair.server.err ? pair.server.err.code : undefined);
|
||||
if (cerr) {
|
||||
assert(pair.client.err);
|
||||
assert.strictEqual(pair.client.err.code, cerr);
|
||||
}
|
||||
if (serr) {
|
||||
assert(pair.server.err);
|
||||
assert.strictEqual(pair.server.err.code, serr);
|
||||
}
|
||||
return cleanup();
|
||||
}
|
||||
|
||||
server.listen(0, '127.0.0.1', function() {
|
||||
const cmd = `"${common.opensslCli}" s_client -cipher ${
|
||||
options.ciphers} -connect 127.0.0.1:${this.address().port}`;
|
||||
const reply = 'So long and thanks for all the fish.';
|
||||
|
||||
exec(cmd, function(err, stdout, stderr) {
|
||||
assert.ifError(err);
|
||||
response = stdout;
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
assert.ifError(pair.server.err);
|
||||
assert.ifError(pair.client.err);
|
||||
assert(pair.server.conn);
|
||||
assert(pair.client.conn);
|
||||
assert.strictEqual(pair.client.conn.getCipher().name, cipher);
|
||||
assert.strictEqual(pair.server.conn.getCipher().name, cipher);
|
||||
|
||||
pair.server.conn.write(reply);
|
||||
|
||||
pair.client.conn.on('data', common.mustCall((data) => {
|
||||
assert.strictEqual(data.toString(), reply);
|
||||
return cleanup();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
const U = undefined;
|
||||
|
||||
// Have shared ciphers.
|
||||
test(U, 'AES256-SHA', 'AES256-SHA');
|
||||
test('AES256-SHA', U, 'AES256-SHA');
|
||||
|
||||
test(U, 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384');
|
||||
test('TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');
|
||||
|
||||
// Do not have shared ciphers.
|
||||
test('TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256',
|
||||
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
|
||||
|
||||
test('AES128-SHA', 'AES256-SHA', U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
|
||||
test('AES128-SHA:TLS_AES_256_GCM_SHA384',
|
||||
'TLS_CHACHA20_POLY1305_SHA256:AES256-SHA',
|
||||
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
|
||||
|
||||
// Cipher order ignored, TLS1.3 chosen before TLS1.2.
|
||||
test('AES256-SHA:TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');
|
||||
test(U, 'AES256-SHA:TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384');
|
||||
|
||||
// TLS_AES_128_CCM_8_SHA256 & TLS_AES_128_CCM_SHA256 are not enabled by
|
||||
// default, but work.
|
||||
test('TLS_AES_128_CCM_8_SHA256', U,
|
||||
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
|
||||
|
||||
test('TLS_AES_128_CCM_8_SHA256', 'TLS_AES_128_CCM_8_SHA256',
|
||||
'TLS_AES_128_CCM_8_SHA256');
|
||||
|
12
test/parallel/test-tls-ticket-12.js
Normal file
12
test/parallel/test-tls-ticket-12.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
// Run test-tls-ticket.js with TLS1.2
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-ticket.js');
|
@ -40,13 +40,17 @@ if (cluster.isMaster) {
|
||||
let workerPort = null;
|
||||
|
||||
function shoot() {
|
||||
console.error('[master] connecting', workerPort);
|
||||
console.error('[master] connecting', workerPort, 'session?', !!lastSession);
|
||||
const c = tls.connect(workerPort, {
|
||||
session: lastSession,
|
||||
rejectUnauthorized: false
|
||||
}, () => {
|
||||
c.end();
|
||||
|
||||
}).on('close', () => {
|
||||
// Wait for close to shoot off another connection. We don't want to shoot
|
||||
// until a new session is allocated, if one will be. The new session is
|
||||
// not guaranteed on secureConnect (it depends on TLS1.2 vs TLS1.3), but
|
||||
// it is guaranteed to happen before the connection is closed.
|
||||
if (++reqCount === expectedReqCount) {
|
||||
Object.keys(cluster.workers).forEach(function(id) {
|
||||
cluster.workers[id].send('die');
|
||||
@ -55,8 +59,11 @@ if (cluster.isMaster) {
|
||||
shoot();
|
||||
}
|
||||
}).once('session', (session) => {
|
||||
assert(!lastSession);
|
||||
lastSession = session;
|
||||
});
|
||||
|
||||
c.resume(); // See close_notify comment in server
|
||||
}
|
||||
|
||||
function fork() {
|
||||
@ -93,12 +100,15 @@ const cert = fixtures.readSync('agent.crt');
|
||||
const options = { key, cert };
|
||||
|
||||
const server = tls.createServer(options, (c) => {
|
||||
console.error('[worker] connection reused?', c.isSessionReused());
|
||||
if (c.isSessionReused()) {
|
||||
process.send({ msg: 'reused' });
|
||||
} else {
|
||||
process.send({ msg: 'not-reused' });
|
||||
}
|
||||
c.end();
|
||||
// Used to just .end(), but that means client gets close_notify before
|
||||
// NewSessionTicket. Send data until that problem is solved.
|
||||
c.end('x');
|
||||
});
|
||||
|
||||
server.listen(0, () => {
|
||||
|
@ -34,6 +34,8 @@ const keys = crypto.randomBytes(48);
|
||||
const serverLog = [];
|
||||
const ticketLog = [];
|
||||
|
||||
let s;
|
||||
|
||||
let serverCount = 0;
|
||||
function createServer() {
|
||||
const id = serverCount++;
|
||||
@ -47,16 +49,37 @@ function createServer() {
|
||||
ticketKeys: keys
|
||||
}, function(c) {
|
||||
serverLog.push(id);
|
||||
c.end();
|
||||
// TODO(@sam-github) Triggers close_notify before NewSessionTicket bug.
|
||||
// c.end();
|
||||
c.end('x');
|
||||
|
||||
counter++;
|
||||
|
||||
// Rotate ticket keys
|
||||
//
|
||||
// Take especial care to account for TLS1.2 and TLS1.3 differences around
|
||||
// when ticket keys are encrypted. In TLS1.2, they are encrypted before the
|
||||
// handshake complete callback, but in TLS1.3, they are encrypted after.
|
||||
// There is no callback or way for us to know when they were sent, so hook
|
||||
// the client's reception of the keys, and use it as proof that the current
|
||||
// keys were used, and its safe to rotate them.
|
||||
//
|
||||
// Rotation can occur right away if the session was reused, the keys were
|
||||
// already decrypted or we wouldn't have a reused session.
|
||||
function setTicketKeys(keys) {
|
||||
if (c.isSessionReused())
|
||||
server.setTicketKeys(keys);
|
||||
else
|
||||
s.once('session', () => {
|
||||
server.setTicketKeys(keys);
|
||||
});
|
||||
}
|
||||
if (counter === 1) {
|
||||
previousKey = server.getTicketKeys();
|
||||
server.setTicketKeys(crypto.randomBytes(48));
|
||||
assert.strictEqual(previousKey.compare(keys), 0);
|
||||
setTicketKeys(crypto.randomBytes(48));
|
||||
} else if (counter === 2) {
|
||||
server.setTicketKeys(previousKey);
|
||||
setTicketKeys(previousKey);
|
||||
} else if (counter === 3) {
|
||||
// Use keys from counter=2
|
||||
} else {
|
||||
@ -95,12 +118,15 @@ function start(callback) {
|
||||
let left = servers.length;
|
||||
|
||||
function connect() {
|
||||
const s = tls.connect(shared.address().port, {
|
||||
s = tls.connect(shared.address().port, {
|
||||
session: sess,
|
||||
rejectUnauthorized: false
|
||||
}, function() {
|
||||
sess = sess || s.getSession();
|
||||
ticketLog.push(s.getTLSTicket().toString('hex'));
|
||||
if (s.isSessionReused())
|
||||
ticketLog.push(s.getTLSTicket().toString('hex'));
|
||||
});
|
||||
s.on('data', () => {
|
||||
s.end();
|
||||
});
|
||||
s.on('close', function() {
|
||||
if (--left === 0)
|
||||
@ -108,7 +134,11 @@ function start(callback) {
|
||||
else
|
||||
connect();
|
||||
});
|
||||
s.on('session', (session) => {
|
||||
sess = sess || session;
|
||||
});
|
||||
s.once('session', (session) => onNewSession(s, session));
|
||||
s.once('session', () => ticketLog.push(s.getTLSTicket().toString('hex')));
|
||||
}
|
||||
|
||||
connect();
|
||||
|
Loading…
Reference in New Issue
Block a user