Quantum-Resistant TLS in Go

Quantum computing attacks may have already begun. Confidential data is being exchanged using algorithms that will eventually be broken by quantum computers. Even though attackers cannot break the communications today (for we lack sufficiently powerful quantum computers), they can patiently record them for future analysis.

Perhaps the most popular way to share confidential data between two remote parties is through a TLS connection. In a TLS handshake, asymmetric cryptography is used for two purposes. The first is to verify the authenticity of the party we are communicating with. The second is to securely agree a shared symmetric key, which will encrypt the rest of the communication.

This second purpose is more important for quantum attackers and defenders. Authentication is a temporal concern in TLS – provided you were happy with the identity of the other party when you complete the handshake, that’s all that matters. But confidentiality is a long-term concern. How many years or decades would need to pass before that confidential data doesn’t matter anymore?

Considering the above, when securing TLS handshakes against quantum attackers we should focus on the key exchange phase of the TLS handshake. We can keep using RSA or ECDSA certificates, but we need to replace (or enhance) the key exchange mechanisms that agree the symmetric keys.

This blog describes our recent work to enable quantum-resistant key exchange in TLS 1.3 for Go applications. After reading, please heed the security warnings at the end of the article.

Key exchange in TLS 1.3

Key exchange looks different in TLS 1.3, compared with earlier versions. In TLS 1.2, the key exchange was specified as part of the ciphersuite. For instance, the TLS 1.2 ciphersuite “TLS_DHE_RSA_WITH_AES_256_CBC_SHA” specifies ephemeral Diffie-Hellman (the “DHE” part) as the chosen key exchange.

In TLS 1.3, the key exchange is negotiated separately in the initial handshake messages between the client and server. In the ClientHello message, the client will typically send one or more key shares to the server. Each share will contain ECDHE or DHE parameters. The server selects one of these shares, computes its own parameters and sends them back in the ServerHello message. Both sides can then derive a common shared secret using the data they received.

TLS Key Exchange

Each key share is associated with a NamedGroup, defined in RFC 8446, Section 4.2.7. There are ten groups available (a mix of ECDHE and DHE), as well as a reserved range for private use. Since we are implementing non-standardised algorithms, we can use the private use range to trigger bespoke behaviour. As long as the client and server agree what our private NamedGroup means, we are in business.

Extending Go’s crypto/tls

Go implements TLS in the crypto/tls package. To add new key exchange algorithms, we needed to fork this package and make some changes.

We introduced a new interface to represent a key exchange, the PrivateKeyExchange:


 
// A PrivateKeyExchange implements a TLS 1.3 key exchange mechanism.
type PrivateKeyExchange interface {
 
  // ClientShare initiates the key exchange and returns the client key
  // share.
  ClientShare() ([]byte, error)
 
  // SecretFromClientShare is called by the server to process the share from
  // the client. It generates (or deduces) the TLS secret and returns this, along with
  // its own share, which is sent to the client.
  SecretFromClientShare(clientShare []byte) (secret, serverShare []byte, err error)
 
  // SecretFromServerShare uses the server key share to deduce the TLS secret,
  // which is returned.
  SecretFromServerShare(serverShare []byte) ([]byte, error)
}
 

The client side will use ClientShare and SecretFromServerShare to produce the key share and shared secret respectively. The server only uses the SecretFromClient share function.

We also extended tls.Config to allow developers to specify PrivateKeyExchange instances to use:


 
type Config struct {
 //...
 

  // PrivateKeyExchanges are TLS 1.3 key exchange implementations
  // for private named groups. The CurveIDs must be from the ecdhe_private_use range
  // (see RFC 8446 section 4.2.7). To enable these private groups, include their
  // CurveID in the CurvePreferences field.
  PrivateKeyExchanges map[CurveID]PrivateKeyExchange
}
 

These changes are available in our fork of Go 1.12.5: https://github.com/thales-e-security/go-tls-key-exchange. Fortunately, the changes are quite minimal, meaning porting to future Go versions would not be difficult.

We also opened a discussion with the Go crypto team about including this functionality in Go proper. You can follow that conversation here: proposal: crypto/tls: allow registration of additional key exchanges.

Accessing quantum-resistant crypto from Go

Modifying crypto/tls was half the battle. To use quantum-resistant algorithms with TLS, we now need implementations of PrivateKeyExchange.

We chose to build a Go wrapper around a popular open-source library that provides NIST quantum-resistant candidate implementations: https://github.com/open-quantum-safe/liboqs. Liboqs is part of the Open Quantum Safe project, which aims to provide software for prototyping quantum-resistant cryptography.

Calling a C library from Go code is reasonably straight-forward using cgo. The main headaches involve writing little “bridging” functions in C to help the Go code execute certain functionality from the C library. For example, Go struggles with function pointers, requiring helper functions to invoke the desired functionality. One also has to consider resource clean-up, which necessitates adding Close() functions to release memory used by the C library.

The resulting library, goliboqs, can be found at https://github.com/thales-e-security/goliboqs.

Putting it all together

We designed the PrivateKeyExchange interface so it would play nicely with NIST quantum-resistant candidate algorithms. All the NIST KEM implementations follow a standard three-function pattern for key generation, encapsulation and decapsulation. Liboqs exposes an identical interface for interacting with the KEMs it supports. We made sure PrivateKeyExchange was complementary to that approach.

We developed an example application to demonstrate a quantum-resistant handshake, which can be found within our Go fork at https://github.com/thales-e-security/go-tls-key-exchange/tree/go1.12.5_private_key_exchanges/example. This code couples together our work on the Go fork with our Go wrapper around liboqs. The resulting server and client binaries perform a handshake using a quantum-resistant algorithm.

Security warnings

Quantum-resistant algorithms are an active area of research. Compared to RSA or AES, most quantum-resistant candidate algorithms have received far less scrutiny from academia. Although NIST is helping to coordinate these efforts with its Post-Quantum Cryptography project, these algorithms should be regarded with some suspicion at this stage.

One approach to mitigate the risk is through a hybrid approach. In the TLS example, this would involve generating (say) half the secret material using classic algorithms and half through quantum-resistant algorithms.

Our work in this area aims to foster further research and innovation. Use of our projects in production environments is not recommended at this time.

Watch our interactive demo Explore
Schedule a live demo Schedule
Get in contact with a specialist Contact us