github.com/luckypickle/go-ethereum-vet@v1.14.2/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/luckypickle/go-ethereum-vet/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-faile. 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 no-op 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)) 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 the empty string if it does not exist or key is of 0-length 79 func (s *AESEncryptedStorage) Get(key string) string { 80 if len(key) == 0 { 81 return "" 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 "" 87 } 88 encrypted, exist := data[key] 89 if !exist { 90 log.Warn("Key does not exist", "key", key) 91 return "" 92 } 93 entry, err := decrypt(s.key, encrypted.Iv, encrypted.CipherText) 94 if err != nil { 95 log.Warn("Failed to decrypt key", "key", key) 96 return "" 97 } 98 return string(entry) 99 } 100 101 // readEncryptedStorage reads the file with encrypted creds 102 func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) { 103 creds := make(map[string]storedCredential) 104 raw, err := ioutil.ReadFile(s.filename) 105 106 if err != nil { 107 if os.IsNotExist(err) { 108 // Doesn't exist yet 109 return creds, nil 110 } 111 log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename) 112 } 113 if err = json.Unmarshal(raw, &creds); err != nil { 114 log.Warn("Failed to unmarshal encrypted storage", "err", err, "file", s.filename) 115 return nil, err 116 } 117 return creds, nil 118 } 119 120 // writeEncryptedStorage write the file with encrypted creds 121 func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCredential) error { 122 raw, err := json.Marshal(creds) 123 if err != nil { 124 return err 125 } 126 if err = ioutil.WriteFile(s.filename, raw, 0600); err != nil { 127 return err 128 } 129 return nil 130 } 131 132 func encrypt(key []byte, plaintext []byte) ([]byte, []byte, error) { 133 block, err := aes.NewCipher(key) 134 if err != nil { 135 return nil, nil, err 136 } 137 aesgcm, err := cipher.NewGCM(block) 138 nonce := make([]byte, aesgcm.NonceSize()) 139 if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 140 return nil, nil, err 141 } 142 if err != nil { 143 return nil, nil, err 144 } 145 ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) 146 return ciphertext, nonce, nil 147 } 148 149 func decrypt(key []byte, nonce []byte, ciphertext []byte) ([]byte, error) { 150 block, err := aes.NewCipher(key) 151 if err != nil { 152 return nil, err 153 } 154 aesgcm, err := cipher.NewGCM(block) 155 if err != nil { 156 return nil, err 157 } 158 plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) 159 if err != nil { 160 return nil, err 161 } 162 return plaintext, nil 163 }