github.com/lsdbitrue/go-ethereum@v1.9.0/signer/storage/aes_gcm_storage.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  //
    17  
    18  package storage
    19  
    20  import (
    21  	"crypto/aes"
    22  	"crypto/cipher"
    23  	"crypto/rand"
    24  	"encoding/json"
    25  	"io"
    26  	"io/ioutil"
    27  	"os"
    28  
    29  	"github.com/ethereum/go-ethereum/log"
    30  )
    31  
    32  type storedCredential struct {
    33  	// The iv
    34  	Iv []byte `json:"iv"`
    35  	// The ciphertext
    36  	CipherText []byte `json:"c"`
    37  }
    38  
    39  // AESEncryptedStorage is a storage type which is backed by a json-file. The json-file contains
    40  // key-value mappings, where the keys are _not_ encrypted, only the values are.
    41  type AESEncryptedStorage struct {
    42  	// File to read/write credentials
    43  	filename string
    44  	// Key stored in base64
    45  	key []byte
    46  }
    47  
    48  // NewAESEncryptedStorage creates a new encrypted storage backed by the given file/key
    49  func NewAESEncryptedStorage(filename string, key []byte) *AESEncryptedStorage {
    50  	return &AESEncryptedStorage{
    51  		filename: filename,
    52  		key:      key,
    53  	}
    54  }
    55  
    56  // Put stores a value by key. 0-length keys results in noop.
    57  func (s *AESEncryptedStorage) Put(key, value string) {
    58  	if len(key) == 0 {
    59  		return
    60  	}
    61  	data, err := s.readEncryptedStorage()
    62  	if err != nil {
    63  		log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
    64  		return
    65  	}
    66  	ciphertext, iv, err := encrypt(s.key, []byte(value), []byte(key))
    67  	if err != nil {
    68  		log.Warn("Failed to encrypt entry", "err", err)
    69  		return
    70  	}
    71  	encrypted := storedCredential{Iv: iv, CipherText: ciphertext}
    72  	data[key] = encrypted
    73  	if err = s.writeEncryptedStorage(data); err != nil {
    74  		log.Warn("Failed to write entry", "err", err)
    75  	}
    76  }
    77  
    78  // Get returns the previously stored value, or an error if it does not exist or
    79  // key is of 0-length.
    80  func (s *AESEncryptedStorage) Get(key string) (string, error) {
    81  	if len(key) == 0 {
    82  		return "", ErrZeroKey
    83  	}
    84  	data, err := s.readEncryptedStorage()
    85  	if err != nil {
    86  		log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
    87  		return "", err
    88  	}
    89  	encrypted, exist := data[key]
    90  	if !exist {
    91  		log.Warn("Key does not exist", "key", key)
    92  		return "", ErrNotFound
    93  	}
    94  	entry, err := decrypt(s.key, encrypted.Iv, encrypted.CipherText, []byte(key))
    95  	if err != nil {
    96  		log.Warn("Failed to decrypt key", "key", key)
    97  		return "", err
    98  	}
    99  	return string(entry), nil
   100  }
   101  
   102  // Del removes a key-value pair. If the key doesn't exist, the method is a noop.
   103  func (s *AESEncryptedStorage) Del(key string) {
   104  	data, err := s.readEncryptedStorage()
   105  	if err != nil {
   106  		log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
   107  		return
   108  	}
   109  	delete(data, key)
   110  	if err = s.writeEncryptedStorage(data); err != nil {
   111  		log.Warn("Failed to write entry", "err", err)
   112  	}
   113  }
   114  
   115  // readEncryptedStorage reads the file with encrypted creds
   116  func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) {
   117  	creds := make(map[string]storedCredential)
   118  	raw, err := ioutil.ReadFile(s.filename)
   119  
   120  	if err != nil {
   121  		if os.IsNotExist(err) {
   122  			// Doesn't exist yet
   123  			return creds, nil
   124  		}
   125  		log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
   126  	}
   127  	if err = json.Unmarshal(raw, &creds); err != nil {
   128  		log.Warn("Failed to unmarshal encrypted storage", "err", err, "file", s.filename)
   129  		return nil, err
   130  	}
   131  	return creds, nil
   132  }
   133  
   134  // writeEncryptedStorage write the file with encrypted creds
   135  func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCredential) error {
   136  	raw, err := json.Marshal(creds)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	if err = ioutil.WriteFile(s.filename, raw, 0600); err != nil {
   141  		return err
   142  	}
   143  	return nil
   144  }
   145  
   146  // encrypt encrypts plaintext with the given key, with additional data
   147  // The 'additionalData' is used to place the (plaintext) KV-store key into the V,
   148  // to prevent the possibility to alter a K, or swap two entries in the KV store with eachother.
   149  func encrypt(key []byte, plaintext []byte, additionalData []byte) ([]byte, []byte, error) {
   150  	block, err := aes.NewCipher(key)
   151  	if err != nil {
   152  		return nil, nil, err
   153  	}
   154  	aesgcm, err := cipher.NewGCM(block)
   155  	nonce := make([]byte, aesgcm.NonceSize())
   156  	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
   157  		return nil, nil, err
   158  	}
   159  	if err != nil {
   160  		return nil, nil, err
   161  	}
   162  	ciphertext := aesgcm.Seal(nil, nonce, plaintext, additionalData)
   163  	return ciphertext, nonce, nil
   164  }
   165  
   166  func decrypt(key []byte, nonce []byte, ciphertext []byte, additionalData []byte) ([]byte, error) {
   167  	block, err := aes.NewCipher(key)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	aesgcm, err := cipher.NewGCM(block)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, additionalData)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	return plaintext, nil
   180  }