Friday, May 4, 2018

GnuTLS and TLS 1.3

[last update 2019-05-25]

GnuTLS already contains support for the latest TLS 1.3 draft (draft-ietf-tls-tls13-26) on its master git branch TLS 1.3 RFC in its 3.6.5 release. One of our major challenges related to TLS 1.3 protocol, was making its support transparent for existing applications; that is, without any code changes or the need for re-compilation. We believe we have sufficiently addressed that challenge, while at the same time made available several new features introduced by the protocol. This post will go through the new features of TLS 1.3, and discuss how one can take advantage of them with GnuTLS. We will also discuss potential pain points during the migration. More information will be provided in the 'upgrade section' of the manual after the release.

New features under TLS 1.3

Unlike its predecessors which were consisting of incremental updates, TLS 1.3 is a clean slate re-write of the TLS 1.2 protocol. In brief, on the algorithmic level the changes are quite minimal, with the support for RSA-PSS signatures being the most significant change, and the elimination of all ciphers which had known issues. On the protocol level, the changes span many aspects, mainly changes to reduce the message roundtrips during handshake, several security related fixes and protocol simplifications. These changes include the complete elimination of the convoluted re-handshake process, and replace it by the simpler re-key and re-authentication processes. They also include the defense against passive eavesdroppers for the certificates sent during handshake, the introduction of a foundation for message length hiding, and the ability to attach OCSP staples to all certificates sent during handshake.

Testing TLS 1.3

In GnuTLS 3.6.5, TLS 1.3 is enabled by default.

Single roundtrip handshake

In contrast with TLS 1.2 which had a handshake message round-trip of 2, the TLS 1.3 handshake process typically takes a single round-trip. The idea behind that optimization is that the client sends its key shares for the key exchange speculatively, by guessing the Diffie-Hellman group(s) the server supports. If that speculation fails, the handshake falls backs to a more verbose mode which includes two additional messages (Hello-Retry-Request and ClientHello) to negotiate the commonly supported groups.

In GnuTLS the client sends by default the key share of the two distinct group types which have the highest priority; currently that is the elliptic curve groups SECP256R1 and X25519. The reason key shares from these two groups were selected is because they are widely available, and require little resources from a CPU-time perspective, while minimizing the risk of fall-back to the more verbose handshake. In order to modify that behavior, applications can re-order the preferred group list via priority strings, and/or provide a special flag to gnutls_init() which switches the behavior. The flag GNUTLS_KEY_SHARE_TOP instructs GnuTLS to send a single key share, GNUTLS_KEY_SHARE_TOP3 to send the top 3 distinct group key shares (e.g., SECP256R1, X25519 and FFDHE2048).

Note however, that the round-trips achieved by TLS 1.3 do not translate to network roundtrip when operating under TCP. That is because TCP introduces an additional round-trip due to the SYN-ACK TCP exchange. To eliminate the additional round-trip, the same tricks apply as with TLS 1.2; one needs to rely on the TCP fast open.

New ciphersuites and key exchange changes

TLS 1.3 replaces all the ciphersuites available under TLS 1.2 and earlier with a new set of five ciphersuites. The ciphersuite meaning is also changed from expressing the key exchange, authentication method and cipher/MAC, to define the cipher and MAC algorithms only. The reason for that change is that in TLS 1.3 there are only two authentication methods defined; the certificate and the pre-shared key (PSK) which are negotiated separately using extensions. Similarly, the key exchange methods, expanded in scope since TLS1.2, and from the being part of the ciphersuite, as elliptic curve Diffie-Hellman (ECDHE) or finite field Diffie-Hellman (DHE), they are now fine-tuned to the actual group in use. That is SECP256R1 for the NIST P256R1 elliptic curve group, or X25519, or FFDHE-2048 for the RFC7919 Diffie-Hellman group and so on. The groups are negotiated via the supported groups TLS extension, and are set in GnuTLS via the priority strings.

The following new ciphersuites are introduced in TLS 1.3:
  • AES-256-GCM
  • CHACHA20-POLY1305
  • AES-128-GCM
  • AES-128-CCM
  • AES-128-CCM-8
They are all supported by GnuTLS, though AES-128-CCM-8, which has a truncated tag, is not enabled by default. Furthermore, the fact that TLS 1.3 limited all the available ciphers to this small set, prompted us for a cleanup of our default settings for TLS 1.2 and earlier. We saw no reason to provide different cipher choices depending on the negotiated protocol. As a result we kept that base of ciphers, disabling the Camellia and 3DES ciphersuites across all protocol versions.

Note that existing applications need no changes to take advantage of the new ciphers. The existing priority strings were sufficiently flexible to support them.

Post handshake authentication

TLS 1.2 provided a combined re-authentication and re-key mechanism under the re-handshake umbrella. In TLS 1.3 these two mechanisms are disassociated, and replaced by the re-key (to be discussed later) and the post-handshake authentication mechanism. The latter, is a mechanism that can be triggered only by the server, and requests the client to present a certificate. In GnuTLS the implementation relies on a new non-fatal error code which must be handled by the client application. To avoid unintentional side-effects to existing software, this mechanism must be explicitly enabled by the client and the server. That enablement is done by specifying GNUTLS_POST_HANDSHAKE_AUTH as a gnutls_init() flag in both client and server. Once that done, a server can request post-handshake authentication from the client by calling the new API, gnutls_reauth(), and the client should re-act to the GNUTLS_E_REAUTH_REQUEST error code, by calling the same function.


Under TLS 1.3 applications can re-key with a very simple mechanism which involves the transmission of a single message. Furthermore, GnuTLS handles re-key transparently and every GnuTLS client and server will automatically re-key after 2^24 messages are exchanged, unless the GNUTLS_NO_AUTO_REKEY flag is specified in gnutls_init(), or the cipher's security properties requires no re-keying as in the CHACHA20-POLY1305 cipher.

To accomodate for well-written applications which were using the re-handshake mechanism of TLS 1.2 to perform re-key, the gnutls_rehandshake() and gnutls_handshake() calls under TLS 1.3 perform a re-key on server and client side respectively.

Server applications which were relying on the re-handshake process to re-authenticate the client must be modified to take advantage of post-handshake authentication under TLS 1.3.

Message length hiding

In TLS 1.3 it is possible to hide the length of the transmitted data on the wire by introducing padding bytes. To take advantage of that, we enhanced the  gnutls_record_send_range() and related APIs, and after realizing their complexity when performing certain length hiding tasks, we decided to introduce a new and simple API for adding padding data. That is, the gnutls_record_send2() API. The new API allows sending an arbitrary amount of data, together with an arbitrary amount of padding limited only by the record constraints.

Attaching multiple OCSP staples

Under TLS 1.3, servers and clients can attach OCSP responses for more than a single certificate in their certificate chain. That is, they can provide fresh responses for their certificate authority, or other intermediate authorities, making the task of certificate validation easier for the peer, without involving the OCSP server during the handshake.

The addition of that functionality resulted to enhancements in the ocsptool utility, in order to be able to export and import OCSP responses in PEM format, something that allows them to be combined with the already PEM-encoded certificate chains. Applications could then use the APIs gnutls_certificate_set_ocsp_status_request_mem() or gnutls_certificate_set_ocsp_status_request_file2() to specify OCSP responses which correspond to their certificates.

RSA-PSS certificates and TLS 1.3 deployment

When we introduced support for RSA-PSS keys and certificates in GnuTLS 3.6.0, we saw them at the time as necessary for TLS 1.3 negotiation, and as a potential to increase the overall protocol security. However, that never happened through the TLS 1.3 drafts, possibly due to striking a balance to the need for security against cross-protocol attacks and compatibility with existing keys and practices. Furthermore, RSA-PSS keys and certificates are quite new, not being universally supported by CAs or by hardware security modules and smart cards.

As such, although RSA-PSS certificates are allowed by the protocol and supported by GnuTLS, the expected method to obtain an RSA-PSS signature in TLS 1.3, is by utilizing a "legacy" RSA PKCS#1 1.5 certificate associated with a multi-purpose RSA key. That is, a legacy RSA key is expected to be used for RSA-PSS, PKCS#1 1.5 signatures and PKCS#1 1.5 decryption. That, to a cryptographer, voids any security proofs available to RSA-PSS algorithm, since such proofs assume a key which is used for a single purpose.

That, although it may have failed to provide a bump in the overall Internet security, had quite good usability reasons to back it up. We are far from having a universal support of RSA-PSS certificates in existing software or hardware, and the ability to re-use the exact same setup and keys as a TLS 1.2 server was seen as of paramount importance in the TLS working group. The protocol designers recognized, however, the security issues and have documented the best practices sufficient to defend against the worst offender in RSA key attacks. That is, against the RSA PKCS#1 (decryption) ciphersuites, which have been the root of every new clone of Bleichenbacher's attack. Their recommendation is to disable the static RSA ciphersuites on TLS 1.3 servers. That is not done (at least for now) by GnuTLS by default, though, it can be achieved with a priority string which disables static RSA, e.g., "NORMAL:-RSA". The reason of not disabling these ciphersuites by default is to allow connections by legacy software and embedded software which often rely on the static RSA due to its simplicity.

