For Devs … “Go” Seal and Open The Box

For some reason many developers struggle with the concepts involved with encryption, and often implement poor standards, or have no…

Photo by Rumman Amin on Unsplash

For Devs … “Go” Seal and Open The Box

We live in the 21st Century, but still, we cannot trust any emails that we receive for either the validity of the sender or that the email message has not been changed or even if it has been read by someone else.

For some reason, though, many developers struggle with the concepts involved with encryption, and often implement poor standards, or have no encryption at all. This has led to an untrusted world of data, and where we can trust little within our data infrastructure. Why can’t we sign data with our private key, and then send it a remote system with the public key of the entity? That’s the way it should be done, but we still blindly send and receive data without any form of checking, and just assume our tunnels will protect us.

In a GDPR-driven world, this must change. So, is there something that developers can quickly integrate, but properly encrypt and authenticate data? Well, the Box package in Go is one of the best methods around for wrapping up salt, fast encryption (with XSalsa20 — a much faster encryption method than AES), authentication (using Curve 25519, and MAC (Poly1305).

So let’s use Go, to implement a simple encryption process:

The Box package works in a similar way to PGP email, and where Bob generates a symmetric key and uses the XSalsa20 encryption method to encrypt the message. Bob then takes a hash of this message, and encrypt it with his private key, and this is included in the encrypted message. Bob then encrypts the XSalsa20 key with Alice’s public key. He then sends the encrypted message to her, and she will decrypt the key used to encrypt the message with her private key. She can then read the message. Now she checks the signature. For this, she takes Bob’s public key and then decrypts the hash. If she can decrypt it, she checks the hash against the hash that she gets, and if they are the same, she knows that it was Bob who sent the message, and that the message is unchanged:

The Go Box package supports the encryption and authentication of short messages, using Curve25519 (for public key encryption), XSalsa20 (for symmetric key encryption) and Poly1305 (for message authentication). In this case we generate a public and private key for Bob and also for Alice. The public key is displayed as the x-co-ordinate of the public key point. The following is a sample of the code:

package main
import (
"golang.org/x/crypto/nacl/box"
"crypto/rand"
"io"
"fmt"
"flag"
)
func main() {
 flag.Parse()
args := flag.Args()
 message:=args[0]
AlicePublicKey, AlicePrivateKey, _ := box.GenerateKey(rand.Reader)
fmt.Printf("Alice private %x\n",*AlicePrivateKey)
fmt.Printf("Alice public (x-co-ord) %x\n",*AlicePublicKey)
BobPublicKey, BobPrivateKey, _ := box.GenerateKey(rand.Reader)

fmt.Printf("\nBob private %x\n",*BobPrivateKey)
fmt.Printf("Bob public (x-co-ord) %x\n",*BobPublicKey)

var nonce [24]byte
io.ReadFull(rand.Reader, nonce[:])
msg := []byte(message)
encrypted := box.Seal(nonce[:], msg, &nonce, BobPublicKey, AlicePrivateKey)
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, _ := box.Open(nil, encrypted[24:], &decryptNonce, AlicePublicKey, BobPrivateKey)
fmt.Printf("\nMessage: %s\n",message)
fmt.Printf("\nEncrypted: %x\n\n",encrypted)
fmt.Printf("Decrypted %s",string(decrypted))
}

The following is a sample run:

Alice private 3b4d6f7784b997ac262071c78a1a605af93f6e32e76ddeed3119fcabbd3b829f
Alice public (x-co-ord) 5c999ef6915b5cec518861303e67de37d9e1b842218c2820cebff4e547912329
Bob private aa1ba56956c75523c8a7f926d0356989c040ea7092cd7d16d25d1aadddda09da
Bob public (x-co-ord) b70cfc4613bf35eed81d58a5d211e4197d207c1a88ee494a3ecd4bd5d85dcb26
Message: abc
Encrypted: 06686a97f5e4fb11d9c90befac2d283cc5730df6942e58d6e6548a3b76c7dd17a4d107a525ca468a85e98d
Decrypted abc

We see that Alice takes the message, and the nonce, and then signs the message with her private key. Next she will encrypt the message and the signature with Bob’s public key:

encrypted := box.Seal(nonce[:], msg, &nonce, BobPublicKey, AlicePrivateKey)

Bob will then take the first 24 bytes of the encrypted data, and use this as the nonce, and the rest as the cipher stream. He will then decrypt with his private key, and checks the signature using Alice’s public key.

decrypted, _ := box.Open(nil, encrypted[24:], &decryptNonce, AlicePublicKey, BobPrivateKey)

In this way we have security (using Bob’s public and private keys) and authenication of Alice and her message (using Alice’s public and private keys). The way it works is that we use an XSala20 symmetric key to encrypt the message, and which is encrypted with Alice’s public key. Only she can then decrypt this key, and thus decrypt the encrypted message.

Conclusions

No excuses for not signing data … the Box package provides an easy to use solution, and makes sure that data is protected and also signed for. Here is the demo: