Cage Match: Programmers vs Encryption

I just spent the last few days upgrading and polishing one of the programming libraries that the Mensago server uses. As time as gone on, I’ve been going back to some of the mini-projects I threw together to get things going and cleaning house. If my code is the only impression of me that someone gets, I certainly don’t want to look like a slob.

The mini-project getting the recent renovations is GoEZNaCl, an encryption library with a bizarre name. If you spend any time reading up on Mensago news, encryption and using it right are top priorities, but what’s the big deal? How hard can it be? Nightmarishly hard, and this is why the library exists. Stick with me for a bit and I’ll show you how I’ve made it much easier for me, and by extension, Mensago. Fair warning: this is going to get really ugly before it gets better, so hold on tight!

Encryption: the Stuff of Nightmares and Headaches

Don’t believe me how hard cryptography is? Try reading the Wikipedia article on RSA, which is probably the most famous modern day way of encrypting information. Go on. I’ll wait here until you come back.

Done? How far did you get before running into something you didn’t even remotely understand? I got through the summary and hit a roadblock in the first paragraph of the history, and that doesn’t even touch the math. What math, you say?

I did well in math in school, but nothing in my high school or college classes prepared me for this. You might be in a different place, and if you understand this stuff, props to you; you are much more studied and/or intelligent than I am.

How Do I Even Do This?

Because RSA is allowed by the U.S. government for certain purposes and I wanted Mensago to have a prayer in that space some day, I needed to get it going in my library. Here is the example in Google’s documentation for how to use RSA in the Go programming language:

secretMessage := []byte("send reinforcements, we're going to advance")
label := []byte("orders")

// crypto/rand.Reader is a good source of entropy for randomizing the encryption function.
rng := rand.Reader

ciphertext, err := EncryptOAEP(sha256.New(), rng, &test2048Key.PublicKey, secretMessage, label)
if err != nil {
	fmt.Fprintf(os.Stderr, "Error from encryption: %s\n", err)
	return
}

// Since encryption is a randomized function, ciphertext will be different each time.
fmt.Printf("Ciphertext: %x\n", ciphertext)

If you understand the Go programming language, some of this might make sense, but unless you know Go and cryptography, some of it will absolutely make no sense whatsoever. The documentation is worse yet:

EncryptOAEP encrypts the given message with RSA-OAEP.

OAEP is parameterised by a hash function that is used as a random oracle. Encryption and decryption of a given message must use the same hash function and sha256.New() is a reasonable choice.

The random parameter is used as a source of entropy to ensure that encrypting the same message twice doesn't result in the same ciphertext.

The label parameter may contain arbitrary data that will not be encrypted, but which gives important context to the message. For example, if a given public key is used to encrypt two types of messages then distinct label values could be used to ensure that a ciphertext for one purpose cannot be used for another by an attacker. If not required it can be empty.

The message must be no longer than the length of the public modulus minus twice the hash length, minus a further 2. 

Even after learning about this kind of stuff for three years, this still makes no sense. Helping you would involve a crash course in cryptography, so let’s just shake our heads and move on.

On The Other Side

It does get better. How? Here’s an example of using RSA with GoEZNaCl.

keypair := GenerateEncryptionPair("RSA2048-SHA256")
testData := "This is a secret message!"

encryptedData, err := keypair.Encrypt([]byte(testData))
if err != nil {
    panic("encryption failed")
}

decryptedMsg, err := keypair.Decrypt(encryptedData)
if err != nil {
    panic("decryption failed")
}

if string(decryptedMsg) != testData {
    panic("The messages didn't match!")
}

For my non-programmer friends out there, this probably won’t look any easier, but for anyone who understands Go, this is easy stuff. All the sharp ends have been sanded off and all that remains is a nice, easy-to-use tool. Kind of like what the iPod did for MP3 players back in the 2000s. If you know anything about RSA, there are a lot of sharp bits. As a cherry on top, I’ve demonstrated both encryption and decryption in the same amount of space needed to demonstrate EncryptOAEP(). Honestly, while I know what OAEP is, as a programmer, I don’t care at all why it’s needed. I just want to encrypt things safely and not need a course in applied cryptography to do it.

If you want to check out my work, you can go to its development page over at GitLab. I’ve learned a bit, but I’m no expert, and I’d love feedback or even just a conversation. Be safe, everyone!