RSA client certificates on smart cards and TLS 1.3 deployment

Under TLS 1.3 it is no longer possible to negotiate the older RSA signing standard, and thus existing smart cards containing RSA keys but not supporting the RSA-PSS mechanism, cannot be used under TLS1.3. There is no easy solution to that issue. Possible options could be (a) re-deploying all the smart cards with new which support RSA-PSS, or cars with the ECDSA algorithm, (b) disabling TLS 1.3 support on client or server side. At GnuTLS 3.6.8 we make sure that GnuTLS servers or clients when presented with such limited hardware, disable support for TLS1.3 transparently for applications instead of failing.

Key derivation

Although key derivation under TLS 1.3 uses different algorithms under the hood, the standard interfaces from RFC5705, continue to be functional for key derivation. As such the interface gnutls_prf_rfc5705() must be used for key derivation by applications; older interfaces like gnutls_prf_raw() or gnutls_prf() return an error code when used under TLS 1.3.

Session Resumption

Session resumption in the previous versions of TLS consisted of exporting the serialized session data as negotiated by TLS the handshake to use them in a future handshake. TLS 1.3 disassociates session resumption from session parameters, and session resumption is an optional server feature, advertised by the server in the form of a resumption ticket which can be sent at any time after the handshake is complete, and can be used only once to connect to the server. Merging the previous resumption semantics with the new, was quite a challenge, though it was possible. A difference with TLS 1.2 semantics is that a call to gnutls_session_get_data2() could wait to receive data from the network, for a maximum of 50 milliseconds.

What about applications not utilizing certificates?

This short section provides information for existing applications which rely on the SRP, Anonymous or PSK key exchanges which were available under TLS 1.2.

SRP / RSA-PSK key exchange

SRP and RSA-PSK key exchanges are not supported in TLS 1.3, so when these key exchanges are present in a priority string, TLS 1.3 is disabled.

Anonymous key exchange

There is no anonymous key exchange supported under TLS 1.3, so if an anonymous key exchange method is set in a priority string, and no certificate credentials are set in the client or server TLS 1.3 will not be negotiated. That approach allows for TLS 1.3 to be negotiated when a server or client supports both anonymous and certificate key exchange, i.e., when supporting an opportunistic-encryption negotiation scenario.

PSK key exchange

The pre-shared key exchange is supported in TLS 1.3, thus any existing setup with pre-shared key is unaffected by the upgrade to TLS 1.3. Both the Diffie-Hellman and "pure" PSK are supported under the new protocol, however in the priority strings the 'ECDHE-PSK' and 'DHE-PSK' options are handled as synonyms and they indicate the intent to support an ephemeral key exchange with the pre-shared key; the parameters of the key exchange, e.g., elliptic curves vs finite field, are negotiated with the supported groups specified in the priority string.

A thing to note is that although certificates are protected against passive eavesdroppers in TLS 1.3, PSK usernames are still sent in the clear, as in TLS 1.2.

Authentication-only ciphersuites

Ciphersuites with the NULL cipher (i.e., authentication-only) are not supported in TLS 1.3, so when they are specified in a priority string, TLS 1.3 is disabled.

Deprecated features

During the GnuTLS 3.6.x releases there was a significant effort to eliminate obscure options and code which could prove a liability in the future. As part of that, support for compression, OpenPGP authentication and SHA2-224 signatures in TLS key exchange was removed, along with their corresponding code. Furthermore, the DSA signature algorithm and the Camellia and 3DES ciphers were disabled by default for TLS sessions, while SHA1 was marked as insecure for certificate signatures.

What's next?

Although, a functional part of the TLS 1.3 protocol is implemented in GnuTLS, there are few items which are still missing and are marked with this label. Furthermore, we would like to extend our existing test suite for TLS 1.3 which consists of unit and interoperability tests, with the tlsfuzzer TLS 1.3 test suite once that is made available. The advantage of the tlsfuzzer test suite, is its design with an attacker mindset and helps uncover issues in corner and rare cases. The inclusion of this test suite will be a prerequisite for the 3.6.x branch being marked as the stable branch [update in 2019-5-25: this branch is already marked as stable and the tlsfuzzer test suite is incorporated].

No comments:

Post a Comment