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