diff --git a/signer/storage/aes_gcm_storage.go b/signer/storage/aes_gcm_storage.go index 8c5e147ac6..d281eb44c0 100644 --- a/signer/storage/aes_gcm_storage.go +++ b/signer/storage/aes_gcm_storage.go @@ -17,24 +17,13 @@ package storage import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" "encoding/json" - "io" "io/ioutil" "os" "github.com/ethereum/go-ethereum/log" ) -type storedCredential struct { - // The iv - Iv []byte `json:"iv"` - // The ciphertext - CipherText []byte `json:"c"` -} - // AESEncryptedStorage is a storage type which is backed by a json-file. The json-file contains // key-value mappings, where the keys are _not_ encrypted, only the values are. type AESEncryptedStorage struct { @@ -62,12 +51,12 @@ func (s *AESEncryptedStorage) Put(key, value string) { log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename) return } - ciphertext, iv, err := encrypt(s.key, []byte(value), []byte(key)) + ciphertext, iv, err := Encrypt(s.key, []byte(value), []byte(key)) if err != nil { log.Warn("Failed to encrypt entry", "err", err) return } - encrypted := storedCredential{Iv: iv, CipherText: ciphertext} + encrypted := StoredCredential{Iv: iv, CipherText: ciphertext} data[key] = encrypted if err = s.writeEncryptedStorage(data); err != nil { log.Warn("Failed to write entry", "err", err) @@ -90,7 +79,7 @@ func (s *AESEncryptedStorage) Get(key string) (string, error) { log.Warn("Key does not exist", "key", key) return "", ErrNotFound } - entry, err := decrypt(s.key, encrypted.Iv, encrypted.CipherText, []byte(key)) + entry, err := Decrypt(s.key, encrypted.Iv, encrypted.CipherText, []byte(key)) if err != nil { log.Warn("Failed to decrypt key", "key", key) return "", err @@ -112,8 +101,8 @@ func (s *AESEncryptedStorage) Del(key string) { } // readEncryptedStorage reads the file with encrypted creds -func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) { - creds := make(map[string]storedCredential) +func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]StoredCredential, error) { + creds := make(map[string]StoredCredential) raw, err := ioutil.ReadFile(s.filename) if err != nil { @@ -131,7 +120,7 @@ func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredentia } // writeEncryptedStorage write the file with encrypted creds -func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCredential) error { +func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]StoredCredential) error { raw, err := json.Marshal(creds) if err != nil { return err @@ -141,39 +130,3 @@ func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCrede } return nil } - -// encrypt encrypts plaintext with the given key, with additional data -// The 'additionalData' is used to place the (plaintext) KV-store key into the V, -// to prevent the possibility to alter a K, or swap two entries in the KV store with eachother. -func encrypt(key []byte, plaintext []byte, additionalData []byte) ([]byte, []byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - aesgcm, err := cipher.NewGCM(block) - nonce := make([]byte, aesgcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return nil, nil, err - } - if err != nil { - return nil, nil, err - } - ciphertext := aesgcm.Seal(nil, nonce, plaintext, additionalData) - return ciphertext, nonce, nil -} - -func decrypt(key []byte, nonce []byte, ciphertext []byte, additionalData []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - plaintext, err := aesgcm.Open(nil, nonce, ciphertext, additionalData) - if err != nil { - return nil, err - } - return plaintext, nil -} diff --git a/signer/storage/aes_gcm_storage_test.go b/signer/storage/aes_gcm_storage_test.go index 664ef12994..50c55e5910 100644 --- a/signer/storage/aes_gcm_storage_test.go +++ b/signer/storage/aes_gcm_storage_test.go @@ -28,31 +28,8 @@ import ( "github.com/mattn/go-colorable" ) -func TestEncryption(t *testing.T) { - // key := []byte("AES256Key-32Characters1234567890") - // plaintext := []byte(value) - key := []byte("AES256Key-32Characters1234567890") - plaintext := []byte("exampleplaintext") - - c, iv, err := encrypt(key, plaintext, nil) - if err != nil { - t.Fatal(err) - } - t.Logf("Ciphertext %x, nonce %x\n", c, iv) - - p, err := decrypt(key, iv, c, nil) - if err != nil { - t.Fatal(err) - } - t.Logf("Plaintext %v\n", string(p)) - if !bytes.Equal(plaintext, p) { - t.Errorf("Failed: expected plaintext recovery, got %v expected %v", string(plaintext), string(p)) - } -} - func TestFileStorage(t *testing.T) { - - a := map[string]storedCredential{ + a := map[string]StoredCredential{ "secret": { Iv: common.Hex2Bytes("cdb30036279601aeee60f16b"), CipherText: common.Hex2Bytes("f311ac49859d7260c2c464c28ffac122daf6be801d3cfd3edcbde7e00c9ff74f"), @@ -133,7 +110,7 @@ func TestSwappedKeys(t *testing.T) { s1.Put("k2", "v2") // Now make a modified copy - creds := make(map[string]storedCredential) + creds := make(map[string]StoredCredential) raw, err := ioutil.ReadFile(s1.filename) if err != nil { t.Fatal(err) diff --git a/signer/storage/aes_gcm_util.go b/signer/storage/aes_gcm_util.go new file mode 100644 index 0000000000..75ba747349 --- /dev/null +++ b/signer/storage/aes_gcm_util.go @@ -0,0 +1,53 @@ +package storage + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" +) + +// StoredCredential stores the json structure of the stored credential +type StoredCredential struct { + // The iv + Iv []byte `json:"iv"` + // The ciphertext + CipherText []byte `json:"c"` +} + +// Encrypt encrypts plaintext with the given key, with additional data +// The 'additionalData' is used to place the (plaintext) KV-store key into the V, +// to prevent the possibility to alter a K, or swap two entries in the KV store with eachother. +func Encrypt(key []byte, plaintext []byte, additionalData []byte) ([]byte, []byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + aesgcm, err := cipher.NewGCM(block) + nonce := make([]byte, aesgcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, nil, err + } + if err != nil { + return nil, nil, err + } + ciphertext := aesgcm.Seal(nil, nonce, plaintext, additionalData) + return ciphertext, nonce, nil +} + +// Decrypt decrypts plaintext from given key, nounce, ciphertext and additionalData +func Decrypt(key []byte, nonce []byte, ciphertext []byte, additionalData []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + plaintext, err := aesgcm.Open(nil, nonce, ciphertext, additionalData) + if err != nil { + return nil, err + } + return plaintext, nil +} diff --git a/signer/storage/aes_gcm_util_test.go b/signer/storage/aes_gcm_util_test.go new file mode 100644 index 0000000000..a92e1d42b6 --- /dev/null +++ b/signer/storage/aes_gcm_util_test.go @@ -0,0 +1,28 @@ +package storage + +import ( + "bytes" + "testing" +) + +func TestEncryption(t *testing.T) { + // key := []byte("AES256Key-32Characters1234567890") + // plaintext := []byte(value) + key := []byte("AES256Key-32Characters1234567890") + plaintext := []byte("exampleplaintext") + + c, iv, err := Encrypt(key, plaintext, nil) + if err != nil { + t.Fatal(err) + } + t.Logf("Ciphertext %x, nonce %x\n", c, iv) + + p, err := Decrypt(key, iv, c, nil) + if err != nil { + t.Fatal(err) + } + t.Logf("Plaintext %v\n", string(p)) + if !bytes.Equal(plaintext, p) { + t.Errorf("Failed: expected plaintext recovery, got %v expected %v", string(plaintext), string(p)) + } +}