Latest release: sanctum 0.9.3

What is sanctum?

Sanctum is a small, reviewable, experimental and fully privilege seperated VPN daemon capable of transporting encrypted network traffic between two peers.


Sanctum is built using a multi-process approach where each process is only doing one thing. This allows for more fine-grained sandboxing in relation to permissions or allowed system calls.

Several different processes exist that all only perform one task:

Packets flow between these processes in a well-defined manner making it impossible to move a packet straight from the red side to the black side without passing the encryption process and vice-versa.

Traffic encryption

Traffic is by default encrypted under AES256-GCM with unique keys in both RX and TX directions using a 96-bit nonce consisting of a 32-bit salt and a 64-bit packet counter (see rfc4106).

Using the CIPHER environment variable you can however change traffic encryption to use Agelas, which is an experimental duplex-sponge AEAD cipher based on the Keccak-p[1600,24] permutation.

Key exchange

Sanctum uses strong shared symmetrical secrets from which an encryption key is derived for wrapping a generated session key that is transmitted to its peer. Each sanctum instance is responsible for sending its RX session key to its configured peer periodically.

Keys are expired automatically after a given number of packets have been submitted on them (1 << 34), or after 1-hour.

The underlying algorithm for wrapping the generated session key is the AEAD duplex-sponge stream cipher mentioned earlier. The entire key derivation and offer process is as follows:

    Wrapping key (wk) derivation:
        s = shared secret, 256-bit
        se = seed selected uniformly at random, 512-bit
        wk = KMAC256(s, len(se) || se), 512-bit

    Key offer:
        now = Seconds since boot, 64-bit
        salt = The salt for nonce construction, 32-bit
        id = unique sanctum ID generated at start, 64-bit
        key = session key selected uniformly at random, 256-bit

        seed = se from wk derivation above
        spi = The SPI for this association

        header = magic || spi || seed
        encdata = id || now || key || salt

        encdata = Agelas(wk, aad=header, encdata)
        send(header || encdata)

While this alone does not provide PFS, the underlying key may be swapped out OOB by other means while sanctum is running. I recommend you rotate this key often via a mechanism of your liking.

Why did you write sanctum?

I wrote it so I can be certain that my packets are blessed correctly according to the scriptures of cryptology.


Ok, I wrote sanctum because I wanted something I can trust fully myself. I am a very private person and want to excercise my right to privacy, even online. There are definitely alternatives, but I opted to carve out something for myself.

Plus, it's cool to hack on stuff.

What makes you qualified to build this?

If you are asking yourself that question, that's ok. The people who know, know. I have been building these type of things for many years at high assurance levels. Now, if this makes you nervous and rather not use Sanctum that is fine, there are plenty of alternatives.

But none of them have cool mythology though ;)


Latest release: sanctum 0.9.3

A mirror of the repository is available on github.


A small simple guide is available here.

I want to contribute!

mail diffs to joris snabel-a sanctorum punkt se