Rot256 : Cryptography & Other Random Bits.

GSoC: WireGuard

Introduction

Google Summer of Code

Every year Google arranges their Summer of Code program, giving students the opportunity of contributing to open source projects during the summer and receive a stipend. Over this summer I have completed the program with the WireGuard team (under the Linux foundation umbrella), with the goal to create a user space implementation of WireGuard.

WireGuard

WireGuard is a simple (layer 3) VPN protocol based around a Noise pattern (in particular Noise_IKpsk2). Among the primary features offered by WireGuard are:

— Simple state machine
By doing away with error prone features such as cipher suite negotiation, the state machine of WireGuard is much simpler than most other VPNs in existence (especially anything relying on TLS). This translates into smaller code bases and hence easier code review. The kernel implementation is ~4000 LoC (excluding cryptographic primitives) and the user space detailed in this post roughly the same; with plenty of comments and support for 3 platforms.

— Forward secrecy
A 3-way Diffie-Hellman handshake is performed (much the same way as the Signal Protocol). Mixing the static identity keys with ephemeral keys to achieve both authentication and forward secrecy. Every 2 minutes a new handshake is negotiated (if it succeeds), after ≈ 6 minutes all key material has been “rotated” and decrypting packets older than 6 minutes becomes impossible.

— Connectionless UX
A WireGuard interface appears stateless. When the interface receives a packet, the packet is either encrypted using an existing session key or a new session key is negotiated and the packet later encrypted and transmitted. By default WireGuard only establishes a session if packets are being send. The user never has to explicitly request that the VPN (re)connect.

— Single client
Every WireGuard client is also a WireGuard server and vise versa. Every interface has a private key it uses to authenticate itself to other peers, every peer has a public keys and a list of subnets from which they are allowed to send packets (meaning the source address of any packet originating from a WireGuard interface is cryptographically authenticated). There is no fundamental distinction between client and server in WireGuard.

— Protection against denial of service
A connecting peer must display knowledge of the public key for the interface, if this check fails WireGuard remains silent and the initial packet is not processed. Very often this alone will thwart a DoS attack (since the public key is often unknown to outside parties). When load increases WireGuard will begin responding with “Cookie messages”, these require no state on the responders site and proves possession of the source IP, after which token based rate-limiting is performed based on source IP.

— Optional post quantum protection
The Noise pattern used by WireGuard allows the mixing of a static secret, since the speedups of quantum attacks seem to be relatively modest against symmetric primitives, the addition of a shared secret allows for the possibility of maintaining secrecy PQ by exchanging a shared secret out-of-band. This also permits people to experiment with PQ key encapsulation schemes for exchanging/rotating the PSK and/or achieve PQ future secrecy (e.g. by applying a hash function to the PSK daily).

— Simple configuration
A WireGuard interfaces guarantees that the source address of a packet maps to the corresponding public key configured for the subnet. Hence firewall rules can be used to grant access to resources based on public keys, in a way that is easy to reason about and with tools administrators are already familiar with.

— Full support for IPv6
Of course WireGuard allows encapsulation of IPv4 in IPv6 and vise versa.

For anyone interested in WireGuard I recommend reading the whitepaper.
Currently WireGuard is implemented as a Linux kernel module, to minimizing latency and maximizing throughput, my task has been to create a user space implementation.

The project

The code has not undergone thorough review and has known deficiencies.
Do not let your privacy, liberty, financial well-being and sanity depend on this software!

The code

The code can be found at git.zx2c4.com, a short description can be found on the GSoC page. The current implementation targets Linux (primary platform), OS X and Windows (although the configuration tools are not yet available on this platform). Although the golang user space implementation compiles and creates an encrypted tunnel with the existing kernel space implementation on all 3 platforms, the currently the status of the project is experimental.

I plan to focus on making sure the core of the implementation (the cross-platform component) conforms with the WireGuard specification (especially the timer based state transitions) and improving the Linux platform specific code during the next months.

Contributions to the Windows, OS X (and BSD) specific code would be greatly appreciated. The project separates the platform specific code from the cross-platform code and divides it into three components:

— TUN interface
Which described how to create and interact with TUN devices on the platform.

— Daemonization
Describes how a service/daemonized process is created on the platform.

— Configuration listener
WireGuard user space implementations are configured using a simple key-value protocol, this allows users to mix and match configuration tools (custom scripts, wg(8) or GUI applications) with WireGuard implementations as they see fit. The platform dependent code must describe how to listen for new configuration connections: on Linux (and OS X) this is done using UNIX domain sockets and on Windows using bidirectional named pipes.

After implementing these three, the implementation should work with no further modifications. Below an image of the current experimental version for Windows:

Wireguard on windows

Go as a language

I chose Go for a number of reasons:

— Readable code
Even people who have never written Go can usually read it after a short introduction to channels and Goroutines. This increases the pool of people who are capable of reading though the source and contributing.

— Reasonably high speed
The current implementation saturates Gigabit connections on commodity laptops.
The overhead of garbage collection appears to be minimal (also in terms of latency).

— Mature crypto library
golang.org/x/crypto contains (almost) everything needed to implement WireGuard.

— Goroutines makes seemingly naive implementations performat
Writing networking code in Go is generally a pleasant experience.

— Easy cross compilation

— I was already proficient in Go

With that said, the following cons are also worth noting:

— Lack of memory management
The primary problem is controlling where cryptographic secrets are allocated.
A common practice within /x/crypto is providing a “New” method which returns an instance implementing some cryptographic interface (e.g cipher.AEAD or cipher.Hash), which results in the allocation of a structure containing crytographic state and key material on the ordinary heap, where it may be swapped to disk, potentially violating the forward secrecy guarantees of WireGuard. I am currently exploring ways to mitigate this. In addition the libraries generally do not provide a way to zero the state, however this can be done using either reflection or unsafe.

— Lack of generics
Due to the lack of generics it is hard to do type-safe pooling of resources. In the implementation the large message buffers are “recycled” to take load of the garbage collector, this is done by inserting them into a sync.Pool, however sync.Pool operates only with the generic interface{}, when removing an element from the pool we therefore need a type assertion, if this fails we get a runtime panic.

Final notes

I would like to thank my mentors Jason Donenfeld & Greg Kroah-Hartman for giving me the chance to learn about and implement Noise and WireGuard in practice.