github.com/prysmaticlabs/prysm@v1.4.4/shared/keystore/keystore.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // Modified by Prysmatic Labs 2018 5 // 6 // The go-ethereum library is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Lesser General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 // 11 // The go-ethereum library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Lesser General Public License for more details. 15 // 16 // You should have received a copy of the GNU Lesser General Public License 17 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 18 19 package keystore 20 21 import ( 22 "bytes" 23 "crypto/aes" 24 "crypto/rand" 25 "encoding/hex" 26 "encoding/json" 27 "errors" 28 "fmt" 29 "io" 30 "io/ioutil" 31 "os" 32 "path/filepath" 33 "strings" 34 35 "github.com/minio/sha256-simd" 36 "github.com/pborman/uuid" 37 "github.com/prysmaticlabs/prysm/shared/bls" 38 log "github.com/sirupsen/logrus" 39 "golang.org/x/crypto/pbkdf2" 40 "golang.org/x/crypto/scrypt" 41 ) 42 43 var ( 44 // ErrDecrypt is the standard error message when decryption is a failure. 45 ErrDecrypt = errors.New("could not decrypt key with given passphrase") 46 ) 47 48 // Keystore defines a keystore with a directory path and scrypt values. 49 type Keystore struct { 50 keysDirPath string 51 scryptN int 52 scryptP int 53 } 54 55 // GetKey from file using the filename path and a decryption password. 56 func (ks Keystore) GetKey(filename, password string) (*Key, error) { 57 // Load the key from the keystore and decrypt its contents 58 // #nosec G304 59 keyJSON, err := ioutil.ReadFile(filename) 60 if err != nil { 61 return nil, err 62 } 63 return DecryptKey(keyJSON, password) 64 } 65 66 // GetKeys from directory using the prefix to filter relevant files 67 // and a decryption password. 68 func (ks Keystore) GetKeys(directory, filePrefix, password string, warnOnFail bool) (map[string]*Key, error) { 69 // Load the key from the keystore and decrypt its contents 70 // #nosec G304 71 files, err := ioutil.ReadDir(directory) 72 if err != nil { 73 return nil, err 74 } 75 keys := make(map[string]*Key) 76 for _, f := range files { 77 n := f.Name() 78 filePath := filepath.Join(directory, n) 79 filePath = filepath.Clean(filePath) 80 if f.Mode()&os.ModeSymlink == os.ModeSymlink { 81 if targetFilePath, err := filepath.EvalSymlinks(filePath); err == nil { 82 filePath = targetFilePath 83 // Override link stats with target file's stats. 84 if f, err = os.Stat(filePath); err != nil { 85 return nil, err 86 } 87 } 88 } 89 cp := strings.Contains(n, strings.TrimPrefix(filePrefix, "/")) 90 if f.Mode().IsRegular() && cp { 91 // #nosec G304 92 keyJSON, err := ioutil.ReadFile(filePath) 93 if err != nil { 94 return nil, err 95 } 96 key, err := DecryptKey(keyJSON, password) 97 if err != nil { 98 if warnOnFail { 99 log.WithError(err).WithField("keyfile", string(keyJSON)).Warn("Failed to decrypt key") 100 } 101 continue 102 } 103 keys[hex.EncodeToString(key.PublicKey.Marshal())] = key 104 } 105 } 106 return keys, nil 107 } 108 109 // StoreKey in filepath and encrypt it with a password. 110 func (ks Keystore) StoreKey(filename string, key *Key, auth string) error { 111 keyJSON, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 112 if err != nil { 113 return err 114 } 115 return writeKeyFile(filename, keyJSON) 116 } 117 118 // JoinPath joins the filename with the keystore directory path. 119 func (ks Keystore) JoinPath(filename string) string { 120 if filepath.IsAbs(filename) { 121 return filename 122 } 123 return filepath.Join(ks.keysDirPath, filename) 124 } 125 126 // EncryptKey encrypts a key using the specified scrypt parameters into a JSON 127 // blob that can be decrypted later on. 128 func EncryptKey(key *Key, password string, scryptN, scryptP int) ([]byte, error) { 129 authArray := []byte(password) 130 salt := make([]byte, 32) 131 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 132 panic("reading from crypto/rand failed: " + err.Error()) 133 } 134 135 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 136 if err != nil { 137 return nil, err 138 } 139 140 encryptKey := derivedKey[:16] 141 keyBytes := key.SecretKey.Marshal() 142 143 iv := make([]byte, aes.BlockSize) // 16 144 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 145 return nil, errors.New("reading from crypto/rand failed: " + err.Error()) 146 } 147 148 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 149 if err != nil { 150 return nil, err 151 } 152 153 mac := Keccak256(derivedKey[16:32], cipherText) 154 155 scryptParamsJSON := make(map[string]interface{}, 5) 156 scryptParamsJSON["n"] = scryptN 157 scryptParamsJSON["r"] = scryptR 158 scryptParamsJSON["p"] = scryptP 159 scryptParamsJSON["dklen"] = scryptDKLen 160 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 161 162 cipherParamsJSON := cipherparamsJSON{ 163 IV: hex.EncodeToString(iv), 164 } 165 166 cryptoStruct := cryptoJSON{ 167 Cipher: "aes-128-ctr", 168 CipherText: hex.EncodeToString(cipherText), 169 CipherParams: cipherParamsJSON, 170 KDF: keyHeaderKDF, 171 KDFParams: scryptParamsJSON, 172 MAC: hex.EncodeToString(mac), 173 } 174 encryptedJSON := encryptedKeyJSON{ 175 hex.EncodeToString(key.PublicKey.Marshal()), 176 cryptoStruct, 177 key.ID.String(), 178 } 179 return json.Marshal(encryptedJSON) 180 } 181 182 // DecryptKey decrypts a key from a JSON blob, returning the private key itself. 183 func DecryptKey(keyJSON []byte, password string) (*Key, error) { 184 var keyBytes, keyID []byte 185 var err error 186 187 k := new(encryptedKeyJSON) 188 if err := json.Unmarshal(keyJSON, k); err != nil { 189 return nil, err 190 } 191 192 keyBytes, keyID, err = decryptKeyJSON(k, password) 193 // Handle any decryption errors and return the key 194 if err != nil { 195 return nil, err 196 } 197 198 secretKey, err := bls.SecretKeyFromBytes(keyBytes) 199 if err != nil { 200 return nil, err 201 } 202 203 return &Key{ 204 ID: keyID, 205 PublicKey: secretKey.PublicKey(), 206 SecretKey: secretKey, 207 }, nil 208 } 209 210 func decryptKeyJSON(keyProtected *encryptedKeyJSON, auth string) (keyBytes, keyID []byte, err error) { 211 keyID = uuid.Parse(keyProtected.ID) 212 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 213 return nil, nil, fmt.Errorf("cipher not supported: %v", keyProtected.Crypto.Cipher) 214 } 215 216 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 217 if err != nil { 218 return nil, nil, err 219 } 220 221 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 222 if err != nil { 223 return nil, nil, err 224 } 225 226 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 227 if err != nil { 228 return nil, nil, err 229 } 230 231 derivedKey, err := kdfKey(keyProtected.Crypto, auth) 232 if err != nil { 233 return nil, nil, err 234 } 235 236 calculatedMAC := Keccak256(derivedKey[16:32], cipherText) 237 if !bytes.Equal(calculatedMAC, mac) { 238 return nil, nil, ErrDecrypt 239 } 240 241 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 242 if err != nil { 243 return nil, nil, err 244 } 245 return plainText, keyID, nil 246 } 247 248 func kdfKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 249 authArray := []byte(auth) 250 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 251 if err != nil { 252 return nil, err 253 } 254 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 255 256 if cryptoJSON.KDF == keyHeaderKDF { 257 n := ensureInt(cryptoJSON.KDFParams["n"]) 258 r := ensureInt(cryptoJSON.KDFParams["r"]) 259 p := ensureInt(cryptoJSON.KDFParams["p"]) 260 return scrypt.Key(authArray, salt, n, r, p, dkLen) 261 262 } else if cryptoJSON.KDF == "pbkdf2" { 263 c := ensureInt(cryptoJSON.KDFParams["c"]) 264 prf, ok := cryptoJSON.KDFParams["prf"].(string) 265 if !ok { 266 return nil, errors.New("KDFParams are not type string") 267 } 268 if prf != "hmac-sha256" { 269 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 270 } 271 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 272 return key, nil 273 } 274 275 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 276 }