github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/accounts/keystore/passphrase.go (about) 1 // Copyright 2014 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 /* 18 19 This key store behaves as KeyStorePlain with the difference that 20 the private key is encrypted and on disk uses another JSON encoding. 21 22 The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition 23 24 */ 25 26 package keystore 27 28 import ( 29 "bytes" 30 "crypto/aes" 31 "crypto/rand" 32 "crypto/sha256" 33 "encoding/hex" 34 "encoding/json" 35 "fmt" 36 "io" 37 "os" 38 "path/filepath" 39 40 "github.com/ethereum/go-ethereum/accounts" 41 "github.com/ethereum/go-ethereum/common" 42 "github.com/ethereum/go-ethereum/common/math" 43 "github.com/ethereum/go-ethereum/crypto" 44 "github.com/google/uuid" 45 "golang.org/x/crypto/pbkdf2" 46 "golang.org/x/crypto/scrypt" 47 ) 48 49 const ( 50 keyHeaderKDF = "scrypt" 51 52 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 53 // memory and taking approximately 1s CPU time on a modern processor. 54 StandardScryptN = 1 << 18 55 56 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 57 // memory and taking approximately 1s CPU time on a modern processor. 58 StandardScryptP = 1 59 60 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 61 // memory and taking approximately 100ms CPU time on a modern processor. 62 LightScryptN = 1 << 12 63 64 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 65 // memory and taking approximately 100ms CPU time on a modern processor. 66 LightScryptP = 6 67 68 scryptR = 8 69 scryptDKLen = 32 70 ) 71 72 type keyStorePassphrase struct { 73 keysDirPath string 74 scryptN int 75 scryptP int 76 // skipKeyFileVerification disables the security-feature which does 77 // reads and decrypts any newly created keyfiles. This should be 'false' in all 78 // cases except tests -- setting this to 'true' is not recommended. 79 skipKeyFileVerification bool 80 } 81 82 func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { 83 // Load the key from the keystore and decrypt its contents 84 keyjson, err := os.ReadFile(filename) 85 if err != nil { 86 return nil, err 87 } 88 key, err := DecryptKey(keyjson, auth) 89 if err != nil { 90 return nil, err 91 } 92 // Make sure we're really operating on the requested key (no swap attacks) 93 if key.Address != addr { 94 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) 95 } 96 return key, nil 97 } 98 99 // StoreKey generates a key, encrypts with 'auth' and stores in the given directory 100 func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) { 101 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth) 102 return a, err 103 } 104 105 func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { 106 keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 107 if err != nil { 108 return err 109 } 110 // Write into temporary file 111 tmpName, err := writeTemporaryKeyFile(filename, keyjson) 112 if err != nil { 113 return err 114 } 115 if !ks.skipKeyFileVerification { 116 // Verify that we can decrypt the file with the given password. 117 _, err = ks.GetKey(key.Address, tmpName, auth) 118 if err != nil { 119 msg := "An error was encountered when saving and verifying the keystore file. \n" + 120 "This indicates that the keystore is corrupted. \n" + 121 "The corrupted file is stored at \n%v\n" + 122 "Please file a ticket at:\n\n" + 123 "https://github.com/ethereum/go-ethereum/issues." + 124 "The error was : %s" 125 //lint:ignore ST1005 This is a message for the user 126 return fmt.Errorf(msg, tmpName, err) 127 } 128 } 129 return os.Rename(tmpName, filename) 130 } 131 132 func (ks keyStorePassphrase) JoinPath(filename string) string { 133 if filepath.IsAbs(filename) { 134 return filename 135 } 136 return filepath.Join(ks.keysDirPath, filename) 137 } 138 139 // EncryptDataV3 encrypts the data given as 'data' with the password 'auth'. 140 func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { 141 salt := make([]byte, 32) 142 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 143 panic("reading from crypto/rand failed: " + err.Error()) 144 } 145 derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) 146 if err != nil { 147 return CryptoJSON{}, err 148 } 149 encryptKey := derivedKey[:16] 150 151 iv := make([]byte, aes.BlockSize) // 16 152 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 153 panic("reading from crypto/rand failed: " + err.Error()) 154 } 155 cipherText, err := aesCTRXOR(encryptKey, data, iv) 156 if err != nil { 157 return CryptoJSON{}, err 158 } 159 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 160 161 scryptParamsJSON := make(map[string]interface{}, 5) 162 scryptParamsJSON["n"] = scryptN 163 scryptParamsJSON["r"] = scryptR 164 scryptParamsJSON["p"] = scryptP 165 scryptParamsJSON["dklen"] = scryptDKLen 166 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 167 cipherParamsJSON := cipherparamsJSON{ 168 IV: hex.EncodeToString(iv), 169 } 170 171 cryptoStruct := CryptoJSON{ 172 Cipher: "aes-128-ctr", 173 CipherText: hex.EncodeToString(cipherText), 174 CipherParams: cipherParamsJSON, 175 KDF: keyHeaderKDF, 176 KDFParams: scryptParamsJSON, 177 MAC: hex.EncodeToString(mac), 178 } 179 return cryptoStruct, nil 180 } 181 182 // EncryptKey encrypts a key using the specified scrypt parameters into a json 183 // blob that can be decrypted later on. 184 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 185 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 186 cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) 187 if err != nil { 188 return nil, err 189 } 190 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 191 hex.EncodeToString(key.Address[:]), 192 cryptoStruct, 193 key.Id.String(), 194 version, 195 } 196 return json.Marshal(encryptedKeyJSONV3) 197 } 198 199 // DecryptKey decrypts a key from a json blob, returning the private key itself. 200 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 201 // Parse the json into a simple map to fetch the key version 202 m := make(map[string]interface{}) 203 if err := json.Unmarshal(keyjson, &m); err != nil { 204 return nil, err 205 } 206 // Depending on the version try to parse one way or another 207 var ( 208 keyBytes, keyId []byte 209 err error 210 ) 211 if version, ok := m["version"].(string); ok && version == "1" { 212 k := new(encryptedKeyJSONV1) 213 if err := json.Unmarshal(keyjson, k); err != nil { 214 return nil, err 215 } 216 keyBytes, keyId, err = decryptKeyV1(k, auth) 217 } else { 218 k := new(encryptedKeyJSONV3) 219 if err := json.Unmarshal(keyjson, k); err != nil { 220 return nil, err 221 } 222 keyBytes, keyId, err = decryptKeyV3(k, auth) 223 } 224 // Handle any decryption errors and return the key 225 if err != nil { 226 return nil, err 227 } 228 key, err := crypto.ToECDSA(keyBytes) 229 if err != nil { 230 return nil, fmt.Errorf("invalid key: %w", err) 231 } 232 id, err := uuid.FromBytes(keyId) 233 if err != nil { 234 return nil, fmt.Errorf("invalid UUID: %w", err) 235 } 236 return &Key{ 237 Id: id, 238 Address: crypto.PubkeyToAddress(key.PublicKey), 239 PrivateKey: key, 240 }, nil 241 } 242 243 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 244 if cryptoJson.Cipher != "aes-128-ctr" { 245 return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher) 246 } 247 mac, err := hex.DecodeString(cryptoJson.MAC) 248 if err != nil { 249 return nil, err 250 } 251 252 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 253 if err != nil { 254 return nil, err 255 } 256 257 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 258 if err != nil { 259 return nil, err 260 } 261 262 derivedKey, err := getKDFKey(cryptoJson, auth) 263 if err != nil { 264 return nil, err 265 } 266 267 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 268 if !bytes.Equal(calculatedMAC, mac) { 269 return nil, ErrDecrypt 270 } 271 272 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 273 if err != nil { 274 return nil, err 275 } 276 return plainText, err 277 } 278 279 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 280 if keyProtected.Version != version { 281 return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) 282 } 283 keyUUID, err := uuid.Parse(keyProtected.Id) 284 if err != nil { 285 return nil, nil, err 286 } 287 keyId = keyUUID[:] 288 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 289 if err != nil { 290 return nil, nil, err 291 } 292 return plainText, keyId, err 293 } 294 295 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 296 keyUUID, err := uuid.Parse(keyProtected.Id) 297 if err != nil { 298 return nil, nil, err 299 } 300 keyId = keyUUID[:] 301 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 302 if err != nil { 303 return nil, nil, err 304 } 305 306 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 307 if err != nil { 308 return nil, nil, err 309 } 310 311 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 312 if err != nil { 313 return nil, nil, err 314 } 315 316 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 317 if err != nil { 318 return nil, nil, err 319 } 320 321 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 322 if !bytes.Equal(calculatedMAC, mac) { 323 return nil, nil, ErrDecrypt 324 } 325 326 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 327 if err != nil { 328 return nil, nil, err 329 } 330 return plainText, keyId, err 331 } 332 333 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 334 authArray := []byte(auth) 335 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 336 if err != nil { 337 return nil, err 338 } 339 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 340 341 if cryptoJSON.KDF == keyHeaderKDF { 342 n := ensureInt(cryptoJSON.KDFParams["n"]) 343 r := ensureInt(cryptoJSON.KDFParams["r"]) 344 p := ensureInt(cryptoJSON.KDFParams["p"]) 345 return scrypt.Key(authArray, salt, n, r, p, dkLen) 346 } else if cryptoJSON.KDF == "pbkdf2" { 347 c := ensureInt(cryptoJSON.KDFParams["c"]) 348 prf := cryptoJSON.KDFParams["prf"].(string) 349 if prf != "hmac-sha256" { 350 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 351 } 352 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 353 return key, nil 354 } 355 356 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 357 } 358 359 // TODO: can we do without this when unmarshalling dynamic JSON? 360 // why do integers in KDF params end up as float64 and not int after 361 // unmarshal? 362 func ensureInt(x interface{}) int { 363 res, ok := x.(int) 364 if !ok { 365 res = int(x.(float64)) 366 } 367 return res 368 }