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