This post is a step-by-step instruction to build HTTPS web server which uses quantum-resistant algorithm for TLS key exchange. Solution is written in Go language. For a web server I’ve used Caddy. Caddy uses TLS implementation from standard Go library. As this doesn’t support any of quantum-resistant algorithms I’ll change it with another implementation.

The quantum-resistant algorithm of my choice is SIDH. Quite some effort has been already put into security research of SIDH. Basics of the algorithm are explained in more details by @LVH in his blog post as well as on Wiki. I’ll use Cloudflare’s implementation of SIDH available from here. One interesting characteristic of the algorithm is that it can be used as a drop-in replacement for the ECDH.

Finally, I’ll need TLS implementation which supports SIDH. This has been done in tls-tris. The library provides built-in support for SIDH/P503-X25519 - key exchange based on IETF draft Hybrid ECDHE-SIDH Key Exchange for TLS. This is done over TLS v1.3, which library now also supports. The tls-tris is code compatible with TLS implementation from standard Go, which makes it possible to simply swap one implementation with the other.

Step 1: SIDH support in TLS

With tls-tris it is possible to perform SIDH key exchange. In order to link application (Caddy in this case) with TLS tris one needs to swap TLS implementation in standard library and recompile Go from source. All this is implementated in the Makefile that comes with the library. Binaries are placed in tls-tris/_dev/GOROOT folder, the GOROOT needs to point to this folder.

Let’s first download needed sources:

# Create some workspace
WORKSPACE=/tmp/workspace/
mkdir -p ${WORKSPACE}

# Get Go 1.10 sources (if not already done)
cd ${WORKSPACE} 
wget https://dl.google.com/go/go1.10.4.linux-amd64.tar.gz -O - | tar -xz

# Clone tls-tris 
git clone https://github.com/cloudflare/tls-tris

Next step is to build Go with TLSv1.3 and SIDH support. This is done automatically and makefile implements all needed steps. The build-all target will basically:

  • Swap standard library TLS with tls-tris
  • Download SIDH crypto library and vendor it to ${WORKSPACE}/go/src/vendor directory. This way SIDH is available as it would be a part of standard library.
# By specifying GOROOT_ENV makefile knows where to look for Go sources
cd tls-tris; GOROOT_ENV=${WORKSPACE}/go make -f _dev/Makefile build-all

Finally GOROOT needs to be adjusted:

export GOROOT=${WORKSPACE}/tls-tris/_dev/GOROOT/linux_amd64

Step 2: Patching Caddy

By default Caddy supports TLS up to version 1.2. Thanks to steps above, TLSv1.3 and Hybrid SIDH/503-X25519 key exchange are ready to use. Nevertheless, some code changes to Caddy are needed in order to benefit from those new features.

Let’s start by cloning the sources:

mkdir -p /tmp/gopath
export GOPATH=/tmp/gopath
go get github.com/mholt/caddy/caddy 
go get github.com/caddyserver/builds
cd $GOPATH/src/github.com/mholt/caddy

Now we need to add 2 lines of code in order to:

  1. Enable TLS v1.3: In file caddytls/config.go Caddy keeps a map of supported TLS protocols. The TLSv.1.3 needs to be added to this map.
  2. Enable SIDH: In the same file Caddy also keeps map of supported curves. The scheme called tls.HybridSIDHp503Curve25519 needs to be added to this map.

The whole diff should look something like that:

> git diff
diff --git a/caddytls/config.go b/caddytls/config.go
index 8cf61e4..fc7510d 100644
--- a/caddytls/config.go
+++ b/caddytls/config.go
@@ -583,6 +583,7 @@ var SupportedProtocols = map[string]uint16{
        "tls1.0": tls.VersionTLS10,
        "tls1.1": tls.VersionTLS11,
        "tls1.2": tls.VersionTLS12,
+       "tls1.3": tls.VersionTLS13,
 }
 
 // GetSupportedProtocolName returns the protocol name
@@ -682,6 +683,7 @@ var supportedCurvesMap = map[string]tls.CurveID{
        "P256":   tls.CurveP256,
        "P384":   tls.CurveP384,
        "P521":   tls.CurveP521,
+       "SIDH/503-X25519": tls.HybridSIDHp503Curve25519,
 }
 
 // List of all the curves we want to use by default.

