Pwu Wireguard Thesis Final
Pwu Wireguard Thesis Final
Pwu Wireguard Thesis Final
Peter Wu
supervised by
Prof. Dr. Tanja Lange
Jacob Appelbaum
Jason A. Donenfeld
i
Acknowledgements
This Master’s thesis would not have been possible if Jason A. Donenfeld did
not come up with this neat protocol in the first place. I thank him for his
relentless work on building and improving the protocol and implementations,
and the helpful discussions.
I would also like to thank my supervisor, Tanja Lange, who was very
enthusiastic to my thesis proposal. She is a very kind person and goes in great
lengths to support me, connecting me to others, providing great feedback
with eye for detail, and answering communications even at times where most
people would be asleep.
Thanks for Jerry den Hartog for being on my committee.
Thanks to my advisor, Jacob Appelbaum, for providing continuous feed-
back and involvement in insightful discussions that resulted in the
AFRICACRYPT 2019 paper.
Thanks to Daniel J. Bernstein for helping me understand ChaCha20
somewhat better by drawing it on paper.
Thanks to Vlad Krasnov for pointing out the potential amplification issue
with transport messages.
Finally lots of thanks to my parents for their unconditional love, tasty
and healthy food, and support throughout my life. Thanks to my siblings,
family and friends for their support, distractions and fun. ^ ¨
ii
Contents
1 Introduction 1
2 WireGuard protocol 6
2.1 Protocol overview . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Noise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.1 Execution of the IKpsk2 handshake pattern . . . . . . 13
2.3 Protocol messages . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.1 Handshake messages . . . . . . . . . . . . . . . . . . . 21
2.3.2 Cookie Reply message . . . . . . . . . . . . . . . . . . 26
2.3.3 Data message . . . . . . . . . . . . . . . . . . . . . . . 27
2.4 Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.5 Key rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.6 Roaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.7 Handshake state machine . . . . . . . . . . . . . . . . . . . . . 34
3 Related work 38
4 Software 40
4.1 Wireshark Lua dissector . . . . . . . . . . . . . . . . . . . . . 40
4.1.1 Key extraction . . . . . . . . . . . . . . . . . . . . . . 42
4.2 Wireshark C dissector . . . . . . . . . . . . . . . . . . . . . . 42
4.2.1 X25519 implementation . . . . . . . . . . . . . . . . . 44
4.2.2 Wireshark dissector features . . . . . . . . . . . . . . . 46
4.3 Low-level prototyping tool for WireGuard . . . . . . . . . . . 48
iii
5.1.2 Validation using a token challenge . . . . . . . . . . . . 55
5.1.3 Validation using a token challenge (variant 2) . . . . . 57
5.2 Handshake initiation collision . . . . . . . . . . . . . . . . . . 58
5.2.1 Impact . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.3 Identity hiding . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5.4 Sender and Receiver indices . . . . . . . . . . . . . . . . . . . 64
7 Conclusion 72
iv
Chapter 1
Introduction
In the current age, people take access to the Internet for granted; it is con-
sidered a commodity item. People access their email, look up information
from websites or retrieve documents from a shared network folder. In the
background, operating system updates are retrieved, shared folders are syn-
chronized, and new social media posts are loaded.
All of these actions can be performed from various devices, including
laptops, smartphones and tablets running various operating systems. These
typically use Wi-Fi or cellular networks for connectivity.
With public Wi-Fi networks eavesdropping is trivial, allowing a shady
person or company to track one’s activity and alter information. Even Inter-
net Service Providers (ISPs) are not always trustworthy, they might decide
to inject HTTP headers to facilitate user tracking [36], inject advertisements,
or block access to services.
To protect themselves against such untrustworthy actors, users can set
up a Virtual Private Network (VPN). The user runs a client on their devices
and connects to some remote server and the VPN protocol ensures that the
link between these two nodes are protected. IPsec and OpenVPN are two
well-known, older, established protocols for this purpose, but are understood
to be complex and easily misconfigured such that they fail to protect the
user. For example, OpenVPN provides many different parameters to control
its security settings, and these may have known vulnerabilities [8].
It is worth noting that the term private network originates from Internet
Protocol (IP) addressing standards. It describes a range of IP addresses
that cannot be publicly routed over the Internet and contrasts with public
network. These private addresses are typically used in residential and office
1
networks. VPNs were originally used to enable users to access services in
their private network from remote sites. Nowadays, commercial providers
have tried to redefine private as privacy, including concealment of the user’s
physical location and anonymity. This for example permits geographical
restrictions to be bypassed, enabling people to watch videos which would
otherwise have been locked to a single country. This differs from the original
use case for VPNs, in this example the VPN provider is not used to access
private network hosts, but merely as an intermediate to access the public
Internet. Nevertheless, both examples do depend on one common piece that
is being offered by VPNs: a secure pipe between the user and the VPN
provider.
A pipe can be constructed in many ways, but ideally one would like a
solid pipe which is not leaky or quickly broken under some pressure. This is
best done by design by not including features that make the pipe bulky and
prone to breakage, and instead select quality components that others have
vetted.
WireGuard [23] is a new VPN protocol that fits the role of this new pipe
and it looks quite promising. Note that WireGuard was originally presented
at NDSS 2017 [15], but while the main concepts still apply, the protocol has
slightly evolved in an incompatible way. The latest version is described in
the WireGuard whitepaper [18].
WireGuard is designed using modern cryptography, aims for high per-
formance and reduces the attack surface as a simple protocol. Unlike other
protocols, no form of negotiation over cryptographic parameters is possible.
Instead, it uses the following constructions and algorithms:
2
ments regarding key validation. It is used in the key agreement proto-
col.
Apart from these primary implementations, there are also some toy and
other third-party user space implementations, including:
3
• wireguard-rs [22], a toy implementation written in the Rust program-
ming language. It uses a generic software library that implements the
Noise protocol.
4
During the thesis, we wrote a related peer-reviewed paper with Appel-
baum and Martindale which was accepted to AFRICACRYPT 2019 and de-
scribes a transitional post-quantum security improvement (Chapter 6). This
description and previous specification issues have been communicated back
to the designer of the WireGuard protocol. Our key extraction approach
has been adopted for the handshake keys extraction tool in the WireGuard
project. The Wireshark modifications have also been contributed and ac-
cepted by the Wireshark project. Our related pcapng format changes to
store WireGuard keys have also been contributed to the official pcapng spec-
ification.
This thesis is organized as follows. Chapter 2 provides an extensive de-
scription of the protocol. Chapter 3 describes related work in the area of
analysis. Chapter 4 describes the software we developed in order to sup-
port protocol analysis. Chapter 5 describes weaknesses in WireGuard based
on our analysis and suggests some countermeasures. Chapter 6 discusses
an improvement that ensures transitional post-quantum security. Finally,
Chapter 7 summarizes our findings and our conclusions.
5
Chapter 2
WireGuard protocol
6
rotation. Peers may change roles over time, after some inactivity from both
sides, a responder might become an initiator of a new session and its peer
will subsequently assume the role of a responder.
Initiator Responder
Handshake Initiation
Handshake Response
Transport Data
Transport Data
Handshake Initiation
Handshake Response
Transport Data
A single UDP source port is used for both the handshake and data chan-
nel. This port number is the same regardless of the initiator or responder
role, and simplifies network address translation (NAT) traversal.
Endpoints are identified by a static 32-byte Curve25519 public key and
will never respond to messages unless the sender proves knowledge of this
public key. Adversaries can therefore not probe arbitrary systems for Wire-
Guard using port scanning without knowing the long-term static public keys
of the endpoints. However, this also makes troubleshooting of configuration
issues more difficult due to the lack of feedback from the protocol itself.
A 1.5 round-trip time (1.5-RTT) handshake ensures fast session establish-
ment. Once the initiator sends its initial handshake message and receives a
handshake response, it may immediately transmit encrypted data messages.
To protect against replay attacks, the responder may not send encrypted
data messages until it has received an encrypted data message from the ini-
tiator that acknowledges receipt of the response handshake message. Hence
this is considered a 1.5-RTT handshake protocol.
Both parties generate fresh ephemeral Curve25519 key pairs during the
handshake. The corresponding private keys are erased after the handshake
and thereby ensure a forward secret session. This session has a fixed lifetime
7
and a limit on the number of data messages that can be sent. Endpoints
that approach the session expiration time or message limit must switch to
new session keys if they wish to continue communication. Frequent, periodic
key rotation ensures limited impact in the event of session key compromise
and is done by starting a new handshake.
The protocol does not have an explicit shutdown signal, it only removes
sessions after a fixed duration. Even if one side has removed its VPN tunnel,
the peer would not be aware of this and continue forwarding data.
A pre-shared symmetric key (PSK) can optionally be configured between
two parties. This provides post-quantum security as long as the PSK remains
secret. A passive adversary [31] that records all network traffic today and
learns the PSK later will be able to decrypt this traffic in the future using a
quantum computer. If the PSK is never revealed, then this traffic cannot be
decrypted, even to an attacker with a quantum computer.
To protect endpoints against denial-of-service attacks that trigger ex-
pensive handshake calculations, handshake messages are authenticated by
a keyed BLAKE2s [50] message authentication code (MAC). The primary
protection mechanism (MAC1 ) uses the receiver’s static public key as MAC
key. Handshake messages that fail this check will be ignored and silently
dropped. The secondary mechanism (MAC2 ) uses a cookie as MAC key and
is only enabled when a receiver is considered “under load”. If “under load”,
the receiver will reject handshake messages and respond with an encrypted
cookie that can only be decrypted by the original sender. This cookie can
subsequently be used to authenticate new handshake messages.
WireGuard does not have additional retransmission logic for IP packets
that are transmitted in data messages. If an encrypted transport data mes-
sage is lost, then higher protocol layers within the tunnel are assumed to
react accordingly. The handshake is a special case, if an initiator does not
receive a timely response, it will periodically attempt a new handshake with
new ephemeral keys until a response is received, or until the “rekey” timeout
is reached.
A WireGuard network interface can be connected to multiple endpoints.
To ensure that IP traffic is routed to exactly one of those endpoints, Wire-
Guard has coined the term Cryptokey routing as a concept for this policy.
An endpoint is associated with a set of allowed IP addresses, possibly cov-
ering all IPv4 or IPv6 addresses. Local traffic directed to the WireGuard
network interface will be sent to an endpoint only if the destination IP ad-
dress is within its set of allowed IP addresses. Inbound traffic is only accepted
8
if the source IP address of the decrypted IP packet is within the set of allowed
IP addresses from the authenticated peer.
Do note that the operating system must still be properly configured to
route traffic to the WireGuard network interface. Failure to do so could
result in leaking network traffic over unprotected network links. These net-
work routes are expected to cover the same addresses as described by the
Cryptokey routing table, but are not necessarily the same. If the Cryptokey
routing table does not cover an address, then traffic passed from the operat-
ing system to WireGuard will be dropped. IP packets originating from the
local host will be answered with an ICMP “no route to host” error.
This mechanism can be used to implement a VPN configuration where a
“client” routes all of its traffic to a “server”, and where this “server” would
only forward traffic to the “client” if the destination IP address matches. In
WireGuard implementations, as opposed to the wire protocol, this can be
achieved via the per-peer Allowed IPs setting. On the client, the setting for
the server would be set to 0.0.0.0/0 which is the equivalent of accepting all
traffic. On the server, the setting for the client could be set to 192.0.2.2/32
for example.
WireGuard’s design choices make the protocol and its implementations
more resilient to attacks that have traditionally been possible with other
VPN protocols:
• Use of modern cipher constructions eliminates attacks such as Ble-
ichenbacher (RSA encryption in TLS), Sweet32 (Birthday attacks on
64-bit block ciphers in TLS and OpenVPN) [8], and padding oracle
attacks like BEAST (AES-CBC in TLS). Similarly, use of an AEAD
construction avoids known problematic constructions such as MAC-
then-Encrypt as used in older TLS versions and IPsec [13].
• Lack of cryptographic agility [5] due to the use of fixed key exchange
and cipher algorithms eliminates downgrade attacks to insecure config-
urations that resulted in issues such as POODLE and FREAK in TLS.
If algorithms have to be updated, a new protocol implementation has
to be deployed. New algorithm names will also completely change the
derived handshake and session keys, and will thus not be vulnerable to
cross-protocol version attacks.
9
prevents issues such as the TLS Triple Handshake attack [9].
• Use of fixed message sizes during the handshake reduces the risk of
vulnerabilities that result from complex message parsing requirements.
This prevents issues such as Heartbleed, and parser bugs in ASN.1 or
X.509 implementations.
2.2 Noise
Noise [47] is a framework for protocols based on the Diffie-Hellman (DH) key
exchange. Protocol designers can use this to build new protocols with prop-
erties such as mutual authentication, identity hiding, and forward secrecy for
derived shared symmetric keys. Instantiations of these protocols have been
formally analyzed and verified [37]. While not all Noise protocols guarantee
these security properties, the IKpsk2 protocol variant selected by WireGuard
does provide them. The remainder of this section provides a general overview
of Noise and is not specific to WireGuard.
A protocol always starts with an exchange of handshake messages
between two parties which are used to derive symmetric keys to encrypt
transport messages. The initial sender is called the initiator while the
intended recipient is referred to as the responder.
Each party always creates a fresh ephemeral key pair for every new
handshake, the corresponding public key is sent to the peer. In protocols that
require authentication, each party additionally possesses a long-term static
key pair to identify themselves. The static public keys may optionally be
published in handshake messages in case a party has no prior knowledge
about the peer identity. These ephemeral and static key pairs are used in a
series of Diffie-Hellman computations to ensure forward secrecy. To ensure
a cryptographic binding of derived secrets to the handshake, the symmet-
ric keys for handshake and transport data encryption are derived using all
handshake messages and key material accumulated so far.
Protocols can also mix a 256-bit pre-shared symmetric key into the
handshake. This can be used to provide channel binding between the Noise
session and an earlier protocol-specific conversation. For example, a hybrid
10
post-quantum key exchange could be executed prior to the Noise handshake
and the output of this prior key exchange could subsequently be transformed
into a PSK. For post-quantum security, the PSK could also be fixed be-
tween different handshakes as long as the PSK is eventually forgotten before
quantum computers become practical.
Each handshake message is described by a message pattern. Such a
message pattern contains a sequence of predefined tokens that describe the
required changes to the cipher and handshake state, and data to read or write.
The cipher state consists of a key and nonce for encryption, a handshake hash,
and a chaining key from which the cipher key is derived. The handshake state
stores the local key pairs and the peer’s ephemeral and static public keys.
The current set of predefined tokens in message patterns for the Noise
framework are "e", "s", "ee", "es", "se", "ss", and "psk". The meaning
of these tokens is as follows:
• "e": The sender generates a new ephemeral key pair, writes the public
key to the output buffer and hashes the public key into the handshake
hash state. In a handshake involving a PSK, the ephemeral public key
is additionally mixed in the chaining key. See the description for the
"psk" token for the motivation on this requirement.
• "s": The sender writes its long-term static public key to the output
buffer in encrypted form if a symmetric key has been set, and in plain-
text otherwise. This output is also hashed into the handshake hash
state.
• "psk": A pre-shared symmetric key is hashed into the chaining key and
handshake hash state. Since a symmetric key is directly derived from
a chaining key, a nonce must be mixed into the chaining key to ensure
that a randomized symmetric encryption key is derived. This prevents
fatal key reuse. The ephemeral public key published by the "e" token
acts as such a nonce. In valid Noise protocols using the "psk" token,
encryption must therefore be preceded by the "e" token. For example,
11
"psk, s, e" would be illegal since "s" would publish the encrypted
static public key using a fixed symmetric key derived from the PSK.
Without knowing the exact public keys from the pre-message patterns,
one will not be able to derive the correct symmetric keys during the hand-
shake and as a result the handshake will fail due to out of sync handshake
states.
12
Noise defines a number of standard handshake patterns with various re-
quirements, properties and security guarantees. User data can be sent after
a handshake has completed, but they could also be included right after the
output buffer for a message pattern during the handshake. However, sending
such data before the handshake has completed can result in weaker authen-
ticity and confidentiality guarantees.
A concrete Noise protocol is instantiated by selecting a handshake pat-
tern, a DH function, a cipher function and a hash function. A pro-
logue byte pattern can be set to bind the cryptographic keys to this value.
WireGuard is instantiated as follows:
handshake pattern IKpsk2
DH function X25519
cipher function ChaCha20-Poly1305
hash function BLAKE2s
prologue WireGuard v1 zx2c4 [email protected] (34 bytes)
The name of the IKpsk2 handshake pattern is decomposed as:
• I: static public key for the initiator is immediately transmitted to the
responder, despite reduced or absent identity hiding.
• K: static public key for the responder is known to the initiator.
• psk2: a PSK is used at the end of the second handshake message.
The Noise protocol name is the concatenation of the string Noise, the
handshake pattern, and functions, separated by an underscore ( ). For Wire-
Guard, this results in: Noise IKpsk2 25519 ChaChaPoly BLAKE2s.
A description of this specific IKpsk2 pattern will be presented in Sec-
tion 2.2.1. Finally note that not all potential handshake patterns are valid
in Noise, additional validity rules may apply [47, Section 7.3].
13
• Sipub : a 32-byte static public key of the initiator.
14
• Hash(data): the BLAKE2s [50] hash function, takes arbitrary length
data and returns a 32-byte output.
• HKDFn (salt, input keying material): uses the HKDF construction [40]
with unkeyed BLAKE2s [50] as hash function for the HMAC function
to derive n keys, each of length 32 bytes.
PRK = HMAC-Hash(salt, input keying material)
T1 = HMAC-Hash(PRK, 1) . The constant 0x01 is a single byte.
T2 = HMAC-Hash(PRK, T1 ||2)
...
Tn = HMAC-Hash(PRK, Tn−1 ||n)
Return (T1 , . . . , Tn )
Figure 2.2 presents the IKpsk2 handshake pattern in a concise notation which
Noise uses to summarize handshake patterns. An elaboration of this pattern
including the precise calculations will follow below.
IKpsk2:
0. <- s
...
1. -> e, es, s, ss
2. <- e, ee, se, psk
Figure 2.2: Summary of IKpsk2 handshake pattern from Noise [47]. The
dots separate pre-messages from messages.
The handshake computations start with hashing the protocol name and pro-
logue:
1: h0 = Hash("Noise IKpsk2 25519 ChaChaPoly BLAKE2s")
2: ck0 = h0
3: h1 = Hash(h0 ||"WireGuard v1 zx2c4 [email protected]")
15
4: h2 = Hash(h1 ||Srpub )
16
• "e": the responder generates an ephemeral key pair and sends
ephemeral public key Erpub to the initiator. Erpub is mixed into the
handshake hash, and the chaining key due to the use of a PSK.
14: R: (Erpriv , Erpub ) = DHGen; publish Erpub
I: Read Erpub
15: h6 = Hash(h5 ||Erpub )
16: ck4 = HKDF1 (ck3 , Erpub )
• "ee": the responder computes ee = DH(Erpriv , Eipub ). The initiator
computes the same secret using ee = DH(Eipriv , Erpub ).
pub
17: R: ck5 = HKDF1 (ck4 , DH(Erpriv , Ei ))
I: ck5 = HKDF1 (ck4 , DH(Eipriv , Erpub ))
• "se": the responder computes se = DH(Erpriv , Sipub ). The initiator
computes the same secret using se = DH(Sipriv , Erpub ).
pub
18: R: ck6 = HKDF1 (ck5 , DH(Erpriv , Si ))
I: ck6 = HKDF1 (ck5 , DH(Sipriv , Erpub ))
• "psk": incorporate the pre-shared symmetric key Q in both the
chaining key and the handshake hash.
19: (ck7 , τ, k2 ) = HKDF3 (ck6 , Q)
20: h7 = Hash(h6 ||τ )
• Append an encrypted (possibly empty) payload to the handshake
message buffer. If the responder fails to decrypt the result, it
aborts the handshake. For WireGuard, the payload is always
empty, any other value is treated as failure.
21: R: enc-empty = aead-enc(k2 , 0, empty, h7 ); publish enc-empty
I: empty = aead-dec(k2 , 0, enc-empty, h7 ); abort on failure
22: h8 = Hash(h7 ||enc-empty)
17
• 32 bytes for Eipub , see Line 5.
• 48 bytes for the encrypted Sipub , 32 bytes for the ciphertext, 16
bytes for the authentication tag. See Line 9.
• x + 16 bytes for the encrypted first payload, x bytes for the ci-
phertext, 16 bytes for the authentication tag. See Line 12. In case
of WireGuard x = 12 which implies a field size of 28 bytes.
2. Responder to initiator:
18
Type Message Name Length Message format
1 Handshake Initiation 148 bytes See Figure 2.5, Section 2.3.1
2 Handshake Response 92 bytes See Figure 2.6, Section 2.3.1
3 Cookie Reply 64 bytes See Figure 2.7, Section 2.3.2
4 Transport Data ≥ 32 bytes See Figure 2.8, Section 2.3.3
Table 2.1: Summary of message types. In the linked figures, the numbers on
the left indicate the starting byte offset for the first column in that row.
Initiator Responder
Handshake Initiation
Transport Data
19
Recall that handshake initiation and response messages have a MAC2
field which uses a cookie as MAC key. The Cookie Reply message contains an
encrypted cookie. The MAC2 field is usually ignored, unless a party is “under
load”. When either party is “under load”, the handshake will become more
complicated and take more time due to additional round trips and timer-
based delays. The “under load” condition is not precisely specified in the
whitepaper, and appears to be implementation defined. For the current Linux
kernel implementation, it means that the incoming queue for unprocessed
handshake messages contains at least 4096 8
= 512 items.1
Consider a responder who is under load (Figure 2.4a). After the initiator
sends its Handshake Initiation message, the responder issues a Cookie Reply.
The initiator decrypts the cookie and remembers it, but will not immediately
send another handshake message with this cookie. Instead, it has to wait for
a “rekey timeout” of five seconds to occur before the initiator starts a fresh
handshake attempt, using the cookie as MAC key for the MAC2 field. This
timeout will be described in more detail in Section 2.4.
A similar situation occurs when the initiator itself is “under load” (Fig-
ure 2.4b). After it receives a Handshake Response from the responder, the
initiator could send a Cookie Reply instead of completing the handshake.
When the responder receives this Cookie Reply, it will store the decrypted
cookie. The initial handshake attempt remains unaffected. After a timeout,
the Initiator will retry a handshake, this time it succeeds since the responder
knows the appropriate cookie.
1
The macro MAX QUEUED INCOMING HANDSHAKES is defined as 4096 in the messages.h
header file. This number is divided by 8 in receive.c in the “under load” check.
20
Initiator Responder
Figure 2.4: Protocol flows where one party is under load and triggers a Cookie
Reply. After the Cookie Reply and a delay, a new handshake is started.
An unfortunate situation could occur when both parties are under load.
In that case, the first Handshake Initiation message triggers a Cookie Reply
which will not complete the handshake. After the initial timeout, a new
Handshake Initiation message with a valid MAC2 field is sent which will
trigger a Handshake Response message without a valid MAC2 field. When
the initiator receives this, it will send a Cookie Reply to the responder. The
responder records this value, but won’t reply with a handshake message.
Only after the second timeout, both parties will have an appropriate cookie
for the MAC2 field to continue the handshake. However, cookies have a
limited lifetime of at most two minutes. In case the cookies have changed,
then the above scenario could be repeated once more. If packets are lost,
then this could take even more time as new handshake initiations can only
be attempted every five seconds.
21
encapsulate handshake messages from the Noise IKpsk2 pattern which was
described earlier in Section 2.2.1. This section will additionally define validity
requirements for the encrypted handshake payloads.
Both handshake messages include a MAC1 and MAC2 field which use the
keyed BLAKE2s MAC [50] with a 128-bit hash value as output. The input
data and key depends on the recipient and field type:
pub
MAC1 Let Sm 0 be the static public key of the receiver. The 256-bit MAC1
The data starts at the Type field and ends before the MAC1 field.
MAC2 If the most recent cookie was received from the peer within 120
seconds, then it will be used as 128-bit MAC2 key. If no such cookie
has been received, or if it is too old, then the MAC2 field will be set to
zeroes. The data starts at the Type field and ends before the MAC2
field.
The initiator creates a Noise handshake message as described in Sec-
tion 2.2.1 (message from initiator to responder) with the following parame-
ters:
• The initiator static key pair Sipriv , Sipub .
• The responder static public key Srpub .
• A pre-shared symmetric key Q.
• The initial message payload: a 12-byte timestamp. The WireGuard
paper recommends using the current time, encoded in the external
TAI64N timestamp format [3] which has nanosecond precision. How-
ever, to avoid being used as time oracle in side-channel attacks, im-
plementations should reduce the accuracy of the nanoseconds part by
clearing some of the least significant bits. For example, the Linux kernel
and Go implementations remove the 24 least significant bits, resulting
in a precision of about 16.7 milliseconds.2 Implementations can also
2
The Linux kernel implementation limits the number of handshake initiations to 50 per
second per peer as identified by their static public key. This implies that one handshake
initiation is permitted every 20 milliseconds or 224 + . . . nanoseconds. Thus by truncating
the last 24 bits, all valid handshake initiations will still contain a valid, increasing value.
Note that the Go implementation has even stricter limits, even though it truncates 24
bits, it only accepts one initiation every 50 milliseconds.
22
choose to send any other monotonically increasing 96-bit unsigned in-
teger value encoded as big-endian, it does not have to be a timestamp.
The Noise outputs (Eipub , encrypted Sipub , encrypted timestamp) are in-
cluded in the Handshake Initiation message (Figure 2.5). A new randomly-
generated local identifier is included as Sender index, the MAC1 and (if
necessary) MAC2 fields are populated and the message is ready to be de-
livered to the responder.
0 1 2 3 4 5 6 7
0
Type: 1 Reserved (3 bytes) Sender index (4 bytes)
8
40
88
120
MAC1 (16 bytes)
128
136
MAC2 (16 bytes)
144
152
23
• On success, continue normal processing.
3. Process the Noise handshake message from the initiator to the respon-
der as described in Section 2.2.1. Abort on decryption failure or if the
decrypted payload, representing the timestamp, is not exactly 12 bytes.
As an optimization, the Linux kernel implementation and wireguard-go
perform the next two checks while processing decrypted fields and abort
earlier on failure. However, implementations using a generic Noise li-
brary, such as wireguard-rs, will not be able to apply this optimization.
24
is copied to the Receiver index field. Finally the MAC1 and, if necessary,
MAC2 fields are populated as described before.
0 1 2 3 4 5 6 7
0
Type: 2 Reserved (3 bytes) Sender index (4 bytes)
8
Receiver index (4 bytes)
16
48
Encrypted empty (16 bytes)
56
64
MAC1 (16 bytes)
72
80
MAC2 (16 bytes)
88
96
3. Look up the local handshake state matching the receiver index. Abort
if not found or if the handshake has already been completed.
At this point, the initiator has all necessary information to derive trans-
port keys and complete the cryptographic handshake by sending an encrypted
transport message to the responder.
25
2.3.2 Cookie Reply message
The MAC2 field described in Section 2.3.1 is only set if a recent cookie
(not older than 120 seconds) is available. This cookie is transmitted in a
Cookie Reply message (Figure 2.7). The Cookie Reply is sent in response
to a handshake message when under load. It is smaller than the original
handshake messages to avoid assisting in amplification attacks as will be
explained in Section 5.1.
0 1 2 3 4 5 6 7
0
Type: 3 Reserved (3 bytes) Receiver index (4 bytes)
8
64
The IP address is 32 bits for IPv4 and 128 bits for IPv6. The 16-bit port
number is encoded in network byte order.
To avoid leaking the cookie in cleartext, it is encrypted using a symmetric
key based on the unkeyed BLAKE2s hash over the concatenation of a 8-byte
pub
label and the local static public key Sm :
pub
cookie-key = Hash("cookie--"||Sm )
26
handshake message msg is used as additional authenticated data to protect
the source against off-path attackers who cannot observe messages and who
might try to send fraudulent cookies to disrupt the sender. 3 The encrypted
cookie is calculated as follows:
nonce ∈R {0, 1}192
encrypted-cookie = XChaCha20Poly1305(cookie-key, nonce, cookie, msg)
The Receiver index is populated with the Sender index from the original
handshake message. The Nonce and Encrypted nonce are as above.
The receiver of this message must look up the original handshake and
associated static public key using Receiver index, decrypt the cookie and
store the cookie in the handshake state. It must not immediately respond
with a handshake message as the timers described in Section 2.4 handle
retransmission of handshake messages. Future handshake messages will use
the stored cookie to populate the MAC2 field.
27
little-endian integer in the counter field. Messages may only be sent only if
send
the messages limit has not been reached yet (Nm < 264 − 24 − 1) and if the
session has not expired yet (see Chapter 2.4).
Encryption uses the same ChaCha20-Poly1305 AEAD as used for hand-
shake encryption. The transport key was established in the handshake, and
the additional authenticated data is empty:
kP k
Ppadded = P || 016·d 16 e−kP k
encrypted-packet = aead-enc(Tmsend , Nm
send
, Ppadded , )
Since UDP does not preserve order nor does it guarantee delivery, re-
ceivers of the Transport Data message must proceed carefully. They must
keep track of previously received counters and drop messages with reused
counter values.
The transport data message is variable length and at minimum 32 bytes.
The practical maximum length of tunneled IP packets over the public Internet
is 1420 bytes as will be described later in Section 2.3.3.
Congestion control prevents networks from becoming overwhelmed by
reducing packet rate. WireGuard has no form of congestion control, this is
a responsibility for upper layers. Protocols such as TCP and QUIC do rely
on the IP layer to provide an Explicit Congestion Notification (ECN) [28]
through two bits. If a transport protocol supports ECN, it will set one of
the two bits. Routers that detect congestion may then set forward packets
and set both bits to signal Congestion Encountered (CE) instead of dropping
the packet. This mechanism enables protocols to explicitly detect congestion
rather than observing it as a result of packet loss. While the wireguard-
go implementation does not support ECN, the Linux kernel implementation
does propagate the CE signal from the outer IP packet to the inner IP packet
for ingress traffic.
28
too. For example, Linux and FreeBSD do not support IPv6 Jumbograms [27,
29].
Table 2.2: Theoretical maximum sizes for UDP datagram payloads without
the IP or the 8-byte UDP headers, based on transport header field limits [1].
Aside from MTU restrictions on the directly attached network link, ad-
ditional limits may apply along the path between two peers. This is called
the path maximum transmission unit (PMTU). One of the reasons for a
lower PMTU are tunneling protocols that add extra protocol headers such
as WireGuard.
As for practical minimum message lengths on the Internet, IPv6 requires
a minimum link MTU of 1280 bytes without Path MTU Discovery (PM-
TUD) [12, Section 5]. Assuming an IPv6 header without extension headers,
this implies a minimum UDP payload size of 1232 bytes. Thus, WireGuard
should usually be able to send IP packets of at least 1200 bytes due to an
overhead of 80 bytes:
• 40 bytes for an IPv6 header without extension headers (20 bytes for
IPv4 without options).
• 32 bytes for the WireGuard message header: 4 bytes for the type and
a reserved field, 4 bytes for the receiver index, 8 bytes for the message
counter, and finally 16 bytes for the authentication tag.
29
packets to fit the measured path MTU” [12]. However as many links on the
public Internet use Ethernet or similar, a MTU 1500 is generally safe [44]
which implies a MTU of 1420 bytes inside a WireGuard tunnel.
Selection of an appropriate MTU is important, a too low value would incur
more per-packet overhead and might reduce throughput. An overly high
value would prevent packets from being delivered across some links or cause
unpredictable behavior due to IP fragmentation. Neither the WireGuard
protocol nor the Linux kernel implementation have a built-in mechanism to
automatically configure an appropriate MTU. This is a manual process, but
scripts such as wg-quick do select a default MTU based on the link MTU.
2.4 Timers
The WireGuard protocol operation is affected by several time-related con-
straints. The involved constants are summarized below:
Symbol Value
Rekey-After-Messages 264 − 216 − 1 messages
Reject-After-Messages 264 − 24 − 1 messages
Rekey-After-Time 120 seconds
Reject-After-Time 180 seconds
Rekey-Attempt-Time 90 seconds
Rekey-Timeout 5 seconds
Keepalive-Timeout 10 seconds
2. A handshake initiation which has not been matched with a valid hand-
shake response will be retried with fresh ephemeral keys until Rekey-
Attempt-Time has passed. This timer will be reset when new transport
data is added to the outbound queue. After that, the handshake is
considered failed and the outbound queue is flushed.
30
3. The age of a secure session is measured from the moment when the
handshake has completed. For the initiator, this means when the hand-
shake response has been processed and transport keys have been de-
rived. For the responder, it means when the first valid transport data
message from the initiator has been received.
4. Timer expiration on its own must not trigger a new handshake initia-
tion, only transport data can trigger it. This ensures that a tunnel is
not unnecessarily kept alive.
Only the initiator may trigger rekeying while a session is valid to avoid a
“thundering herd” [18] problem where the both the initiator and responder
try to start a new session at the same time.
In order to quickly identify whether a tunnel is still alive, WireGuard
implements a Passive Keepalive mechanism:
31
Otherwise, if the most recently sent two messages were Keepalives, no
passive keepalive message should be sent.
2. If a transport data or keepalive message was sent, but no data has been
received in the past Keepalive-Timeout + Rekey-Timeout, consider the
link dead and start a new handshake initiation. The session itself is
not yet destroyed, WireGuard session keys are only destroyed after a
fixed time to permit decryption of out-of-order packets.
Aside from the passive keepalive mechanism, the Linux kernel and wireguard-
go implementations can also be configured to send a Persistent Keepalive.
This will result in a Keepalive message whenever a tunnel has been idle for
a configurable amount of time.
Network Address Translation (NAT) is a mechanism where routers be-
tween a private and public network change addresses of IP packets. As a
single public IP address is typically shared between the many private IP ad-
dresses, the UDP source port will also change to avoid collisions. As router
memory is constrained, these mappings are typically only maintained for a
limited time. WireGuard’s keepalive mechanism is intended to keep NAT
sessions active to avoid NAT rebinding, a phenomenon where a host behind
a NAT router becomes unreachable from the outside, and where this host
changes the source port once it start transmitting data again. We observe
that while this setting can be configured in the Linux kernel to 65535 seconds
(the maximum of a 16-bit integer), it will not keep the tunnel alive in this
full time period. Since the maximum session age is 180 seconds, any higher
persistent keepalive setting will result in a window where the tunnel is not
active.
32
At any point in time, at most three session key slots are available, the
“previous”, “current”, and “next” session. Initially no session exists between
two peers and thus all slots are empty. Outbound transport data messages
will always be encrypted with keys from the “current” key slot. Inbound
transport data messages can be decrypted with keys from either the “current”
or “previous” slot as identified by the receiver index.
When an initiator sends a handshake response, it has completed the hand-
shake and can derive session keys which will be stored in “current”. The pre-
vious recent session (either “next” or “current”) will be moved to “previous”
and “next” will be emptied. Note that these slots store session keys and not
incomplete sessions which are instantiated by handshake initiations.
When a responder receives a handshake response, it still needs to wait for
a confirmation even if it can derive session keys. These keys will be stored in
“next”. Once a handshake has been confirmed by receipt of the first transport
data message, slots rotate: the “next” session moves to “current”, and the
“current” slot moves to the “previous” slot. If the “previous” slot was non-
empty before rotation, it must be properly destroyed. Likewise, sessions that
reach Reject-After-Time must be removed from its slot and destroyed.
In the unfortunate event that two peers decide to start a handshake with
each other at exactly the same time, this behavior is not defined in the current
whitepaper. In the current Linux kernel implementation, each peer has a
single handshake state. When one side begins an initiation, it initializes this
handshake state for the initiator role and only accepts a responder message.
However, when it receives a valid handshake initiation message from the
peer, it will unconditionally overwrite its own pending handshake initiation
state with a new state based on the received handshake initiation. If the
same happens to the other side, then both peers end up with an unusable
handshake state which cannot be completed since they lack their own original
handshake initiation state. We will explore this scenario in Section 5.2.
2.6 Roaming
The external source IP address is used by WireGuard to identify the end-
point address to which messages should be sent. This address can change
for various reasons, including moving between Wi-Fi and mobile networks.
WireGuard supports maintaining an active session even under these condi-
tions. It does so by updating the endpoint address of the peer from which
33
the change was observed. It does not restart the handshake after roaming,
presumably because that would increase latency before data can be trans-
mitted.
The external source IP address and UDP port for the most recent au-
thenticated handshake or transport message is always used by the receiver to
determine the destination for future responses. At least the Linux kernel im-
plementation always uses the most recently seen source address, disregarding
the counter in transport data messages for example. Changes to the source
address do not have other side-effects such as triggering a new handshake.
We observe that cookie reply messages are not consistently handled like
other messages. The source address of cookie replies are ignored, at least in
the Linux kernel implementation. Peers that receive a cookie reply should try
to avoid updating their own source address for the next handshake message
as the cookie within the cookie reply is bound to the source IP address of
the original message.
While the current mechanism to support roaming is fast, it does contain
a denial-of-service vulnerability which will be described later in Section 5.1.
34
ISent The handshake initiation message was previously sent to the peer, so
the local party assumes the initiator role.
RSent A handshake response message has been sent to the initiator and is
now pending confirmation from the initiator.
RRcvd The local party is the initiator and receives a handshake response
message.
Alive The handshake process is complete, transport data may be sent and
received.
Dead The terminal state where the session has exceeded its maximum age
and can be destroyed.
Transitions are generally channel-related events, recv Init means that a
handshake initiation message was received while send Resp means that a
handshake response message is sent. The expires transition is a time-related
event and indicates that the completed handshake state is older than Reject-
After-Time.
In this state machine, we assume that the messages are properly formed.
In particular, we assume that counters do not increase past Reject-After-
Messages, AEAD decryption passes, MAC1 and MAC2 are valid, the hand-
shake initiation timestamp has been validated, and so on. Since cookie reply
messages do not directly affect the handshake state, and only affect the ses-
sion state, cookie replies are not included as transition either.
Data messages are omitted from earlier states as decryption is not possible
without a completed handshake. In those cases, implementations could either
discard them or buffer messages if they would like to account for out-of-order
packets. The implementation is assumed to be valid, so we omit transitions
that send messages from unexpected states.
Obviously the handshake response transitions are only possible if their
receiver index matches the sender index of the original handshake initiation.
Otherwise a receiver would not be able to find a matching handshake state
and the handshake response message would be discarded with no transitions.
Likewise for data messages, a receiver would not be able to find matching
session keys and thus these messages would be dropped.
35
start Idle recv Resp
Figure 2.9: Handshake state machine. Dashed transitions are illegal, but
implementations must properly handle them by discarding the messages.
Red transitions are not defined in the whitepaper, but exist in the Linux and
Go implementations.
36
Data transition exists in the Response Sent (RSent) state. Likewise, a Re-
ceive Data (recv Data) transition is forbidden from the Response Received
(RRcvd ) state.
Finally, when the session is alive for Reject-After-Time, it must no longer
be used (it enters the Dead state). That marks the end of one handshake
state.
37
Chapter 3
Related work
Various aspects of WireGuard have previously been studied with formal anal-
ysis. In this chapter we will describe their scope and summarize their results.
Noise Explorer by Kobeissi and Bhargavan [37] enables automated sym-
bolic modelling and verification for Noise protocols. Their symbolic security
analysis is based on ProVerif and covers 57 different Noise handshake patterns
including the IKpsk2 handshake pattern used by WireGuard. Specifically for
this IKpsk2 pattern, the following security properties from the Noise specifi-
cation [47] were formally verified:
Donenfeld and Milner [25] have developed a symbolic model of the Wire-
Guard handshake protocol using Tamarin and have proven properties such as
key agreement and correctness, key secrecy, forward secrecy, session unique-
ness, and identity hiding. Identity hiding will be discussed later in Sec-
tion 5.3. Since session keys are derived from identity keys, it is not possible
to compute the same session key between different peers and therefore un-
known key-share attack resistance is also proven.
38
Donenfeld and Milner [25] also briefly describe resistance against KCI
attacks, we will elaborate on that. In WireGuard, knowledge of the initiator
static private key does not allow an attacker to impersonate any responder to
the initiator since the attacker would need either the responder static private
key or initiator ephemeral private key to derive session keys (see "es" in
Section 2.2.1). Knowledge of the responder static private key permits an
attacker to create a handshake initiation message that can impersonate any
initiator to the responder, but the attacker will not be able to compute the
actual session keys as that requires either the initiator static private key or the
responder ephemeral private key (see "se" in Section 2.2.1). Since the initial
handshake message can be crafted by an attacker, the responder must only
consider a handshake authenticated when it receives a message that proves
the initiator’s ability to derive session keys. This is done in WireGuard via
the initial transport data message from the initiator to the responder. Hence
WireGuard is secure against KCI attacks if both static private keys are not
compromised at the same time.
Dowling and Paterson [26] describe a computational proof on a modified
version of WireGuard. They were not able to prove the original WireGuard
protocol secure with respect to key indistinguishability since session keys are
no longer indistinguishable from random. To remedy this, they could either
model the handshake and data transport as a monolithic whole, or modify the
protocol to be proven. They have opted for the latter and inserted an addi-
tional message at the end of the handshake that proves authenticity without
requiring a data message that uses session keys. With this modification, they
were able to prove key indistinguishability security in their security model in
addition to properties such as perfect forward secrecy and resilience against
KCI attacks. This analysis excludes cookie messages and key rotation.
Lipp [42] presented a mechanised computational proof using CryptoVerif
covering properties such as correctness, message secrecy, forward secrecy,
mutual authentication, resistance against KCI, resistance against unknown
key-share attacks, and resistance against replay of the first protocol message.
All these related works have focused on a theoretical model of the Wire-
Guard protocol. Rather than replicating the work on symbolic and compu-
tational models, we have instead focused on developing a complete specifi-
cation of the WireGuard protocol suitable for implementers (Chapter 2). In
addition, we include an analysis on practical aspects of WireGuard imple-
mentations.
39
Chapter 4
Software
40
Figure 4.1: A screenshot of Wireshark, showing the WireGuard Lua dissector.
41
4.1.1 Key extraction
In order to extract the AEAD secrets from a Linux system, one could patch
the WireGuard source code and expose the keys. However, that could in-
crease the risk of key exposure and crashes depending on the implementation.
It would also not work on a system where the kernel module cannot be re-
placed, or where doing so requires a reboot. To enable key extraction on a
live system, we used kprobe-based event tracing [33] to dump kernel memory
when certain carefully chosen functions are entered. This mechanism makes
it possible to write one line with register and memory contents to a special
file (/sys/kernel/debug/tracing/trace pipe).
We ended up hooking into the chacha20poly1305 encrypt and
chacha20poly1305 decrypt functions in order to dump the key and AAD
for fields in handshake messages. Luckily, transport data messages do not
use these functions and thus we can prevent the log from growing fast when
there are many data messages. Recall that the symmetric keys for transport
data are derived using the HKDF function which involves multiple calls to
a HMAC function. While HKDF is called multiple times, only the final call
uses an empty input parameter. Therefore we hooked into the blake2s hmac
function and extracted the parameters passed to the HKDF function. To
identify the final call, we distinguished calls based on the input size. To link
keys to a session, we dumped the sender and receiver indexes by hooking into
the function that inserted new sessions into a hash table using the sender
index as hash table key. To match the initiator with the responder, we hook
into the handshake response processing function. Finally a post-processing
script (key-extract.py) correlates all logged information and produces a key
log file which can be fed to Wireshark to enable decryption.
42
dissector has been integrated in Wireshark 3.0 and thus Wireshark now rec-
ognizes the WireGuard protocol out-of-the-box.
Deriving symmetric keys using the Noise protocol requires static and
ephemeral private keys, and an optional PSK. We extended Wireshark such
that these keys can be provided via either a key log file as configured in
the protocol preferences, or via a decryption secrets block within the pcapng
file [59]. The original kprobe method extracted symmetric keys that required
a session identifier for linking. As asymmetric keys can be linked to a public
key, the kprobe post-processing script can be greatly simplified. The simpli-
fied script to extract keys from the Linux kernel is extract-handshakes.sh [17].
The earlier Lua dissector looks up decryption secrets based on the sender
or receiver indices. This is stateless in the sense that individual packets can
be processed independently, but it is vulnerable to receiver index collisions.
The new C dissector instead has to perform the full handshake computations
which requires linking messages to a session. A naive implementation could
use the IP addresses and port numbers of the outer IP packet, but this
information could change due to roaming. For this reason, we maintain state
to link handshake, cookie replies, and data messages to a session. Connection
tracking survives IP address changes and depends on the received message.
The dissector recognizes the following message types:
• Transport Data: look up the most recent session based on the receiver
index. If the handshake has been completed, decryption can be enabled
using the corresponding handshake secrets.
43
Actual implementations such as the Linux kernel implementation typi-
cally use the receiver index to identify the associated session. Since Wire-
shark is not a real initiator or responder, it does not know the exact session
that belongs to a receiver or sender index. As implementations could gener-
ate colliding indices, potentially due to malice, it is important that a passive
observer such as Wireshark can accurately model the session associations.
Hence it makes assumptions such as a match between the handshake re-
sponse destination address and the handshake initiation source address.
Unlike for the Lua dissector, a full implementation of the WireGuard
handshake is required in order to compute the appropriate decryption keys
for the static initiator public key, the timestamp field, the empty respon-
der confirmation field, and transport data. For this we mostly relied on
Libgcrypt [39], a general purpose cryptographic library. This includes im-
plementations for BLAKE2s, ChaCha20-Poly1305, HMAC, and HKDF via
existing glue code in Wireshark. Since X25519 is not readily available in
Libgcrypt, we had to create our own implementation, see Section 4.2.1.
Our Wireshark C dissector (packet-wireguard.c) has over 1300 SLOC.
This includes code for dissection of protocol structures, connection tracking,
an implementation of the WireGuard handshake protocol, code to parse the
key log file, an implementation of our identity hiding attack (Section 5.3),
and code to support decryption. Our X25519 glue code has 66 SLOC while
the test suite gained about 200 SLOC. The related changes can be found in
the Wireshark bug tracker [57].
44
a justification.
45
All of the above X25519 implementations have been fuzz-tested using Lib-
Fuzzer. Address Sanitizer (ASAN) was enabled to check for memory safety
issues and UBSan was enabled to catch potential undefined behavior. Only
TweetNaCl triggered UBSan issues as described above. The C standard de-
clares left shift of a signed negative integer as undefined behavior, and since
compilers could turn code affected by undefined behavior into defective pro-
grams [10], we have adopted a policy to avoid such code. This makes it easier
to reason about correctness of a program on various platforms supported by
Wireshark.
The final implementation in Wireshark has also been tested against the
reference implementation using LibFuzzer. LibFuzzer is an in-process, coverage-
guided, evolutionary fuzzing engine [48]. It calls a target function,
LLVMFuzzerTestOneInput, with random input and mutates this input in or-
der to maximize code coverage. In our target function, we partitioned the
input into a 32-byte private key and a 32-byte public key. The fuzzer ran for a
day, and we observed that all outputs from our crypto scalarmult curve25519
and crypto scalarmult curve25519 base functions produce exactly the same
output as corresponding reference implementations in Sodium. This increases
the confidence in the correctness of our X25519 implementation.
46
Figure 4.2: A screenshot of Wireshark, revealing successful decryption of the
initiator static key and the timestamp. The receiver public key was also
identified.
47
to recognize this new format. This enables decryption of WireGuard
packet captures without additional configuration.
48
ample, the decrypted timestamp field is not checked against replay which
is required for conforming implementations. This kind of freedom ensures
that a reproducible test scenario can be written to test real implementations
against edge cases. It is also a reason why wgll is not suitable for production
use, it should only be used for testing purposes.
The wgll tool consists of a core component that defines state and man-
ages state transitions (states.py). This includes computations for the cryp-
tographic handshake protocol (noise wg.py). This library can be used to
develop new tests in Python, but for more concise test specifications we have
developed a small domain-specific language (DSL) that is interpreted by the
wgll.py script.
We have used wgll to verify a specification and implementation issue
related to key rotation (Section 2.5). Our tool could potentially be adapted
to create protocol conformance tests in the future, for example to verify
anti-replay functionality.
Our low-level prototyping tool for WireGuard consists of 585 SLOC. The
source code and example scenarios can be found in the author’s Git reposi-
tory [60].
49
Chapter 5
50
at minimum 32 bytes, but potentially 1232 bytes according to Section 2.3.3.
Unlike protocols such as QUIC [35], WireGuard performs no validation of the
source IP address during connection migration. This weakness makes Wire-
Guard VPN providers complicit in denial-of-service attacks against other
hosts.
Consider an attacker with the following capabilities:
The attacker does not necessarily have the ability to observe arbitrary
network traffic from a VPN provider. Neither does the attacker require visi-
bility into network traffic to the victim.
We present the following attack scenario:
• The attacker sends a small HTTP GET request through the VPN tun-
nel in order to download a large file. It sets the source address of the
outer IP packet to the address of a victim.
• The VPN server receives the WireGuard transport data message and
verifies its authenticity. As it detects a change of the source IP address
of the outer packet, it will deliver future transport data messages to
the victim.
• The VPN server forwards the HTTP GET request to the website which
will start returning TCP segments with response data.
• The attacker continues sending small TCP ACK segments through the
tunnel with a forged IP address in order to avoid the TCP window from
closing down. This keeps the download stream active. At the Wire-
Guard session level, this data message also ensures that the receiver
considers the peer as reachable.
51
• The attacker can periodically restore its original IP address to check
the status of the TCP stream and synchronize sequence numbers if
needed.
This attack can continue until the session expires after about 3 minutes,
but an attacker can always start a new handshake and continue exploitation.
The attacker could also use multiple providers to increase the load to the
victim.
The above attack scenario works best if the website that acts as traffic
generator is able to produce a stable packet stream at a predictable rate and
with predictable segment sizes. A possible mechanism to maximize through-
put is to probe the TCP congestion avoidance algorithm of the receiver,
measure the latency, and model the behavior of the TCP stack of the traffic
generator. Once these details are known, the attacker can try to maximize
the receive window assumed by the traffic generator by emitting TCP ACK
segments at fixed intervals, even if those segments have not been received yet.
Note that this attack does not require a large bandwidth from the attacker.
Low latency is not necessary either since the attacker does not actually have
to receive the payload. Low jitter on the other hand is important to avoid
the traffic generator from filling up the assumed TCP receive window which
would reduce the throughput.
While HTTP over TCP is a commonly available service that could be
abused in an attack, UDP-based protocols could be even more dangerous if
they require no acknowledgement messages. For example, iPerf3 is a protocol
for testing network throughput. It consists of a TCP control channel and a
UDP test stream [43]. While an iPerf3 client usually floods a server, this
role can be reversed such that the server floods the client instead. The pro-
tocol operates as follows. The iPerf3 client connects to the iPerf3 server over
TCP and exchanges parameters, including the reverse mode setting. Once
confirmed, the client sends a single UDP datagram to the server. The server
will then continue flooding UDP datagrams to the originating UDP port un-
til a stop signal is sent over the TCP control channel. In an attack, one
could perform the initial iPerf3 setup over TCP, then send the single iPerf3
UDP message in a WireGuard data message with a spoofed outer IP source
address. If suitable iPerf3 servers are available, then this attack is more pow-
erful than the TCP/HTTP variant since no further messages are necessary
after the initial setup. We tested one iPerf3 server using the iperf3 -u -t
3 -c ping.online.net -p5208 -R command and confirmed that the pro-
52
tocol is indeed a viable amplification method. This particular server does
impose rate limits however.
The author of the WireGuard protocol has previously reported a potential
issue due to the lack of authenticity for the IP address [16]. In their scenario,
an attacker could force itself into a temporary man-in-the-middle position
after observing and forwarding one packet with a modified IP source address.
The attacker could then selectively drop packets, perform correlation attacks,
and so on. The author of the WireGuard protocol did not consider this to
problematic enough to warrant inclusion of extra implementation options
to disable roaming. However, our amplification attack was not considered
before and it arguably needs further action. Either the option to disable
roaming should be added, or the protocol should be repaired.
To deter these attacks, we recommend modifying the WireGuard proto-
col specification by applying the connection migration considerations from
QUIC [35, Section 9]:
• Addresses that have been validated before might not trigger another
address validation. This is for efficiency reasons.
53
to be determined. Lower numbers could result in data loss on high-
latency networks since the handshake takes more time, higher numbers
could increase the exploitation window.
• Endpoints must not change their addresses during the handshake. Ad-
dress validation works by sending a challenge to the peer that is being
verified. They must return a response from the same address to prove
that they are able to receive messages from said IP address. If there
is a mismatch between addresses, it could indicate a rogue peer who
was able to complete the handshake but subsequently directs traffic to
a third-party victim. We note that this property is already required
for when MAC2 validation is enabled as cookies are cryptographically
bound to the original address. However current implementations, in-
cluding Linux and wireguard-go, do not enforce this for regular hand-
shake messages, any accepted handshake message will implicitly update
the endpoint address without further validation.
• New handshakes are usually created as part of key rotation (Section 2.5).
Overloading this mechanism for address validation could result in colli-
54
sions with regular key rotations and thus loss of data due to expiration
of old session keys.
• Handshake initiations can occur only once every 5 seconds which limits
the potential validations that can be triggered. This could increase
latency for session establishment when a user rapidly switches between
networks, for example from Wi-Fi to mobile and back.
• Handshake initiation messages are larger than the smallest data mes-
sage (148 bytes versus 32 bytes), so there is still potential for an am-
plification attack.
55
This message is 40 bytes which is 1.25 times larger than the smallest, a
keepalive message, but still smaller than a data message with an actual IP
packet. To avoid an amplification attack to one target, we limit the number
of challenges per address per time unit (Challenge-Timeout). Similarly to
handshake initiation messages, a retransmission mechanism is necessary to
ensure that the challenge request reaches the peer. A challenge token message
would thus be retransmitted for up to Challenge-Attempt-Time. Note that
both timers are tracked on a per-peer basis rather than per-address under
the assumption that legitimate peers are typically active on only one address
at a time, and only their most recent address would have to be confirmed.
While handshakes are expensive, challenges are not, so we suggest lower
parameter values:
Symbol Value
Challenge-Attempt-Time 30 seconds
Challenge-Timeout 2 seconds
2. If the address was not previously challenged, create a new random token
and remember the address and token pair for this peer. This pair must
be stored per peer and not shared between different peers. Otherwise
that could be used as a side-channel to confirm use of a certain IP
address by other users at the same VPN provider.
3. If this address has recently been challenged (within the past Challenge-
Timeout), defer validation.
56
• The peer has become unreachable again on the new network, for
example due to switching networks or shutting down.
57
same token in its payload field. The payload fields of both Echo messages
can contain arbitrary data and can store up to approximately 216 bytes, and
is thus a perfect fit for the 64-bit token.
While this is compatible with existing deployed implementations, it does
have drawbacks when compared to the previous solution:
On the other hand, it might offer slightly more privacy since a passive
observer is not able to immediately recognize token messages based on the
type field.
This compatible mechanism likely requires a considerable amount of extra
complexity in the implementations. Additionally, the unpatched peer imple-
mentations would still remain vulnerable to the attack that was supposed to
be countered. Thus, while this mechanism could potentially be implemented
by VPN providers, a separate challenge message would still be preferable for
simplicity.
58
initiation, it would no longer be able to complete the handshake. Effec-
tively, the peers have ended up with two disjoint sessions and are unable to
communicate with each other until a new handshake is attempted.
We were able to confirm this theory through experimental evaluation us-
ing our low-level WireGuard prototyping tool (wgll) (Section 4.3). Consider
this test scenario:
5. wgll sends a data message, encrypted using keys matching its own
initiated session from step 2.
Figure 5.1 visualizes our empirical results using a packet capture between
wgll and WireGuard. The above steps match with the frame numbers.
59
Figure 5.1: The packet list in Wireshark matching the handshake initiation
collision scenario. From left to right, the displayed column cover the frame
number, the relative time in seconds, the UDP source port, the WireGuard
session number, and a summary of the packet.
The kernel logs from WireGuard that correspond to these events are:
60
that WireGuard’s current session is based on the handshake initiation from
wgll in Frame 2.
Figure 5.2: .
5.2.1 Impact
While the protocol specification and implementations now require random
jitter to reduce the probability of accidental handshake initiation collisions,
it does not completely prevent the issue where both peers are unable to send
encrypted data.
61
Consider the following denial-of-service attack against two peers, Alice
and Bob, from an adversary, Mallory, with interception and suppression ca-
pabilities:
4. Alice and Bob overwrite their original handshake state and are no
longer able to complete the handshake.
While both parties are following the protocol, they will still not be able
to establish a mutual session in our attacker model. The practical impact of
this issue might be limited as an attacker with such capabilities could also
suppress all traffic without maintaining state.
62
that the MAC1 field enables adversaries to confirm the destination public
key of messages from honest senders.
So while the Noise IKpsk2 protocol pattern provides a form of identity
hiding, WireGuard weakens this property due to changes outside the cryp-
tographic handshake which is based on Noise. The cryptographic handshake
does not leak the identity, but the MAC1 field trivially allows passive ob-
servers to confirm the identity of a receiver using their static public key, as-
suming honest senders. Based on these observations, we have extended our
Wireshark dissector (Section 4.2) to identify the receiver in a linear number
of operations (linear based on the set of known static public keys).
When considering a quantum adversary who knows the receiver’s pub-
lic key, then the initiator identity can also be learned from the handshake
initiation message as will be described in Chapter 6.
Implementations that would like to maintain the stronger identity hiding
property of Noise could ignore or remove the MAC1 field, but this has some
disadvantages:
• It is not compatible with the WireGuard protocol, both the sender and
receiver would require modifications. Even if the wire format remains
compatible (for example, by using all zeroes for the MAC1 field), no
handshake can be completed if only one party ignores the field. If only
the initiator is modified, then the responder will drop the message due
to an invalid MAC1 value. If only the responder is modified, then the
initiator will still reveal the responder’s identity and will still require
the responder to include a valid MAC1 field in the response.
• When a receiver of a message is “under load”, and the MAC2 field does
not pass validation, it may skip expensive handshake computations and
respond with a Cookie Reply message instead, but only if the MAC1
field passes validation. If the MAC1 field is ignored, then an attacker
can induce a reply to any destination address.
63
• Receivers of Responder messages can be forced to perform even more
computations under some circumstances. When the receiver has out-
standing, unanswered initiation messages as identified by a unique 32-
bit identifier, then an attacker could use this 32-bit identifier to force
computation of at minimum four KDFs, two hash functions, two DH
functions, and one AEAD decryption. These computations are required
to derive the keys that are necessary to verify the encrypted payload
in a response message. This attack does not even require knowledge of
any public key, passively observing initiation messages is sufficient to
learn the 32-bit identifier. Actively blocking responder messages from
a legitimate responder will also extend the time period in which the
receiver (initiator) can be attacked.
64
Chapter 6
Transitional post-quantum
security improvement
This chapter includes the main results from Tiny WireGuard Tweak by Jacob
Appelbaum, Chloe Martindale, and the author as published and presented at
AFRICACRYPT 2019 [1]. It has been edited to reduce duplicate information
that has already been treated elsewhere in this thesis. The original function
and variable names were maintained in references to the handshake details
in Algorithm 1. For the full, original version, see [1].
6.1 Introduction
WireGuard optionally allows peers to fix a pairwise-unique static symmetric
value known as a Pre-Shared Key (PSK). WireGuard does not require, nor
use a PSK by default. A protocol is post-quantum transitionally secure when
it is secure against a passive adversary with a quantum computer [51]. If this
transitionally secure protocol is used today, it is not possible for a quantum
attacker to decrypt today’s network traffic, tomorrow.
If a future adversary has access to a quantum computer, historic network
traffic protected by WireGuard, and knowledge of one WireGuard user’s
long-term static public key, this threatens the security of the protocol for
all related WireGuard users, as explained in Section 6.2. In this chapter
we propose a tiny tweak to the WireGuard protocol that makes WireGuard
traffic flows secure against such an adversary; if our alteration is incorporated
into the WireGuard protocol, a user’s historic traffic will not be decryptable
65
by such an adversary if users do not release their long-term static public key
to the network, as explained in Section 6.4. We accomplish this with both
extremely minimal costs and minimal changes to the original protocol, as
detailed in Section 6.5.
66
Algorithm 1 Simplified WireGuard key agreement process
Public Input: Curve25519 E/Fp , base point P ∈ E(Fp ), hash function H,
an empty string , key derivation function KDFn returning n derived
values indexed by n, and a MAC function Poly1305.
Secret Input (Laura): secret key skL ∈ Z, public key pkL = skL · P ∈
E(Fp ), Julian’s pre-shared public key pkJ ∈ E(Fp ), shared secret s =
DH(skL , pkJ ), message time, PSK Q ∈ {0, 1}256 ; Q = 0256 by default.
Secret Input (Julian): secret key skJ ∈ Z, public key pkJ = skJ · P ∈
E(Fp ), Laura’s pre-shared public key pkL ∈ E(Fp ), shared secret s =
DH(skJ , pkL ), PSK Q ∈ {0, 1}256 ; Q = 0256 by default.
Output: Session keys.
1: Both parties choose ephemeral secrets: eskL ∈ Z for Laura, eskJ ∈ Z for
Julian.
2: Laura publishes epkL ← eskL · P .
3: Laura computes seJL ← eskL · pkJ ; Julian computes seJL ← skJ · epkL .
4: Both parties compute (ck1 , k1 ) ← KDF2 (epkL , seJL ).
5: Laura computes h1 ← H(pkJ ||epkL ).
6: Laura computes and transmits enc-id ← aead-enc(k1 , 0, pkL , h1 ).
7: Julian decrypts enc-id with aead-dec(k1 , 0, enc-id, h1 ) and verifies that the
resulting value (pkL ) is valid user’s public key; aborts on failure.
8: Both parties compute (ck2 , k2 ) = KDF2 (ck1 , s).
9: Laura computes h2 ← H(h1 ||enc-id).
10: Laura computes and transmits enc-time ← aead-enc(k2 , 0, time, h2 ).
11: Both parties compute pkt ← epkL ||enc-id||enc-time.
12: Laura computes and transmits mac1 ← MAC(pkJ , pkt).
13: Julian verifies that mac1 = MAC(pkJ , pkt); aborts on failure.
14: Julian computes time = aead-dec(k2 , 0, enc-time, h2 ); aborts on failure.
15: Julian transmits epkJ ← eskJ · P .
16: Laura computes seLJ ← skL · epkJ ; Julian computes seLJ ← eskJ · pkL .
17: Laura computes ee ← eskL · epkJ ; Julian computes ee ← eskJ · epkL .
18: Both parties compute (ck3 , t, k3 ) ← KDF3 (ck2 ||epkJ ||ee||seLJ , Q).
19: Julian computes h3 ← H(h2 ||enc-time||epkJ ||t).
20: Julian computes and transmits enc-e ← aead-enc(k3 , 0, , h3 ).
21: Laura verifies that = aead-dec(k3 , 0, enc-e, h3 ).
22: Both parties compute shared secrets (Ti , Tr ) ← KDF2 (ck3 , ).
23: return (Ti , Tr ).
67
other WireGuard users, then this attack does not apply to those two users.
68
prevent compromise of session keys Ti and Tr in Step 22 of Algorithm 1 as
the adversary no longer has enough information to compute ck3 in Step 18
of Algorithm 1.
A prudent user may still be concerned about an adversary stealing their
PSK; the tiny protocol tweak presented in Section 6.4 addresses this concern
as well as protecting those who use the default mode of the protocol.
Of course our tweak cannot protect against an adversary who steals the
static long-term public key of both the initiator and the responder in a Wire-
Guard handshake.
69
With knowledge of zero or only one long-term static public key, the protocol
remains secure. A redesign of the WireGuard protocol to achieve full post-
quantum security is still needed.
There are of course other choices of values to replace the static public
key in Step 6 and Step 7 of Algorithm 1 to increase security. One alternative
choice of value is an empty string, as in the case with the message sent in re-
sponse to initiator packets by the responder. This would change the number
of trial decryptions for the responder for initiator messages to O(n) where
n is the number of configured peers. This change would allow any would-be
attacker to force the responder to perform many more expensive calculations.
It would improve identity hiding immensely but at a cost that simply suggests
using a different Noise pattern in the first place. A second alternative choice
of value is a random string which is linked to a user at configuration time,
similar to a username or a numbered account, which is common in OpenVPN
and similar deployments. This provides O(1) efficiency in lookups of session
structures but with a major loss in ease of use and configuration. It would
also add a second identifier for the peer which does not improve identity hid-
ing. Both alternative choices have drawbacks. The first method would create
an attack vector for unauthenticated consumption of responder resources and
the second method would require additional configuration. Both weaken the
channel binding property of Noise [47, Chapter 14] as the encrypted public
key of the initiator is no longer hashed in the handshake hash. The major
advantage of our proposed choice is that it does not complicate configuration,
nor does it require a wire format change for the WireGuard protocol. Assum-
ing collision-resistance of the hash function, the channel binding property is
also preserved. Our proposal concretely improves the confidentiality of the
protocol without increasing the computation in any handshake. It increases
the computation for peer configuration by only a single hash function for
each configured public key.
This change does not prevent linkability of flows as it exchanges one static
identifier for another, and it does preclude sharing that identifier in a known
vulnerable context.
70
Guard paper [18] as well as the effect on the alternative implementations.
The hash function input (the initiator’s static public key) and the output
have an identical length, thus the wire format and internal message structure
definitions do not need to change to accommodate the extra hash operation.
Initiators only have a single additional computational cost, computing the
hash of their own static public key. This could be done during each handshake
at no additional memory cost, or during device configuration which only
requires an additional 32 bytes of memory in the device configuration data
structure to store the hash of the peer’s long-term static public key.
Responders must be able to find the peer configuration based on the
initiation handshake message since it includes the peer’s static public key,
optional PSK, permitted addresses, and so on. In the unmodified protocol, a
hash table could be used to enable efficient lookups using the static public key
as table key. At insertion time, a hash would be computed over the table key.
The Linux kernel implementation uses SipHash2-4 [2] as hash function for this
table key [18, Section 7.4]. Our modification increases the size of the per-peer
data structure by 32 bytes and requires a single additional hash computation
per long-term static public key at device configuration time. There are no
additional memory or computational costs during the handshake.
The wireguard-go [21, device/device.go] implementation uses a standard
map data type using the static public key as map key. Again, a single addi-
tional hash computation is required at configuration time with no additional
memory usage.
Recall that WireGuard is based on the Noise protocol framework. Our
modification is not compatible with the current version of this framework,
and thus implementations that rely on a Noise library to create and process
handshake messages must be changed to use an alternative Noise implemen-
tation. This affects the Rust implementation [22].
71
Chapter 7
Conclusion
The primary research question was evaluating whether the security claims
made by WireGuard are correct and evaluating whether it is suitable for
implementing a secure Virtual Private Network (VPN).
Based on the analysis of the protocol specification and implementations,
we provided a detailed protocol description (Chapter 2). This description
includes details that were omitted in the original whitepaper, but present in
actual implementations, such as ECN support (Section 2.3.3) and handshake
initiation jitter (Section 5.2).
Based on our analysis and the results in related work (Chapter 3), we can
confirm that the security claims are correct. However, the claimed identity
hiding property is actually weaker than the original guarantee provided by the
Noise protocol (Section 5.3). We demonstrated the feasibility of exploitation
by developing a new Wireshark protocol dissector for WireGuard that reveals
the identities of the peers (Section 4.2).
We created several tools to study WireGuard implementations, an ini-
tial prototype of a Wireshark dissector in Lua (Section 4.1), an improved
Wireshark dissector in C (Section 4.2), and the wgll WireGuard implemen-
tation in Python for testing other implementations (Section 4.3). To enable
decryption of network traffic in Wireshark dissectors, we developed mecha-
nisms to extract secrets from kernel memory (Section 4.1.1) and store these
in packet capture files [59]. We investigated various X25519 implementations
to support decryption using these secrets (Section 4.2.1).
We described several potential improvements, such as protection against
a denial-of-service attack against third parties (Section 5.1) and transitional
post-quantum security (Chapter 6). The latter improvement has been incor-
72
porated in our peer-reviewed paper that was published at AFRICACRYPT
2019.
Our wgll tool was able to confirm a potential issue in WireGuard imple-
mentations that could result in failure to establish a communication channel
in rare circumstances (Section 5.2). An attacker could potentially use this
implementation detail to prevent sessions from being established. However,
in most cases it would be more effective to simply drop messages to achieve
the same denial-of-service attack against peers.
Our evaluation of the boringtun implementation also revealed that the
original specification was not clear enough, resulting in linkability concerns
for sessions (Section 5.4). However as WireGuard does not claim to possess
this property, it should not considered to be problematic. A more severe
issue with the boringtun implementation is improper implementation of key
rotation [58]. Our protocol specification and state machine description (Sec-
tion 2.7) could potentially help third-party implementations to avoid such
issues.
We conclude that WireGuard is a secure building block to build secure
VPN software, but do observe that it can be used in a denial-of-service attack
against third parties. Aside from that issue, we think that WireGuard is
suitable for mass-adoption.
73
Bibliography
[1] Jacob Appelbaum, Chloe Martindale, and Peter Wu. Tiny WireGuard
Tweak. In AFRICACRYPT 2019, volume 11627 of Lecture Notes in
Computer Science. Springer, 2019. URL https://eprint.iacr.org/
2019/482. To appear.
[2] Jean-Philippe Aumasson and Daniel J. Bernstein. SipHash: A fast
short-input PRF. In INDOCRYPT 2012, volume 7668 of Lecture Notes
in Computer Science, pages 489–508. Springer, 2012. URL https:
//eprint.iacr.org/2012/351.
[3] Daniel J. Bernstein. TAI64, TAI64N, and TAI64NA, 1997. URL https:
//cr.yp.to/libtai/tai64.html.
[4] Daniel J. Bernstein. Curve25519: New Diffie-Hellman Speed Records.
In Public Key Cryptography, volume 3958 of Lecture Notes in Com-
puter Science, pages 207–228. Springer, 2006. URL https://cr.yp.
to/papers.html#curve25519.
[5] Daniel J. Bernstein. Boring crypto, October 2015. URL https://cr.
yp.to/talks.html#2015.10.05.
[6] Daniel J. Bernstein, Tanja Lange, and Peter Schwabe. The Security Im-
pact of a New Cryptographic Library. In LATINCRYPT 2012, volume
7533 of Lecture Notes in Computer Science, pages 159–176. Springer,
2012. URL https://eprint.iacr.org/2011/646.
[7] Daniel J. Bernstein, Bernard van Gastel, Wesley Janssen, Tanja Lange,
Peter Schwabe, and Sjaak Smetsers. TweetNaCl: A Crypto Li-
brary in 100 Tweets. In LATINCRYPT 2014, volume 8895 of Lec-
ture Notes in Computer Science, pages 64–83. Springer, 2014. URL
https://tweetnacl.cr.yp.to/papers.html.
74
[8] Karthikeyan Bhargavan and Gaëtan Leurent. On the practical
(In-)Security of 64-bit block ciphers: Collision attacks on HTTP over
TLS and OpenVPN. In ACM Conference on Computer and Commu-
nications Security, pages 456–467. ACM, 2016. URL https://eprint.
iacr.org/2016/798.
[10] CERT. C compilers may silently discard some wraparound checks, April
2008. URL https://www.kb.cert.org/vuls/id/162289/.
[12] Dr. Steve E. Deering and Bob Hinden. Internet Protocol, Version 6
(IPv6) Specification. RFC 8200, July 2017. URL https://rfc-editor.
org/rfc/rfc8200.txt.
[14] Frank Denis. Sodium: A modern, portable, easy to use crypto library,
2019. URL https://libsodium.org/. version 1.0.16.
75
[17] Jason A. Donenfeld. extract-handshakes.sh: Handshake extractor, 2018.
URL https://git.zx2c4.com/WireGuard/tree/contrib/examples/
extract-handshakes/README.
[22] Jason A. Donenfeld. Source code for the Rust implementation of Wire-
Guard, Jan 2019. URL https://git.zx2c4.com/wireguard-rs. com-
mit a7a2e5231571.
[24] Jason A. Donenfeld. Wintun – Layer 3 TUN Driver for Windows, April
2019. URL https://www.wintun.net/.
[25] Jason A. Donenfeld and Kevin Milner. Formal verification of the Wire-
Guard protocol, 2018. URL https://www.wireguard.com/papers/
wireguard-formal-verification.pdf. version b956944 2018-06-07.
[27] Eric Dumazet. Linux kernel patch: ipv6: Limit mtu to 65575 bytes,
2014. URL https://git.kernel.org/linus/30f78d8ebf7f.
76
[28] Sally Floyd, Dr. K. K. Ramakrishnan, and David L. Black. The Addition
of Explicit Congestion Notification (ECN) to IP. RFC 3168, September
2001. URL https://rfc-editor.org/rfc/rfc3168.txt.
[30] Gerald Combs, et. al. Wireshark: Network protocol analyzer, 1998–
2019. URL https://www.wireshark.org/.
77
[38] Werner Koch. Add dedicated X25519 function to Libcgrypt, December
2018. URL https://dev.gnupg.org/T4293.
[45] Yoav Nir and Adam Langley. ChaCha20 and Poly1305 for IETF Pro-
tocols. RFC 8439, June 2018. URL https://rfc-editor.org/rfc/
rfc8439.txt.
[47] Trevor Perrin. The Noise Protocol Framework, 2018. URL https:
//noiseprotocol.org/noise.html. revision 34 2018-07-11.
78
[48] LLVM Project. libFuzzer – a library for coverage-guided fuzz testing,
2019. URL https://llvm.org/docs/LibFuzzer.html. version 8.0.0.
[52] Yaron Sheffer and Hannes Tschofenig. Internet Key Exchange Protocol
Version 2 (IKEv2) Session Resumption. RFC 5723, January 2010. URL
https://rfc-editor.org/rfc/rfc5723.txt.
[54] Ludvig Strigeus. Source code of the TunSafe client, Dec 2018. URL
https://github.com/TunSafe/TunSafe. commit 85a871c1d226.
[56] Peter Wu. Wireshark dissector (written in Lua) for dissecting the
WireGuard tunneling protocol., Aug 2018. URL https://github.com/
Lekensteyn/wireguard-dissector. commit 240f0ab22232.
[57] Peter Wu. Bug 15011 – Support for WireGuard VPN protocol,
2018. URL https://bugs.wireshark.org/bugzilla/show_bug.cgi?
id=15011.
79
[58] Peter Wu. Incomplete session lifetime management (key rotation)
– boringtun, Mar 2019. URL https://github.com/cloudflare/
boringtun/issues/50. Bug report about an implementation issue.
[59] Peter Wu. Add DSB type for WireGuard (0x57474b4c) – pcapng, May
2019. URL https://github.com/pcapng/pcapng/pull/62.
[60] Peter Wu. Low-level prototyping tool for WireGuard, May 2019. URL
https://github.com/Lekensteyn/wgll. commit 11ac7925687b.
80
Appendix A
This appendix lists the protocol narration with a focus on the most important
data flow in the handshake computations as described in Section 2.3. For
brevity, steps 4 and 5 have been simplified by removing hashing.
1. Initial handshake message creation
1 : Initiator Responder
2 : pkI , skI , tsp, initial data P pkR , skR
81
2. Initial handshake message processing
epkI , enc-id, enc-tsp, mac1
12 :
Initiator packet
82
4. Response handshake message processing
epkR , enc-e, mac1r
30 :
Responder packet
. . . . . . . . . . . . . . . . . . . . . . . . . . Session established . . . . . . . . . . . . . . . . . . . . . . . . . .
44 :
83