Bletchley is a simple, high-level Go library and command-line tool for asymmetric encryption and decryption.
It implements a basic hybrid cryptosystem by wrapping functionality in the Go standard library.
Design goals:
- Easy to use
- Idiot-proof
- Use standard, open formats
Design non-goals:
- Authentication
- Performance
- Flexibility
- Determinism
go get -u github.com/pivotal-cf-experimental/bletchley/bletchley
bletchley -o generate -public public_key.pem -private private_key.pem
echo "this is a secret message" | bletchley -o encrypt -public public_key.pem > encrypted.json
cat encrypted.json | bletchley -o decrypt -private private_key.pem
Look at the CLI tool for a full working example. Look at the Godoc for a complete reference.
privateKey, publicKey, err := bletchley.Generate()
privateKeyPEM := bletchley.PrivateKeyToPEM(privateKey)
err := ioutil.WriteFile("private_key.pem", privateKeyPEM, os.FileMode(0600))
publicKeyPEM, err := bletchley.PublicKeyToPEM(publicKey)
err = ioutil.WriteFile("public_key.pem", publicKeyPEM, os.FileMode(0644))
privateKeyPEM, err := ioutil.ReadFile("private_key.pem")
privateKey, err := bletchley.PEMToPrivateKey(privateKeyPEM)
publicKeyPEM, err := ioutil.ReadFile("public_key.pem")
publicKey, err := bletchley.PEMToPublicKey(publicKeyPEM)
plaintextBytes := []byte("this is a secret message")
encryptedMessage, err := bletchley.Encrypt(publicKey, plaintextBytes)
encryptedBytes, err := json.Marshal(encryptedMessage)
err = ioutil.WriteFile("encrypted.json", encryptedBytes, os.FileMode(0644))
encryptedBytes, err := ioutil.ReadFile("encrypted.json")
var encryptedMessage bletchley.EncryptedMessage
err = json.Unmarshal(encryptedBytes, &encryptedMessage)
plaintextBytes, err := bletchley.Decrypt(privateKey, encrypted)
Bletchley does not authenticate messages. You must rely on an external mechanism to prevent or detect tampering of encrypted messages. Authentication would require the sender to have a secret, either an asymmetric private key for a digital signature or a symmetric secret for message authentication. That is out of scope for this project.
Looking at the code in symmetric.go
you'll see we're using zero bytes for the "nonce" in the symmetric encryption step. This is justified for two reasons:
-
We use each symmetric key for exactly one message. Each key is created from a cryptographically strong pseudo-random generator, used once, asymmetrically encrypted, and never re-used. See Section 8.2.1 of NIST Special Publication 800-38D for details on nonce requirements when the key is one-time-use. See also this discussion of AEAD nonces, which covers the zero nonce case.
-
We make no authentication assurances (see above), but nonce uniqueness is only required for the authentication guarantees of GCM, not for the secrecy guarantees. This is detailed in Appendix A of the same NIST document.
The math of asymmetric cryptography requires that the message not be longer than the public key. Therefore, we implement a hybrid cryptosystem where arbitrary-length plaintext is first symmetrically encrypted using a strong random key, and that key is then asymmetrically encrypted. This is the standard approach to solving the message length issue.
The Go standard library implements RSA, and ECDSA, but not ECIES. While there appears to be at least one partial implementation of ECIES in Go, we're reluctant to depend on anything outside the standard library.