On June 27th, 2021, the first permanent cjdns session was established using protocol version 22. Version 22 uses a new encryption handshake protocol based on a modified version of Wireguard, a VPN protocol designed by Jason A. Donenfeld. This is the result of the project which was funded by the PKT Network Steward executed by a team led by Alex Lightman.
This is the part one in a three part series on the encryption used in cjdns and Wireguard.
Prior to version 22, cjdns used its own cryptographic handshake protocol known as CryptoAuth. In this article, we’ll explain how Wireguard and CryptoAuth work, how they are similar, different, and the changes which were made in order to integrate a modified version of the Wireguard protocol in cjdns.
Cryptography in general can seem like an unapproachable topic; afterall, it’s based on mathematical puzzles which are considered unfeasible without secret hints to solve them. This makes the feeling that encryption can only be understood by a select few, perfectly understandable.
But applied cryptography is not very complicated at all, you just do everything the mathematicians say you must do and don’t expect anything they don’t explicitly guarantee.
The most simple and obvious form of encryption is encipherment. If you and I both know a common secret, then I can encipher a message using that secret, and you are able to decipher that message using the same secret.
In this lies a potential problem. The mathematicians tell us if we encipher more than one message using the same secret, then there are ways for someone to figure out our secret.
It turns out, we can change the secret ever so slightly, even by just one bit, and that is enough to make it safe. So when we encipher a message, we don’t use exactly the secret, we use a slightly modified version of it, and we attach a little message telling the counterparty exactly how we modified it. Since the modification to the secret can take the form of a simple “append this number”, we use what is known as a nonce, or “number used once” and the recipient knows that they must always append the nonce to the secret when deciphering the message.
So the actual encipherment process looks more like this:
We need to be attentive to the fact that this nonce takes up some extra space in each message we send, so we should send the smallest nonces feasible. Both Wireguard and cjdns use a simple counter to create a sequence of nonces.
Now that we’ve solved the problem of sending a number of messages without an intermediary knowing what those messages say, we still have the problem of an intermediary potentially tampering with them.
The way both cjdns and Wireguard prevent this is by adding another piece of data to each message called an authenticator. An authenticator is a number computed from the message content and shared secret. Any change to the message will cause the authenticator to be wrong, and correcting it is impossible without the knowledge of the shared secret.
Both cjdns and Wireguard use the Poly1305 authenticator algorithm which is known for being very fast to compute. For encipherment, Wireguard uses an algorithm called Chacha20 while the cjdns CryptoAuth uses a similar but older algorithm called Salsa20.
Poly1305 authenticators are 16 bytes in size, which means both cjdns and Wireguard must send at least 16 bytes plus the size of the nonce, in addition to the encrypted content of each message.
But how do we get this shared secret to start with?
In the old days, a shared secret needed to be hand delivered or otherwise communicated through some kind of pre-existing secure channel, but ever since the RSA algorithm was developed in the late 1970s, we have been able to acquire a shared secret mathematically with only public information communicated.
It’s easiest to think of this like cryptographic multiplication. I make up a secret number and multiply by a well known public number, this results in a number I can share with you.
Assuming you do the same thing: make up a secret number, multiply by the same well known public number and share the result with me, I can now multiply my secret by your public number and it will result in the same thing as you multiplying my public number by your secret.
my_secret = random()
my_public = my_secret * WELL_KNOWN_NUMBER
your_secret = random()
your_public = your_secret * WELL_KNOWN_NUMBER
your_shared_secret = my_public * your_secret
my_shared_secret = your_public * my_secret
In effect, you and I are able to get a shared secret by only exchanging public information, and because we’re using a special cryptographic function rather than simple multiplication, someone watching us exchange our public keys is unable to divide in order to figure out our individual secret keys or the shared secret.
Now a simple cryptographic handshake is enough to secure against passive surveillance, but unless one knows who they are communicating with, one is at risk of a man in the middle attack. In a man in the middle attack, I might think I’m talking to you, and you think you’re talking to me, but in fact we each have a secure connection to a middleman who is passing your messages to me and my messages to you. To combat this, Wireguard and cjdns (and most protocols) use long term public keys. In the case of cjdns, your IPv6 address is defined as the hash of your public key while Wireguard uses the key itself as a form of identity.
Now that we have established that we need to create long term secrets in order to identify ourselves, one might be tempted to compute a single shared secret and use it forever. This however is not at all ideal because if your secret key should ever be compromised, the attacker would not only be able to impersonate you in the future, but would also be able to discover your shared secrets and retroactively decrypt everything you ever sent or received.
CryptoAuth and Wireguard use a process where temporary public keys are created, used to compute a shared secret, and then forgotten. This allows for historical messages to remain secure even if the long term secret keys are compromised later.
When doing a handshake, CryptoAuth sends a message containing the permanent public key in plaintext and the temporary key encrypted (to prove ownership). Wireguard however uses a more clever trick of sending the temporary key in plaintext and encrypting the permanent key, thus masking the permanent key (identity) of the person doing the handshake.
Now that we’ve established how to encrypt messages with an authenticator, we don’t need to worry about forgery or modification. Using key exchange, we are able to establish a shared secret through public communication only. We haven’t covered what happens if a message is either accidentally duplicated, which occasionally happens in networks, or purposely “replayed” by an attacker.
Since each message can only be decrypted using the correct nonce, and each nonce is the previous nonce plus 1, an easy way to prevent duplicate messages is to maintain a “receive counter” and reject any nonces which are older than the most recently seen.
Unfortunately, packets in a network can arrive out of order, so both CryptoAuth and Wireguard use a bit field in addition to a received counter so that packets can be correctly received even in the case of mild re-ordering. In CryptoAuth, this is called the ReplayProtector.
One place where Wireguard protocol holds a significant advantage over CryptoAuth is in the handling of duplicate or maliciously replayed handshake packets. CryptoAuth regards this as out of the scope and thus a replay of a handshake packet can cause the encryption session to break down. Wireguard solves this by using a number in the handshake which must increase with each new handshake. To get this number, the sender uses their computer’s millisecond clock.
In the case of a small computer with no hardware clock (e.g. a wifi router), a restart will set the value back to zero which will of course make it unable to produce correct handshakes. This is not a major problem because after a couple of minutes, the sessions will expire from inactivity and then the lower number will be accepted.
This was a brief overview of how VPN-like technologies encrypt their data. While this does not cover every scenario, this should provide a good basic understanding. In the next part, we’ll cover the key differences of how the CryptoAuth and Wireguard protocols work.Go back to Blog →
An introduction to PKT Network as a high speed, end-to-end encrypted internet built by the people for the people.
PKT is an open-source project. All of the PKT blockchain code and the various projects being developed in the ecosystem are managed by contributing community members. The July 2021 development…