From 42dbaed4605f44c393a057aad75a31cac1d0e5f5 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 28 Nov 2018 17:58:08 -0800 Subject: [PATCH] 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 Reviewed-By: James M Snell Reviewed-By: Rod Vagg --- doc/api/cli.md | 37 +++- doc/api/tls.md | 119 +++++++++--- doc/node.1 | 22 ++- lib/_tls_common.js | 36 +++- lib/_tls_wrap.js | 88 +++++++-- lib/internal/stream_base_commons.js | 4 + lib/tls.js | 16 +- src/node_constants.cc | 1 + src/node_constants.h | 8 +- src/node_crypto.cc | 176 ++++++++++++++---- src/node_crypto.h | 5 + src/node_crypto_clienthello.cc | 2 + src/node_crypto_clienthello.h | 6 + src/node_options.cc | 30 ++- src/node_options.h | 9 +- src/tls_wrap.cc | 36 +++- src/tls_wrap.h | 1 + test/async-hooks/test-graph.tls-write-12.js | 11 ++ test/async-hooks/test-graph.tls-write.js | 11 +- test/async-hooks/test-tlswrap.js | 7 +- test/parallel/test-crypto.js | 1 + .../test-https-agent-additional-options.js | 2 +- .../test-https-agent-session-eviction.js | 2 +- .../test-https-client-renegotiation-limit.js | 3 + test/parallel/test-https-client-resume.js | 3 +- test/parallel/test-tls-alert-handling.js | 2 +- .../test-tls-async-cb-after-socket-end.js | 5 +- test/parallel/test-tls-basic-validations.js | 6 +- test/parallel/test-tls-cli-max-version-1.2.js | 15 ++ test/parallel/test-tls-cli-max-version-1.3.js | 15 ++ test/parallel/test-tls-cli-min-version-1.0.js | 4 +- test/parallel/test-tls-cli-min-version-1.1.js | 4 +- test/parallel/test-tls-cli-min-version-1.3.js | 15 ++ test/parallel/test-tls-client-auth.js | 39 +++- .../test-tls-client-getephemeralkeyinfo.js | 3 + test/parallel/test-tls-client-reject-12.js | 13 ++ test/parallel/test-tls-client-reject.js | 10 +- .../test-tls-client-renegotiation-13.js | 37 ++++ .../test-tls-client-renegotiation-limit.js | 3 + test/parallel/test-tls-client-resume-12.js | 13 ++ test/parallel/test-tls-client-resume.js | 43 ++++- test/parallel/test-tls-destroy-stream-12.js | 13 ++ test/parallel/test-tls-destroy-stream.js | 22 ++- .../test-tls-disable-renegotiation.js | 7 + test/parallel/test-tls-getcipher.js | 23 ++- test/parallel/test-tls-min-max-version.js | 55 +++++- .../test-tls-net-socket-keepalive-12.js | 13 ++ .../parallel/test-tls-net-socket-keepalive.js | 6 +- test/parallel/test-tls-server-verify.js | 2 + test/parallel/test-tls-set-ciphers-error.js | 3 + test/parallel/test-tls-set-ciphers.js | 135 ++++++++------ test/parallel/test-tls-ticket-12.js | 12 ++ test/parallel/test-tls-ticket-cluster.js | 16 +- test/parallel/test-tls-ticket.js | 42 ++++- 54 files changed, 990 insertions(+), 222 deletions(-) create mode 100644 test/async-hooks/test-graph.tls-write-12.js create mode 100644 test/parallel/test-tls-cli-max-version-1.2.js create mode 100644 test/parallel/test-tls-cli-max-version-1.3.js create mode 100644 test/parallel/test-tls-cli-min-version-1.3.js create mode 100644 test/parallel/test-tls-client-reject-12.js create mode 100644 test/parallel/test-tls-client-renegotiation-13.js create mode 100644 test/parallel/test-tls-client-resume-12.js create mode 100644 test/parallel/test-tls-destroy-stream-12.js create mode 100644 test/parallel/test-tls-net-socket-keepalive-12.js create mode 100644 test/parallel/test-tls-ticket-12.js diff --git a/doc/api/cli.md b/doc/api/cli.md index b2589cd52fd..cf28d9b2bf9 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -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` -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` -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` + + +Set default [`minVersion`][] to `'TLSv1'`. Use for compatibility with old TLS +clients or servers. + +### `--tls-min-v1.1` + + +Set default [`minVersion`][] to `'TLSv1.1'`. Use for compatibility with old TLS +clients or servers. + +### `--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. ### `--trace-deprecation` @@ -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