Encryption with Stream Ciphers: ChaCha20

While I often use some form of cryptography as a software engineer, I only recently dug deeper, and I found that spending just a bit of time learning some details can yield a lot of useful knowledge. Let’s dig into one facet here, starting with one cipher used for encryption and decryption. I hope this will be a gentle introduction that gives you enough knowledge to explore more cryptography topics further on your own.

Let’s take a look at a common cipher called ChaCha20. It’s symmetric, meaning the two sides must already have a common key. Sometimes asymmetric encryption is used to negotitate this key, like when opening an HTTPS or other TLS connection.

ChaCha is a stream cipher, meaning a securely random stream of data called a keystream is combined with a plaintext message using a mathematical operation like XOR to produce the encrypted message. For a synchronous stream cipher like ChaCha, the keystream has no relation to the data being encrypted.

The ChaCha algorithm produces a keystream from a 32-byte key and an 2-byte nonce. Once we have the keystream, the encryption operation is easy: simply XOR the plaintext message with the keystream. If we have a 100-byte message to encrypt, we’ll use the ChaCha algorithm to get the first 100 bytes of the keystream. We iterate through each byte, taking the XOR of that byte with the corresponding byte from the plaintext message to give us the encrypted message.

We can perform exactly that same operation (with the same key and nonce) again on the encrypted message to get the decrypted message.

The complexity in a stream cipher is in generating a secure key stream from a given key and nonce; the rest is easy. I’ll leave figuring out the math behind generating the key stream as an exercise for the reader 😉.

Standard

TLS 1.3 and Forward Secrecy

One of Edward Snowden’s revelations was how the NSA decrypts much of the traffic on the Internet. The strategy is straightforward: they capture tons and tons of traffic that’s encrypted with TLS from important routers on the Internet. Separately, they break into and obtain private keys from servers across the Internet. If or when they have the private key for a given server, they can use it to decrypt the TLS traffic for all of that server’s connections.

Fortunately, it’s possible to protect all of that content even if a server’s private key is compromised. This is called “forward secrecy.” Even if the server’s private key is compromised, the encryption is not broken.

How can we achieve forward secrecy? Recall the ways that an asymmetric key pair can be used: the private key can be used to create a signature that the public key can verify, or the public key can be used to encrypt a message that only the private key can decrypt. The weakness is in encrypting a message using the server’s public key: this asymmetric key pair doesn’t change for potentially years at a time, because it’s tied to an X.509 certificate issued by some already-trusted third party (AKA a certificate authority).

Fortunately, there’s a simple alternative: rather than using the server’s long-term key pair for encryption, we can create a new key pair for each connection or “session”. We’ll use the long-term key pair only to sign the “session” public key. The session key pair can then be used to establish a shared symmetric key that’ll be used for an authenticated, encrypted communication channel going forward.

Using this strategy, when a private key is compromised by a third party, that third party can’t use the compromised key to decrypt the connection’s traffic! Forward secrecy is one of the improvements in TLS 1.3, which was just finalized in March 2018, and should be coming soon to a browser (and servers) near you.

Standard