mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
signer/storage abstract aes_gcm related funcs to separate file for db_storage impl
This commit is contained in:
parent
b2c09a7e62
commit
1ffab783a5
4 changed files with 89 additions and 78 deletions
|
|
@ -17,24 +17,13 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"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
|
// 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.
|
// key-value mappings, where the keys are _not_ encrypted, only the values are.
|
||||||
type AESEncryptedStorage struct {
|
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)
|
log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ciphertext, iv, err := encrypt(s.key, []byte(value), []byte(key))
|
ciphertext, iv, err := Encrypt(s.key, []byte(value), []byte(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to encrypt entry", "err", err)
|
log.Warn("Failed to encrypt entry", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
encrypted := storedCredential{Iv: iv, CipherText: ciphertext}
|
encrypted := StoredCredential{Iv: iv, CipherText: ciphertext}
|
||||||
data[key] = encrypted
|
data[key] = encrypted
|
||||||
if err = s.writeEncryptedStorage(data); err != nil {
|
if err = s.writeEncryptedStorage(data); err != nil {
|
||||||
log.Warn("Failed to write entry", "err", err)
|
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)
|
log.Warn("Key does not exist", "key", key)
|
||||||
return "", ErrNotFound
|
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 {
|
if err != nil {
|
||||||
log.Warn("Failed to decrypt key", "key", key)
|
log.Warn("Failed to decrypt key", "key", key)
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -112,8 +101,8 @@ func (s *AESEncryptedStorage) Del(key string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// readEncryptedStorage reads the file with encrypted creds
|
// readEncryptedStorage reads the file with encrypted creds
|
||||||
func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) {
|
func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]StoredCredential, error) {
|
||||||
creds := make(map[string]storedCredential)
|
creds := make(map[string]StoredCredential)
|
||||||
raw, err := ioutil.ReadFile(s.filename)
|
raw, err := ioutil.ReadFile(s.filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -131,7 +120,7 @@ func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredentia
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeEncryptedStorage write the file with encrypted creds
|
// 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)
|
raw, err := json.Marshal(creds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -141,39 +130,3 @@ func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCrede
|
||||||
}
|
}
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -28,31 +28,8 @@ import (
|
||||||
"github.com/mattn/go-colorable"
|
"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) {
|
func TestFileStorage(t *testing.T) {
|
||||||
|
a := map[string]StoredCredential{
|
||||||
a := map[string]storedCredential{
|
|
||||||
"secret": {
|
"secret": {
|
||||||
Iv: common.Hex2Bytes("cdb30036279601aeee60f16b"),
|
Iv: common.Hex2Bytes("cdb30036279601aeee60f16b"),
|
||||||
CipherText: common.Hex2Bytes("f311ac49859d7260c2c464c28ffac122daf6be801d3cfd3edcbde7e00c9ff74f"),
|
CipherText: common.Hex2Bytes("f311ac49859d7260c2c464c28ffac122daf6be801d3cfd3edcbde7e00c9ff74f"),
|
||||||
|
|
@ -133,7 +110,7 @@ func TestSwappedKeys(t *testing.T) {
|
||||||
s1.Put("k2", "v2")
|
s1.Put("k2", "v2")
|
||||||
// Now make a modified copy
|
// Now make a modified copy
|
||||||
|
|
||||||
creds := make(map[string]storedCredential)
|
creds := make(map[string]StoredCredential)
|
||||||
raw, err := ioutil.ReadFile(s1.filename)
|
raw, err := ioutil.ReadFile(s1.filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
53
signer/storage/aes_gcm_util.go
Normal file
53
signer/storage/aes_gcm_util.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
28
signer/storage/aes_gcm_util_test.go
Normal file
28
signer/storage/aes_gcm_util_test.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue