github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/signer/storage/aes_gcm_storage.go (about)

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