github.com/pslzym/go-ethereum@v1.8.17-0.20180926104442-4b6824e07b1b/accounts/keystore/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 "io/ioutil" 38 "os" 39 "path/filepath" 40 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/pborman/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 := ioutil.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) (common.Address, error) { 101 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth) 102 return a.Address, 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 return fmt.Errorf(msg, tmpName, err) 126 } 127 } 128 return os.Rename(tmpName, filename) 129 } 130 131 func (ks keyStorePassphrase) JoinPath(filename string) string { 132 if filepath.IsAbs(filename) { 133 return filename 134 } 135 return filepath.Join(ks.keysDirPath, filename) 136 } 137 138 // EncryptKey encrypts a key using the specified scrypt parameters into a json 139 // blob that can be decrypted later on. 140 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 141 authArray := []byte(auth) 142 143 salt := make([]byte, 32) 144 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 145 panic("reading from crypto/rand failed: " + err.Error()) 146 } 147 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 148 if err != nil { 149 return nil, err 150 } 151 encryptKey := derivedKey[:16] 152 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 153 154 iv := make([]byte, aes.BlockSize) // 16 155 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 156 panic("reading from crypto/rand failed: " + err.Error()) 157 } 158 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 159 if err != nil { 160 return nil, err 161 } 162 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 163 164 scryptParamsJSON := make(map[string]interface{}, 5) 165 scryptParamsJSON["n"] = scryptN 166 scryptParamsJSON["r"] = scryptR 167 scryptParamsJSON["p"] = scryptP 168 scryptParamsJSON["dklen"] = scryptDKLen 169 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 170 171 cipherParamsJSON := cipherparamsJSON{ 172 IV: hex.EncodeToString(iv), 173 } 174 175 cryptoStruct := cryptoJSON{ 176 Cipher: "aes-128-ctr", 177 CipherText: hex.EncodeToString(cipherText), 178 CipherParams: cipherParamsJSON, 179 KDF: keyHeaderKDF, 180 KDFParams: scryptParamsJSON, 181 MAC: hex.EncodeToString(mac), 182 } 183 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 184 hex.EncodeToString(key.Address[:]), 185 cryptoStruct, 186 key.Id.String(), 187 version, 188 } 189 return json.Marshal(encryptedKeyJSONV3) 190 } 191 192 // DecryptKey decrypts a key from a json blob, returning the private key itself. 193 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 194 // Parse the json into a simple map to fetch the key version 195 m := make(map[string]interface{}) 196 if err := json.Unmarshal(keyjson, &m); err != nil { 197 return nil, err 198 } 199 // Depending on the version try to parse one way or another 200 var ( 201 keyBytes, keyId []byte 202 err error 203 ) 204 if version, ok := m["version"].(string); ok && version == "1" { 205 k := new(encryptedKeyJSONV1) 206 if err := json.Unmarshal(keyjson, k); err != nil { 207 return nil, err 208 } 209 keyBytes, keyId, err = decryptKeyV1(k, auth) 210 } else { 211 k := new(encryptedKeyJSONV3) 212 if err := json.Unmarshal(keyjson, k); err != nil { 213 return nil, err 214 } 215 keyBytes, keyId, err = decryptKeyV3(k, auth) 216 } 217 // Handle any decryption errors and return the key 218 if err != nil { 219 return nil, err 220 } 221 key := crypto.ToECDSAUnsafe(keyBytes) 222 223 return &Key{ 224 Id: uuid.UUID(keyId), 225 Address: crypto.PubkeyToAddress(key.PublicKey), 226 PrivateKey: key, 227 }, nil 228 } 229 230 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 231 if keyProtected.Version != version { 232 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 233 } 234 235 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 236 return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 237 } 238 239 keyId = uuid.Parse(keyProtected.Id) 240 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 241 if err != nil { 242 return nil, nil, err 243 } 244 245 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 246 if err != nil { 247 return nil, nil, err 248 } 249 250 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 251 if err != nil { 252 return nil, nil, err 253 } 254 255 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 256 if err != nil { 257 return nil, nil, err 258 } 259 260 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 261 if !bytes.Equal(calculatedMAC, mac) { 262 return nil, nil, ErrDecrypt 263 } 264 265 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 266 if err != nil { 267 return nil, nil, err 268 } 269 return plainText, keyId, err 270 } 271 272 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 273 keyId = uuid.Parse(keyProtected.Id) 274 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 275 if err != nil { 276 return nil, nil, err 277 } 278 279 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 280 if err != nil { 281 return nil, nil, err 282 } 283 284 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 285 if err != nil { 286 return nil, nil, err 287 } 288 289 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 290 if err != nil { 291 return nil, nil, err 292 } 293 294 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 295 if !bytes.Equal(calculatedMAC, mac) { 296 return nil, nil, ErrDecrypt 297 } 298 299 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 300 if err != nil { 301 return nil, nil, err 302 } 303 return plainText, keyId, err 304 } 305 306 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 307 authArray := []byte(auth) 308 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 309 if err != nil { 310 return nil, err 311 } 312 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 313 314 if cryptoJSON.KDF == keyHeaderKDF { 315 n := ensureInt(cryptoJSON.KDFParams["n"]) 316 r := ensureInt(cryptoJSON.KDFParams["r"]) 317 p := ensureInt(cryptoJSON.KDFParams["p"]) 318 return scrypt.Key(authArray, salt, n, r, p, dkLen) 319 320 } else if cryptoJSON.KDF == "pbkdf2" { 321 c := ensureInt(cryptoJSON.KDFParams["c"]) 322 prf := cryptoJSON.KDFParams["prf"].(string) 323 if prf != "hmac-sha256" { 324 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 325 } 326 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 327 return key, nil 328 } 329 330 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 331 } 332 333 // TODO: can we do without this when unmarshalling dynamic JSON? 334 // why do integers in KDF params end up as float64 and not int after 335 // unmarshal? 336 func ensureInt(x interface{}) int { 337 res, ok := x.(int) 338 if !ok { 339 res = int(x.(float64)) 340 } 341 return res 342 }