From 1916f385ed63c99c7912c895f3fb4b8057a12ece Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 10 Jan 2014 03:58:43 -0700 Subject: [PATCH] Move {En,De}cryptCBC and MakeRandom to symcrypt. The symcrypt package now contains common secret-key code that is redefined in a number of packages. --- cryptor/cryptor.go | 15 ++------ ecdh/ecdh.go | 48 ++---------------------- passvault/passvault.go | 77 ++++++++++----------------------------- symcrypt/symcrypt.go | 46 +++++++++++++++++++++++ symcrypt/symcrypt_test.go | 39 ++++++++++++++++++++ 5 files changed, 113 insertions(+), 112 deletions(-) create mode 100644 symcrypt/symcrypt.go create mode 100644 symcrypt/symcrypt_test.go diff --git a/cryptor/cryptor.go b/cryptor/cryptor.go index 707f9ab..8d260d8 100644 --- a/cryptor/cryptor.go +++ b/cryptor/cryptor.go @@ -9,13 +9,13 @@ import ( "crypto/aes" "crypto/cipher" "crypto/hmac" - "crypto/rand" "crypto/sha1" "encoding/json" "errors" "github.com/cloudflare/redoctober/keycache" "github.com/cloudflare/redoctober/padding" "github.com/cloudflare/redoctober/passvault" + "github.com/cloudflare/redoctober/symcrypt" "sort" "strconv" ) @@ -51,13 +51,6 @@ type EncryptedData struct { Signature []byte } -// makeRandom is a helper to make new buffer full of random data -func makeRandom(length int) (bytes []byte, err error) { - bytes = make([]byte, length) - _, err = rand.Read(bytes) - return -} - // encryptKey encrypts data with the key associated with name inner, // then name outer func encryptKey(nameInner, nameOuter string, clearKey []byte, pubKeys map[string]SingleWrappedKey) (out MultiWrappedKey, err error) { @@ -265,7 +258,7 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) { } // Generate random IV and encryption key - ivBytes, err := makeRandom(16) + ivBytes, err := symcrypt.MakeRandom(16) if err != nil { return } @@ -274,7 +267,7 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) { // encrypted.IV encrypted.IV = append([]byte{}, ivBytes...) - clearKey, err := makeRandom(16) + clearKey, err := symcrypt.MakeRandom(16) if err != nil { return } @@ -295,7 +288,7 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) { if rec.GetType() == passvault.RSARecord || rec.GetType() == passvault.ECCRecord { // only wrap key with RSA key if found - if singleWrappedKey.aesKey, err = makeRandom(16); err != nil { + if singleWrappedKey.aesKey, err = symcrypt.MakeRandom(16); err != nil { return nil, err } diff --git a/ecdh/ecdh.go b/ecdh/ecdh.go index 2264293..b05d6da 100644 --- a/ecdh/ecdh.go +++ b/ecdh/ecdh.go @@ -6,7 +6,6 @@ package ecdh import ( "crypto/aes" - "crypto/cipher" "crypto/ecdsa" "crypto/elliptic" "crypto/hmac" @@ -14,6 +13,7 @@ import ( "crypto/sha1" "errors" "github.com/cloudflare/redoctober/padding" + "github.com/cloudflare/redoctober/symcrypt" ) var Curve = elliptic.P256 @@ -37,13 +37,13 @@ func Encrypt(pub ecdsa.PublicKey, in []byte) (out []byte, err error) { return nil, errors.New("Failed to generate encryption key") } shared := x.Bytes() - iv, err := makeRandom(16) + iv, err := symcrypt.MakeRandom(16) if err != nil { return } paddedIn := padding.AddPadding(in) - ct, err := encryptCBC(paddedIn, iv, shared[:16]) + ct, err := symcrypt.EncryptCBC(paddedIn, iv, shared[:16]) if err != nil { return } @@ -92,50 +92,10 @@ func Decrypt(priv *ecdsa.PrivateKey, in []byte) (out []byte, err error) { return nil, errors.New("Invalid MAC") } - paddedOut, err := decryptCBC(ct[aes.BlockSize:tagStart], ct[:aes.BlockSize], shared[:16]) + paddedOut, err := symcrypt.DecryptCBC(ct[aes.BlockSize:tagStart], ct[:aes.BlockSize], shared[:16]) if err != nil { return } out, err = padding.RemovePadding(paddedOut) return } - -// Utility functions copied from passvault. These handle encryption -// and decryption of data using AES-128-CBC. - -// decryptCBC decrypt bytes using a key and IV with AES in CBC mode. -func decryptCBC(data, iv, key []byte) (decryptedData []byte, err error) { - aesCrypt, err := aes.NewCipher(key) - if err != nil { - return - } - ivBytes := append([]byte{}, iv...) - - decryptedData = make([]byte, len(data)) - aesCBC := cipher.NewCBCDecrypter(aesCrypt, ivBytes) - aesCBC.CryptBlocks(decryptedData, data) - - return -} - -// encryptCBC encrypt data using a key and IV with AES in CBC mode. -func encryptCBC(data, iv, key []byte) (encryptedData []byte, err error) { - aesCrypt, err := aes.NewCipher(key) - if err != nil { - return - } - ivBytes := append([]byte{}, iv...) - - encryptedData = make([]byte, len(data)) - aesCBC := cipher.NewCBCEncrypter(aesCrypt, ivBytes) - aesCBC.CryptBlocks(encryptedData, data) - - return -} - -// makeRandom is a helper that makes a new buffer full of random data -func makeRandom(length int) ([]byte, error) { - bytes := make([]byte, length) - _, err := rand.Read(bytes) - return bytes, err -} diff --git a/passvault/passvault.go b/passvault/passvault.go index 7994d94..fa9b7cd 100644 --- a/passvault/passvault.go +++ b/passvault/passvault.go @@ -10,7 +10,6 @@ import ( "bytes" "code.google.com/p/go.crypto/scrypt" "crypto/aes" - "crypto/cipher" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -22,6 +21,7 @@ import ( "errors" "github.com/cloudflare/redoctober/ecdh" "github.com/cloudflare/redoctober/padding" + "github.com/cloudflare/redoctober/symcrypt" "io/ioutil" "math/big" mrand "math/rand" @@ -97,7 +97,7 @@ type Summary struct { func init() { // seed math.random from crypto.random - seedBytes, _ := makeRandom(8) + seedBytes, _ := symcrypt.MakeRandom(8) seedBuf := bytes.NewBuffer(seedBytes) n64, _ := binary.ReadVarint(seedBuf) mrand.Seed(n64) @@ -109,40 +109,33 @@ func hashPassword(password string, salt []byte) ([]byte, error) { return scrypt.Key([]byte(password), salt, N, R, P, KEYLENGTH) } -// makeRandom is a helper that makes a new buffer full of random data -func makeRandom(length int) ([]byte, error) { - bytes := make([]byte, length) - _, err := rand.Read(bytes) - return bytes, err -} - // encryptRSARecord takes an RSA private key and encrypts it with // a password key func encryptRSARecord(newRec *PasswordRecord, rsaPriv *rsa.PrivateKey, passKey []byte) (err error) { - if newRec.RSAKey.RSAExpIV, err = makeRandom(16); err != nil { + if newRec.RSAKey.RSAExpIV, err = symcrypt.MakeRandom(16); err != nil { return } paddedExponent := padding.AddPadding(rsaPriv.D.Bytes()) - if newRec.RSAKey.RSAExp, err = encryptCBC(paddedExponent, newRec.RSAKey.RSAExpIV, passKey); err != nil { + if newRec.RSAKey.RSAExp, err = symcrypt.EncryptCBC(paddedExponent, newRec.RSAKey.RSAExpIV, passKey); err != nil { return } - if newRec.RSAKey.RSAPrimePIV, err = makeRandom(16); err != nil { + if newRec.RSAKey.RSAPrimePIV, err = symcrypt.MakeRandom(16); err != nil { return } paddedPrimeP := padding.AddPadding(rsaPriv.Primes[0].Bytes()) - if newRec.RSAKey.RSAPrimeP, err = encryptCBC(paddedPrimeP, newRec.RSAKey.RSAPrimePIV, passKey); err != nil { + if newRec.RSAKey.RSAPrimeP, err = symcrypt.EncryptCBC(paddedPrimeP, newRec.RSAKey.RSAPrimePIV, passKey); err != nil { return } - if newRec.RSAKey.RSAPrimeQIV, err = makeRandom(16); err != nil { + if newRec.RSAKey.RSAPrimeQIV, err = symcrypt.MakeRandom(16); err != nil { return } paddedPrimeQ := padding.AddPadding(rsaPriv.Primes[1].Bytes()) - newRec.RSAKey.RSAPrimeQ, err = encryptCBC(paddedPrimeQ, newRec.RSAKey.RSAPrimeQIV, passKey) + newRec.RSAKey.RSAPrimeQ, err = symcrypt.EncryptCBC(paddedPrimeQ, newRec.RSAKey.RSAPrimeQIV, passKey) return } @@ -154,12 +147,12 @@ func encryptECCRecord(newRec *PasswordRecord, ecPriv *ecdsa.PrivateKey, passKey return } - if newRec.ECKey.ECPrivIV, err = makeRandom(16); err != nil { + if newRec.ECKey.ECPrivIV, err = symcrypt.MakeRandom(16); err != nil { return } paddedX509 := padding.AddPadding(ecX509) - newRec.ECKey.ECPriv, err = encryptCBC(paddedX509, newRec.ECKey.ECPrivIV, passKey) + newRec.ECKey.ECPriv, err = symcrypt.EncryptCBC(paddedX509, newRec.ECKey.ECPrivIV, passKey) return } @@ -167,7 +160,7 @@ func encryptECCRecord(newRec *PasswordRecord, ecPriv *ecdsa.PrivateKey, passKey func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err error) { newRec.Type = DefaultRecordType - if newRec.PasswordSalt, err = makeRandom(16); err != nil { + if newRec.PasswordSalt, err = symcrypt.MakeRandom(16); err != nil { return } @@ -175,7 +168,7 @@ func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err return } - if newRec.KeySalt, err = makeRandom(16); err != nil { + if newRec.KeySalt, err = symcrypt.MakeRandom(16); err != nil { return } @@ -211,7 +204,7 @@ func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err } // encrypt AES key with password key - aesKey, err := makeRandom(16) + aesKey, err := symcrypt.MakeRandom(16) if err != nil { return } @@ -257,36 +250,6 @@ func encryptECB(data, key []byte) (encryptedData []byte, err error) { return } -// decryptCBC decrypt bytes using a key and IV with AES in CBC mode. -func decryptCBC(data, iv, key []byte) (decryptedData []byte, err error) { - aesCrypt, err := aes.NewCipher(key) - if err != nil { - return - } - ivBytes := append([]byte{}, iv...) - - decryptedData = make([]byte, len(data)) - aesCBC := cipher.NewCBCDecrypter(aesCrypt, ivBytes) - aesCBC.CryptBlocks(decryptedData, data) - - return -} - -// encryptCBC encrypt data using a key and IV with AES in CBC mode. -func encryptCBC(data, iv, key []byte) (encryptedData []byte, err error) { - aesCrypt, err := aes.NewCipher(key) - if err != nil { - return - } - ivBytes := append([]byte{}, iv...) - - encryptedData = make([]byte, len(data)) - aesCBC := cipher.NewCBCEncrypter(aesCrypt, ivBytes) - aesCBC.CryptBlocks(encryptedData, data) - - return -} - // InitFromDisk reads the record from disk and initialize global context. func InitFromDisk(path string) error { jsonDiskRecord, err := ioutil.ReadFile(path) @@ -358,7 +321,7 @@ func InitFromDisk(path string) error { if records.Version == 0 { records.Version = DEFAULT_VERSION records.VaultId = int(mrand.Int31()) - records.HmacKey, err = makeRandom(16) + records.HmacKey, err = symcrypt.MakeRandom(16) if err != nil { return err } @@ -429,14 +392,14 @@ func ChangePassword(name, password, newPassword string) (err error) { } // add the password salt and hash - if pr.PasswordSalt, err = makeRandom(16); err != nil { + if pr.PasswordSalt, err = symcrypt.MakeRandom(16); err != nil { return } if pr.HashedPassword, err = hashPassword(newPassword, pr.PasswordSalt); err != nil { return } - if pr.KeySalt, err = makeRandom(16); err != nil { + if pr.KeySalt, err = symcrypt.MakeRandom(16); err != nil { return } newPassKey, err := derivePasswordKey(newPassword, pr.KeySalt) @@ -623,7 +586,7 @@ func (pr PasswordRecord) GetKeyECC(password string) (key *ecdsa.PrivateKey, err return } - x509Padded, err := decryptCBC(pr.ECKey.ECPriv, pr.ECKey.ECPrivIV, passKey) + x509Padded, err := symcrypt.DecryptCBC(pr.ECKey.ECPriv, pr.ECKey.ECPrivIV, passKey) if err != nil { return } @@ -651,7 +614,7 @@ func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err err return } - rsaExponentPadded, err := decryptCBC(pr.RSAKey.RSAExp, pr.RSAKey.RSAExpIV, passKey) + rsaExponentPadded, err := symcrypt.DecryptCBC(pr.RSAKey.RSAExp, pr.RSAKey.RSAExpIV, passKey) if err != nil { return } @@ -660,7 +623,7 @@ func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err err return } - rsaPrimePPadded, err := decryptCBC(pr.RSAKey.RSAPrimeP, pr.RSAKey.RSAPrimePIV, passKey) + rsaPrimePPadded, err := symcrypt.DecryptCBC(pr.RSAKey.RSAPrimeP, pr.RSAKey.RSAPrimePIV, passKey) if err != nil { return } @@ -669,7 +632,7 @@ func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err err return } - rsaPrimeQPadded, err := decryptCBC(pr.RSAKey.RSAPrimeQ, pr.RSAKey.RSAPrimeQIV, passKey) + rsaPrimeQPadded, err := symcrypt.DecryptCBC(pr.RSAKey.RSAPrimeQ, pr.RSAKey.RSAPrimeQIV, passKey) if err != nil { return } diff --git a/symcrypt/symcrypt.go b/symcrypt/symcrypt.go new file mode 100644 index 0000000..3b3552d --- /dev/null +++ b/symcrypt/symcrypt.go @@ -0,0 +1,46 @@ +// Package symcrypt contains common symmetric encryption functions. +package symcrypt + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" +) + +// DecryptCBC decrypt bytes using a key and IV with AES in CBC mode. +func DecryptCBC(data, iv, key []byte) (decryptedData []byte, err error) { + aesCrypt, err := aes.NewCipher(key) + if err != nil { + return + } + ivBytes := append([]byte{}, iv...) + + decryptedData = make([]byte, len(data)) + aesCBC := cipher.NewCBCDecrypter(aesCrypt, ivBytes) + aesCBC.CryptBlocks(decryptedData, data) + + return +} + +// EncryptCBC encrypt data using a key and IV with AES in CBC mode. +func EncryptCBC(data, iv, key []byte) (encryptedData []byte, err error) { + aesCrypt, err := aes.NewCipher(key) + if err != nil { + return + } + ivBytes := append([]byte{}, iv...) + + encryptedData = make([]byte, len(data)) + aesCBC := cipher.NewCBCEncrypter(aesCrypt, ivBytes) + aesCBC.CryptBlocks(encryptedData, data) + + return +} + +// MakeRandom is a helper that makes a new buffer full of random data. +func MakeRandom(length int) ([]byte, error) { + bytes := make([]byte, length) + _, err := io.ReadFull(rand.Reader, bytes) + return bytes, err +} diff --git a/symcrypt/symcrypt_test.go b/symcrypt/symcrypt_test.go new file mode 100644 index 0000000..0a76a3a --- /dev/null +++ b/symcrypt/symcrypt_test.go @@ -0,0 +1,39 @@ +package symcrypt + +import ( + "bytes" + "github.com/cloudflare/redoctober/padding" + "testing" +) + +func TestCrypt(t *testing.T) { + msg := []byte("One ping only, please.") + padMsg := padding.AddPadding(msg) + + key, err := MakeRandom(16) + if err != nil { + t.Fatalf("%v", err) + } + + iv, err := MakeRandom(16) + if err != nil { + t.Fatalf("%v", err) + } + + out, err := EncryptCBC(padMsg, iv, key) + if err != nil { + t.Fatalf("%v", err) + } + + out, err = DecryptCBC(out, iv, key) + if err != nil { + t.Fatalf("%v", err) + } + + unpadOut, err := padding.RemovePadding(out) + if err != nil { + t.Fatalf("%v", err) + } else if !bytes.Equal(unpadOut, msg) { + t.Fatal("Decrypted message doesn't match original plaintext.") + } +}