From 181aa56c8711c9dc40eb7d2f2b259b0d1ed5240e Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 13 Mar 2016 12:55:02 -0700 Subject: [PATCH] Added symmetric encrypt/decrypt methods --- encrypt.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ encrypt_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 encrypt.go create mode 100644 encrypt_test.go diff --git a/encrypt.go b/encrypt.go new file mode 100644 index 000000000..36bddd44c --- /dev/null +++ b/encrypt.go @@ -0,0 +1,51 @@ +package crypto + +import ( + "errors" + + . "github.com/tendermint/go-common" + "golang.org/x/crypto/nacl/secretbox" +) + +const nonceLen = 24 +const secretLen = 32 + +// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) +// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. +// NOTE: call crypto.MixEntropy() first. +func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { + if len(secret) != secretLen { + PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) + } + nonce := CRandBytes(nonceLen) + nonceArr := [nonceLen]byte{} + copy(nonceArr[:], nonce) + secretArr := [secretLen]byte{} + copy(secretArr[:], secret) + ciphertext = make([]byte, nonceLen+secretbox.Overhead+len(plaintext)) + copy(ciphertext, nonce) + secretbox.Seal(ciphertext[nonceLen:nonceLen], plaintext, &nonceArr, &secretArr) + return ciphertext +} + +// secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) +// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. +func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) { + if len(secret) != secretLen { + PanicSanity(Fmt("Secret must be 32 bytes long, got len %v", len(secret))) + } + if len(ciphertext) <= secretbox.Overhead+nonceLen { + return nil, errors.New("Ciphertext is too short") + } + nonce := ciphertext[:nonceLen] + nonceArr := [nonceLen]byte{} + copy(nonceArr[:], nonce) + secretArr := [secretLen]byte{} + copy(secretArr[:], secret) + plaintext = make([]byte, len(ciphertext)-nonceLen-secretbox.Overhead) + _, ok := secretbox.Open(plaintext[:0], ciphertext[nonceLen:], &nonceArr, &secretArr) + if !ok { + return nil, errors.New("Ciphertext decryption failed") + } + return plaintext, nil +} diff --git a/encrypt_test.go b/encrypt_test.go new file mode 100644 index 000000000..f05c812b6 --- /dev/null +++ b/encrypt_test.go @@ -0,0 +1,51 @@ +package crypto + +import ( + "bytes" + "golang.org/x/crypto/bcrypt" + "testing" +) + +func TestSimple(t *testing.T) { + + MixEntropy([]byte("someentropy")) + + plaintext := []byte("sometext") + secret := []byte("somesecretoflengththirtytwo===32") + ciphertext := EncryptSymmetric(plaintext, secret) + + plaintext2, err := DecryptSymmetric(ciphertext, secret) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(plaintext, plaintext2) { + t.Errorf("Decrypted plaintext was %X, expected %X", plaintext2, plaintext) + } + +} + +func TestSimpleWithKDF(t *testing.T) { + + MixEntropy([]byte("someentropy")) + + plaintext := []byte("sometext") + secretPass := []byte("somesecret") + secret, err := bcrypt.GenerateFromPassword(secretPass, 12) + if err != nil { + t.Error(err) + } + secret = Sha256(secret) + + ciphertext := EncryptSymmetric(plaintext, secret) + + plaintext2, err := DecryptSymmetric(ciphertext, secret) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(plaintext, plaintext2) { + t.Errorf("Decrypted plaintext was %X, expected %X", plaintext2, plaintext) + } + +}