With those changes applied, Caddy can be built:

# Once again, just to make sure right Go version is used
export GOROOT=${WORKSPACE}/tls-tris/_dev/GOROOT/linux_amd64

# And let's build caddy
cd $GOPATH/src/github.com/mholt/caddy/caddy
go run build.go

Caddy configuration and server bring up

At this point hybrid post quantum key exchange should be supported by Caddy. Obviusly that’s not a default configuration, so one needs to tell Caddy to use it. Caddy is configuring by providing Caddyfile. In this file max version of the protocol is set to “tls1.3”. Also elliptic curve preferences are changed by specifying “SIDH/503-X25519” as key exchange algorithm. My minimal Caddyfile looks like this:

localhost:2015 {
        tls self_signed
        tls {
                protocols tls1.2 tls1.3
                curves X25519 P256 "SIDH/503-X25519"
        }
        log stdout
        proxy / http://www.amongbytes.com
}

This file is placed in the same folder where “caddy” binary lives, namely $GOPATH/src/github.com/mholt/caddy/caddy/Caddyfile. It will basically open a port 2015 on localhost and forward all traffic to http://www.amongbytes.com. The self_signed certificate will be used in this test configuration. Let’s start it:

> cd $GOPATH/src/github.com/mholt/caddy/caddy
> ./caddy
Activating privacy features... done.
https://localhost:2015

Looks like it’s working. Now it would be good to actually test if post quantum key exchange is working. There are probably not too many browsers supporting SIDH/503-X25519 (if any). Nevertheless tls-tris contains a patch for boringssl which adds SIDH and is used for interoperability testing. We can reuse it to test our setup.

cd ${WORKSPACE}

# Patch for BoringSSL adding SIDH/P503-X25519 support
wget https://raw.githubusercontent.com/cloudflare/tls-tris/master/_dev/boring/sidh_ff433815b51c34496bb6bea13e73e29e5c278238.patch

# Clone and checkout BoringSSL. I'm using specific commit as I want to make sure patch applies correctly
git clone https://boringssl.googlesource.com/boringssl
cd boringssl
git fetch && git checkout ff433815b51c34496bb6bea13e73e29e5c278238 
patch -p1 < ../sidh_ff433815b51c34496bb6bea13e73e29e5c278238.patch

# When building, make sure EXP_SIDH is defined as it enables SIDH
mkdir build && cd build; cmake -DEXP_SIDH=1 -GNinja .. && ninja

Assuming server is running we can now check if quantum resistant TLS handshake works.

> ./tool/bssl client -curves x25519sidh503 -connect localhost:2015                                         
Connecting to [::1]:2015
Connected.
  Version: TLSv1.3
  Resumed session: no
  Cipher: TLS_AES_128_GCM_SHA256
  ECDHE curve: x25519sidh503
  Signature algorithm: ecdsa_secp256r1_sha256
  Secure renegotiation: yes
  Extended master secret: yes
  Next protocol negotiated: 
  ALPN protocol: 
  OCSP staple: no
  SCT list: no
  Early data: no
  Cert subject: O = Caddy Self-Signed
  Cert issuer: O = Caddy Self-Signed

BoringSSL reports that ECHDE curve used for key exchange was “x25519sidh503”. It seems post quantum key exchange works just all right!

Conclusion and next steps

I’m neither an expert nor big fan of Golang. Nevertheless, I think it is great language for performing experiments - especially those related to networking. The setup presented here is used for experiments related to post-quantum cryptographic primitive implementation. I’m using it both on ARM and Intel and thanks to Golang’s build tools, compilation process is very simple, fast and quite easy to perform.

The SIDH looks interesting as a quantum resistant replacement for ECDH. Nevertheless, KEM construction providing IND-CCA2 security sounds to me like something worth trying. In the next steps I will be experimenting with other algorithms - my non exhaustive list contains Round5 presenting very interesting results, something NTRU based, Kyber and CSIDH. Also at further step I’ll try to tackle quantum-resistant signature schemes.