This demo was
announced 2020.04.16
on the
pqc-forum mailing list,
updated 2020.04.23
from OpenSSL 1.1.1f to OpenSSL 1.1.1g,
updated 2021.06.08
from OpenSSL 1.1.1g to OpenSSL 1.1.1k,
including additional support for sntrup857
,
updated 2021.09.30
from OpenSSL 1.1.1k to OpenSSL 1.1.1l,
alongside an update of the instructions to use
stunnel 5.60 and glib-networking 2.60.4,
updated 2021.11.02
to cover usage of tls_timer
and suggestions
regarding its use for experiments,
and
updated 2021.12.14
from OpenSSL 1.1.1l to OpenSSL 1.1.1m.
Our patches work for versions of OpenSSL from 1.1.1f to 1.1.1m.
This is a demo of OpenSSLNTRU web browsing
taking just 156317 Haswell cycles
to generate a new one-time sntrup761
public key
for each TLS 1.3 session.
This demo uses
- the Gnome web browser (client) and
stunnel
(server) using - a patched version of OpenSSL 1.1.1m using
- a new OpenSSL ENGINE using
- a fast new
sntrup761
library.
The TLS 1.3 integration in OpenSSLNTRU
uses the same basic data flow as the CECPQ2 experiment
carried out by Google and Cloudflare.
Compared to the cryptography in CECPQ2,
the cryptography in OpenSSLNTRU has a higher security level and better performance.
Furthermore,
OpenSSLNTRU's new software layers
decouple the fast-moving post-quantum software ecosystem
from the TLS software ecosystem.
OpenSSLNTRU also supports a second NTRU Prime parameter set, sntrup857
,
optimizing computation costs at an even higher security level.
Demo overview
Warning:
This demo comes with no cryptographic warranties
and no other security warranties.
The software here is experimental,
and is built upon other software
with a long history of security problems,
such as OpenSSL.
The purpose of this demo
is purely to show the sntrup761
and sntrup857
performance
achievable with a CECPQ2-type data flow for TLS 1.3.
The demo has two parts: a server side and a client side. We recommend running each side in its own VM.
The server side uses stunnel
for SSL termination.
It receives TLS connections,
including sntrup761
and sntrup857
connections,
and passes along the answers
provided by a preexisting back-end web server,
which does not need to support sntrup761
/sntrup857
connections.
For example, the demo site
https://test761.cr.yp.to
looks just like the preexisting site
https://ntruprime.cr.yp.to
,
but with the extra feature
of supporting sntrup761
connections.
Internally,
https://test761.cr.yp.to
passes requests along through a local connection
to the preexisting back-end web server
for ntruprime.cr.yp.to
.
You can use
https://test761.cr.yp.to
as the server side of this demo,
or you can set up the server side
for a web server of your choice.
The client side uses Epiphany,
the Gnome web browser,
with no modifications to the Epiphany source code.
The glib-networking
library used inside Epiphany
already supports OpenSSL as an option for outgoing connections,
and is configured below to use this option.
Both sides use a version of OpenSSL 1.1.1m
patched inside libssl
to support sntrup761
and sntrup857
respectively as experimental groups 0xfe00 and 0xfe01 for TLS 1.3.
We also provide a separate optional patch for libcrypto
to include a reference implementation of sntrup761
.
Our new engntru
library
then overrides this reference implementation
providing a faster implementation,
which in turn is built on top of our new libsntrup761
.
engntru
also provides access to a fast implementation of sntrup857
built on top of our new libsntrup857
.
This way of using the OpenSSL ENGINE feature
allows OpenSSL to take advantage of fast software implementations
while allowing those implementations
to be developed in separate libraries;
see https://eprint.iacr.org/2018/354
.
Various other applications that use OpenSSL
have been verified to transparently work
with libsntrup761
and libsntrup857
via engntru
.
This demo focuses on stunnel
on the server side
and Epiphany on the client side.
Server side
The following instructions for setting up the server side
have been tested in a VM
running Debian 11 (Bullseye)
on a CPU supporting AVX2.
You can skip down to the client side
if you simply want to try
https://test761.cr.yp.to
as the server.
As root:
apt install -y wget python3 build-essential clang cmake pkg-config
adduser --disabled-password --gecos opensslntru opensslntru
As the new opensslntru
user
(change the first three lines
for your own demo server name,
demo server address,
and preexisting back-end server address—of course,
you should use your favorite VPN
to protect the connection
from this SSL terminator to the back-end server):
EXTERNALNAME=test761.cr.yp.to
EXTERNALADDRESS=1.2.3.4:65024 # provide TLS service on this address
INTERNALADDRESS=5.6.7.8:80 # use existing server on this address
export OPENSSLNTRU_PREFIX=$HOME/local
export PATH=$OPENSSLNTRU_PREFIX/bin:$PATH
cd
wget https://www.openssl.org/source/openssl-1.1.1m.tar.gz
wget https://opensslntru.cr.yp.to/openssl-1.1.1m-ntru.patch
# Following patch is optional, to embed a reference
# implementation of sntrup761 in libcrypto:
# wget https://opensslntru.cr.yp.to/openssl-1.1.1m-ntru-add_ref_sntrup761.patch
tar -xf openssl-1.1.1m.tar.gz
mv openssl-1.1.1m openssl-1.1.1m-ntru
cd openssl-1.1.1m-ntru
patch -p1 < ../openssl-1.1.1m-ntru.patch
# Optionally, to embed a portable reference implementation
# of sntrup761 in libcrypto:
# patch -p1 < ../openssl-1.1.1m-ntru-add_ref_sntrup761.patch
./config shared --prefix=$OPENSSLNTRU_PREFIX --openssldir=$OPENSSLNTRU_PREFIX -Wl,-rpath=$OPENSSLNTRU_PREFIX/lib
make update
make -j8 # a few minutes
make test # more minutes
make install_sw
cd
wget https://opensslntru.cr.yp.to/libsntrup761-20210608.tar.gz
tar -xf libsntrup761-20210608.tar.gz
cd libsntrup761-20210608
env CPATH="${OPENSSLNTRU_PREFIX}/include" LIBRARY_PATH="${OPENSSLNTRU_PREFIX}/lib" make USE_RPATH=RUNPATH OPENSSL_PREFIX="${OPENSSLNTRU_PREFIX}" PREFIX="${OPENSSLNTRU_PREFIX}" all test install
cd
wget https://opensslntru.cr.yp.to/libsntrup857-20210608.tar.gz
tar -xf libsntrup857-20210608.tar.gz
cd libsntrup857-20210608
env CPATH="${OPENSSLNTRU_PREFIX}/include" LIBRARY_PATH="${OPENSSLNTRU_PREFIX}/lib" make USE_RPATH=RUNPATH OPENSSL_PREFIX="${OPENSSLNTRU_PREFIX}" PREFIX="${OPENSSLNTRU_PREFIX}" all test install
cd
wget https://opensslntru.cr.yp.to/engntru-20210608.tar.gz
tar -xf engntru-20210608.tar.gz
cd engntru-20210608
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -Dprovider="batch_lib" -DCMAKE_PREFIX_PATH="${OPENSSLNTRU_PREFIX}" ..
make
make install
cd
wget https://www.stunnel.org/archive/5.x/stunnel-5.60.tar.gz
tar -xf stunnel-5.60.tar.gz
cd stunnel-5.60
./configure --prefix="${OPENSSLNTRU_PREFIX}" --with-ssl="${OPENSSLNTRU_PREFIX}" LDFLAGS=-Wl,-rpath="${OPENSSLNTRU_PREFIX}/lib"
make
make install
cd
mkdir service
cd service
openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout "$EXTERNALNAME.key" -days 730 -out "$EXTERNALNAME.crt" -subj "/CN=$EXTERNALNAME" -config /etc/ssl/openssl.cnf
(
echo "key = $EXTERNALNAME.key"
echo "cert = $EXTERNALNAME.crt"
echo 'foreground = yes'
echo 'engine = engntru'
echo 'engineDefault = ALL'
echo '[forward]'
echo "accept = $EXTERNALADDRESS"
echo "connect = $INTERNALADDRESS"
echo 'curves = SNTRUP761:SNTRUP857:X25519:P-256'
echo 'config = MinProtocol:TLSv1.2'
echo 'ciphers = ECDHE+CHACHA20:ECDHE+AES256:ECDHE+AES128:!aNULL:!eNULL:!LOW:!EXPORT:!DES:!3DES:!RC4:!MD5:!PSK:!SRP:!DSS:!aECDSA'
) > stunnel.conf
As root:
(
echo '[Unit]'
echo 'Description=opensslntru forwarding'
echo 'DefaultDependencies=no'
echo 'After=network.target'
echo ''
echo '[Service]'
echo 'Type=simple'
echo 'User=opensslntru'
echo 'Group=opensslntru'
echo 'WorkingDirectory=/home/opensslntru/service'
echo 'ExecStart=/home/opensslntru/local/bin/stunnel stunnel.conf'
echo ''
echo '[Install]'
echo 'WantedBy=default.target'
) > /etc/systemd/system/opensslntru.service
ln -s /etc/systemd/system/opensslntru.service /etc/systemd/system/multi-user.target.wants
systemctl restart opensslntru
At this point the server should be working. Try any browser to connect to the server's external address. The certificate is self-signed; signing it with Let's Encrypt is recommended but is outside the scope of these instructions.
This stunnel
configuration
passes SNI along from the client to the server,
so the client is free to access
any server name provided by the server.
For example, almost all *.cr.yp.to
are hosted on the same back-end server
and can now be retrieved through sntrup761
,
although for the moment
this is announced to the client (and signed)
only for test761.cr.yp.to
.
You can advertise multiple names
on the same server
through the same stunnel configuration
by adding those names to DNS
and creating an appropriate certificate.
You can instead configure stunnel
to forward different SNI choices
to different servers with different certificates.
Client side
The following instructions for setting up the client side have been tested in a VM running Debian 10 (Buster) on a CPU supporting AVX2.
As root:
apt install -y wget python3 build-essential clang cmake \
pkg-config epiphany-browser meson gnome-pkg-tools \
libglib2.0-dev libproxy-dev \
gsettings-desktop-schemas-dev ca-certificates
adduser --disabled-password --gecos opensslntru opensslntru
As the new opensslntru
user:
export OPENSSLNTRU_PREFIX=$HOME/local
export PATH=$OPENSSLNTRU_PREFIX/bin:$PATH
cd
wget https://www.openssl.org/source/openssl-1.1.1m.tar.gz
wget https://opensslntru.cr.yp.to/openssl-1.1.1m-ntru.patch
# Following patch is optional, to embed a reference
# implementation of sntrup761 in libcrypto:
# wget https://opensslntru.cr.yp.to/openssl-1.1.1m-ntru-add_ref_sntrup761.patch
tar -xf openssl-1.1.1m.tar.gz
mv openssl-1.1.1m openssl-1.1.1m-ntru
cd openssl-1.1.1m-ntru
patch -p1 < ../openssl-1.1.1m-ntru.patch
# Optionally, to embed a portable reference implementation
# of sntrup761 in libcrypto:
# patch -p1 < ../openssl-1.1.1m-ntru-add_ref_sntrup761.patch
./config shared --prefix=$OPENSSLNTRU_PREFIX --openssldir=$OPENSSLNTRU_PREFIX -Wl,-rpath=$OPENSSLNTRU_PREFIX/lib
make update
make -j8 # a few minutes
make test # more minutes
make install_sw
cd
wget https://opensslntru.cr.yp.to/libsntrup761-20210608.tar.gz
tar -xf libsntrup761-20210608.tar.gz
cd libsntrup761-20210608
env CPATH="${OPENSSLNTRU_PREFIX}/include" LIBRARY_PATH="${OPENSSLNTRU_PREFIX}/lib" make USE_RPATH=RUNPATH OPENSSL_PREFIX="${OPENSSLNTRU_PREFIX}" PREFIX="${OPENSSLNTRU_PREFIX}" all test install
cd
wget https://opensslntru.cr.yp.to/libsntrup857-20210608.tar.gz
tar -xf libsntrup857-20210608.tar.gz
cd libsntrup857-20210608
env CPATH="${OPENSSLNTRU_PREFIX}/include" LIBRARY_PATH="${OPENSSLNTRU_PREFIX}/lib" make USE_RPATH=RUNPATH OPENSSL_PREFIX="${OPENSSLNTRU_PREFIX}" PREFIX="${OPENSSLNTRU_PREFIX}" all test install
cd
wget https://opensslntru.cr.yp.to/engntru-20210608.tar.gz
tar -xf engntru-20210608.tar.gz
cd engntru-20210608
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -Dprovider="batch_lib" -DCMAKE_PREFIX_PATH="${OPENSSLNTRU_PREFIX}" ..
make
make install
cd
git clone --branch 2.60.4 https://gitlab.gnome.org/GNOME/glib-networking.git
cd glib-networking
mkdir build
cd build
env PKG_CONFIG_PATH=$OPENSSLNTRU_PREFIX/lib/pkgconfig CPATH=$OPENSSLNTRU_PREFIX/include LIBRARY_PATH=$OPENSSLNTRU_PREFIX/lib meson --prefix=$OPENSSLNTRU_PREFIX -Dopenssl=enabled -Dgnutls=disabled ..
env CPATH=$OPENSSLNTRU_PREFIX/include ninja
ninja install
cd
wget https://opensslntru.cr.yp.to/tls_timer-20210608.tar.gz
tar xf tls_timer-20210608.tar.gz
cd tls_timer-20210608
env PKG_CONFIG_PATH=$OPENSSLNTRU_PREFIX/lib/pkgconfig make PREFIX=$OPENSSLNTRU_PREFIX all install
cd
(
echo 'openssl_conf = openssl_init'
echo ''
echo '[openssl_init]'
echo 'ssl_conf = ssl_sect'
echo 'engines = engine_section'
echo ''
echo '[ssl_sect]'
echo 'system_default = system_default_sect'
echo ''
echo '[system_default_sect]'
echo 'Groups = SNTRUP761:SNTRUP857:X25519:P-256'
echo ''
echo '[engine_section]'
echo 'engntru = engntru_section'
echo ''
echo '[engntru_section]'
echo 'engine_id = engntru'
echo 'default_algorithms = ALL'
echo 'init = 1'
) > openssl-engntru.cnf
export OPENSSL_CONF=$HOME/openssl-engntru.cnf
export LD_LIBRARY_PATH=$OPENSSLNTRU_PREFIX/lib
export GIO_MODULE_DIR=$OPENSSLNTRU_PREFIX/lib/x86_64-linux-gnu/gio/modules
export ENGNTRU_DEBUG=4 # to watch engntru activating
ln -s /etc/ssl/certs $OPENSSLNTRU_PREFIX/certs
epiphany https://test761.cr.yp.to
You should be able to browse to this demo server
(using sntrup761
or sntrup857
),
whichever other demo servers you set up above
(using sntrup761
or sntrup857
),
and other sites
(typically not using sntrup761
/sntrup857
yet).
The ENGNTRU_DEBUG=4
log information in the terminal
includes a message for each sntrup761
/sntrup857
keygen,
a message for each sntrup761
/sntrup857
dec,
and a message for each computation of a batch of 32 keys.
Alternatively on the client you can use tls_timer
to
measure the wall-clock execution time of a number of
sequential TLS connections using different TLS groups:
tls_timer -help
shows a short usage message and lists
available options.
Common usage of tls_timer
will be similar to:
tls_timer -n 1024 -connect server:12345 -groups SNTRUP761
where the first parameter sets the number of consecutive
connections to benchmark, the second parameter indicates the
address and port of the TLS terminator (stunnel
in this
demo) and the value of the -groups
option restricts the
client to only use SNTRUP761
as the group for the key
exchange.
Alternative groups of interest include SNTRUP857
, P-256
and X25519
.
The client side instructions for the demo recommend using
-DCMAKE_BUILD_TYPE=Debug
when building engntru
.
This configuration is useful to observe individual key
requests, decapsulations and batch computations setting
ENGNTRU_DEBUG=4
as detailed above.
The verbose logging information can affect measurements, so
when running tls_timer
to collect measures to be used in
comparisons we recommend at least setting ENGNTRU_DEBUG=0
.
Although our experiments seem to suggest that the
differences between Debug
and Release
builds of
engntru
have negligible effects on the collected
measurements (as engntru
is a shallow ENGINE and the
actual cryptographic operations are provided via
libsntrup761
/libsntrup857
, which are unaffected by the
build profile used for engntru
), we would further
recommend to recompile (and reinstall) engntru
on the
client side using -DCMAKE_BUILD_TYPE=Release
for fairer
comparisons.
The output of tls_timer
is formatted in JSON, and the key
names should be self-explanatory, for example:
opensslntru@client:~$ tls_timer -n 1024 -connect terminator:44443 -groups SNTRUP761
{ "connections": 1024, "wallclock_usec": 1960119, "key_type": "SNTRUP761", "key_bits": 9264 }
opensslntru@client:~$ tls_timer -n 1024 -connect terminator:44443 -groups P-256
{ "connections": 1024, "wallclock_usec": 2280968, "key_type": "P-256", "key_bits": 256 }
opensslntru@client:~$ tls_timer -n 1024 -connect terminator:44443 -groups X25519
{ "connections": 1024, "wallclock_usec": 2218708, "key_type": "X25519", "key_bits": 253 }
Notice that SNTRUP857
is available only through engntru
(which is loaded by the configuration file indicated by the
OPENSSL_CONF
environment variable), while a reference
implementation of SNTRUP761
would be included in the
libcrypto
installed following these instructions if the
corresponding optional patch was enabled.
To disable automatic loading of engntru
, use your favorite
editor to comment the line engntru = engntru_section
prepending a #
at the very beginning of the line.
These instructions should allow to use tls_timer
to
compare our implementation of SNTRUP761
against the
reference version optionally included in libcrypto
, to
SNTRUP857
, and to popular optimized implementations of
pre-quantum ECC primitives such as P-256
and X25519
.
Notice that tls_timer
measures full end-to-end TLS
connections, including a new key for each connection on the
client side, encapsulation on the server side, decapsulation
on the client side, validation against a traditional
pre-quantum certificate, and communication over the network.
An important caveat in setting up experiments and
interpreting the data is that fast local network can mask
most of the communication costs. See the full paper for more
details.
Version: This is version 2021.12.14 of the "Demo" web page.