Skip to content
Permalink
Browse files

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 #25831 for more
information.

PR-URL: #26209
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rod Vagg <rod@vagg.org>
  • Loading branch information...
sam-github committed Nov 29, 2018
1 parent 4306300 commit 42dbaed4605f44c393a057aad75a31cac1d0e5f5
Showing with 988 additions and 220 deletions.
  1. +30 −7 doc/api/cli.md
  2. +90 −29 doc/api/tls.md
  3. +16 −6 doc/node.1
  4. +32 −4 lib/_tls_common.js
  5. +76 −12 lib/_tls_wrap.js
  6. +4 −0 lib/internal/stream_base_commons.js
  7. +13 −3 lib/tls.js
  8. +1 −0 src/node_constants.cc
  9. +7 −1 src/node_constants.h
  10. +137 −37 src/node_crypto.cc
  11. +5 −0 src/node_crypto.h
  12. +2 −0 src/node_crypto_clienthello.cc
  13. +6 −0 src/node_crypto_clienthello.h
  14. +23 −9 src/node_options.cc
  15. +5 −4 src/node_options.h
  16. +32 −4 src/tls_wrap.cc
  17. +1 −0 src/tls_wrap.h
  18. +11 −0 test/async-hooks/test-graph.tls-write-12.js
  19. +7 −4 test/async-hooks/test-graph.tls-write.js
  20. +6 −1 test/async-hooks/test-tlswrap.js
  21. +1 −0 test/parallel/test-crypto.js
  22. +1 −1 test/parallel/test-https-agent-additional-options.js
  23. +1 −1 test/parallel/test-https-agent-session-eviction.js
  24. +3 −0 test/parallel/test-https-client-renegotiation-limit.js
  25. +2 −1 test/parallel/test-https-client-resume.js
  26. +1 −1 test/parallel/test-tls-alert-handling.js
  27. +4 −1 test/parallel/test-tls-async-cb-after-socket-end.js
  28. +4 −2 test/parallel/test-tls-basic-validations.js
  29. +15 −0 test/parallel/test-tls-cli-max-version-1.2.js
  30. +15 −0 test/parallel/test-tls-cli-max-version-1.3.js
  31. +2 −2 test/parallel/test-tls-cli-min-version-1.0.js
  32. +2 −2 test/parallel/test-tls-cli-min-version-1.1.js
  33. +15 −0 test/parallel/test-tls-cli-min-version-1.3.js
  34. +33 −4 test/parallel/test-tls-client-auth.js
  35. +3 −0 test/parallel/test-tls-client-getephemeralkeyinfo.js
  36. +13 −0 test/parallel/test-tls-client-reject-12.js
  37. +8 −2 test/parallel/test-tls-client-reject.js
  38. +37 −0 test/parallel/test-tls-client-renegotiation-13.js
  39. +3 −0 test/parallel/test-tls-client-renegotiation-limit.js
  40. +13 −0 test/parallel/test-tls-client-resume-12.js
  41. +35 −8 test/parallel/test-tls-client-resume.js
  42. +13 −0 test/parallel/test-tls-destroy-stream-12.js
  43. +15 −7 test/parallel/test-tls-destroy-stream.js
  44. +7 −0 test/parallel/test-tls-disable-renegotiation.js
  45. +22 −1 test/parallel/test-tls-getcipher.js
  46. +50 −5 test/parallel/test-tls-min-max-version.js
  47. +13 −0 test/parallel/test-tls-net-socket-keepalive-12.js
  48. +5 −1 test/parallel/test-tls-net-socket-keepalive.js
  49. +2 −0 test/parallel/test-tls-server-verify.js
  50. +3 −0 test/parallel/test-tls-set-ciphers-error.js
  51. +82 −51 test/parallel/test-tls-set-ciphers.js
  52. +12 −0 test/parallel/test-tls-ticket-12.js
  53. +13 −3 test/parallel/test-tls-ticket-cluster.js
  54. +36 −6 test/parallel/test-tls-ticket.js
@@ -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
@@ -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
@@ -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.

0 comments on commit 42dbaed

Please sign in to comment.
You can’t perform that action at this time.