github.com/xfond/eth-implementation@v1.8.9-0.20180514135602-f6bc65fc6811/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 crand "crypto/rand" 32 "crypto/sha256" 33 "encoding/hex" 34 "encoding/json" 35 "fmt" 36 "io/ioutil" 37 "path/filepath" 38 39 "github.com/ethereum/go-ethereum/common" 40 "github.com/ethereum/go-ethereum/common/math" 41 "github.com/ethereum/go-ethereum/crypto" 42 "github.com/ethereum/go-ethereum/crypto/randentropy" 43 "github.com/pborman/uuid" 44 "golang.org/x/crypto/pbkdf2" 45 "golang.org/x/crypto/scrypt" 46 ) 47 48 const ( 49 keyHeaderKDF = "scrypt" 50 51 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 52 // memory and taking approximately 1s CPU time on a modern processor. 53 StandardScryptN = 1 << 18 54 55 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 56 // memory and taking approximately 1s CPU time on a modern processor. 57 StandardScryptP = 1 58 59 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 60 // memory and taking approximately 100ms CPU time on a modern processor. 61 LightScryptN = 1 << 12 62 63 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 64 // memory and taking approximately 100ms CPU time on a modern processor. 65 LightScryptP = 6 66 67 scryptR = 8 68 scryptDKLen = 32 69 ) 70 71 type keyStorePassphrase struct { 72 keysDirPath string 73 scryptN int 74 scryptP int 75 } 76 77 func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { 78 // Load the key from the keystore and decrypt its contents 79 keyjson, err := ioutil.ReadFile(filename) 80 if err != nil { 81 return nil, err 82 } 83 key, err := DecryptKey(keyjson, auth) 84 if err != nil { 85 return nil, err 86 } 87 // Make sure we're really operating on the requested key (no swap attacks) 88 if key.Address != addr { 89 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) 90 } 91 return key, nil 92 } 93 94 // StoreKey generates a key, encrypts with 'auth' and stores in the given directory 95 func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) { 96 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, crand.Reader, auth) 97 return a.Address, err 98 } 99 100 func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { 101 keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 102 if err != nil { 103 return err 104 } 105 return writeKeyFile(filename, keyjson) 106 } 107 108 func (ks keyStorePassphrase) JoinPath(filename string) string { 109 if filepath.IsAbs(filename) { 110 return filename 111 } 112 return filepath.Join(ks.keysDirPath, filename) 113 } 114 115 // EncryptKey encrypts a key using the specified scrypt parameters into a json 116 // blob that can be decrypted later on. 117 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 118 authArray := []byte(auth) 119 salt := randentropy.GetEntropyCSPRNG(32) 120 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 121 if err != nil { 122 return nil, err 123 } 124 encryptKey := derivedKey[:16] 125 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 126 127 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 128 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 129 if err != nil { 130 return nil, err 131 } 132 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 133 134 scryptParamsJSON := make(map[string]interface{}, 5) 135 scryptParamsJSON["n"] = scryptN 136 scryptParamsJSON["r"] = scryptR 137 scryptParamsJSON["p"] = scryptP 138 scryptParamsJSON["dklen"] = scryptDKLen 139 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 140 141 cipherParamsJSON := cipherparamsJSON{ 142 IV: hex.EncodeToString(iv), 143 } 144 145 cryptoStruct := cryptoJSON{ 146 Cipher: "aes-128-ctr", 147 CipherText: hex.EncodeToString(cipherText), 148 CipherParams: cipherParamsJSON, 149 KDF: keyHeaderKDF, 150 KDFParams: scryptParamsJSON, 151 MAC: hex.EncodeToString(mac), 152 } 153 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 154 hex.EncodeToString(key.Address[:]), 155 cryptoStruct, 156 key.Id.String(), 157 version, 158 } 159 return json.Marshal(encryptedKeyJSONV3) 160 } 161 162 // DecryptKey decrypts a key from a json blob, returning the private key itself. 163 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 164 // Parse the json into a simple map to fetch the key version 165 m := make(map[string]interface{}) 166 if err := json.Unmarshal(keyjson, &m); err != nil { 167 return nil, err 168 } 169 // Depending on the version try to parse one way or another 170 var ( 171 keyBytes, keyId []byte 172 err error 173 ) 174 if version, ok := m["version"].(string); ok && version == "1" { 175 k := new(encryptedKeyJSONV1) 176 if err := json.Unmarshal(keyjson, k); err != nil { 177 return nil, err 178 } 179 keyBytes, keyId, err = decryptKeyV1(k, auth) 180 } else { 181 k := new(encryptedKeyJSONV3) 182 if err := json.Unmarshal(keyjson, k); err != nil { 183 return nil, err 184 } 185 keyBytes, keyId, err = decryptKeyV3(k, auth) 186 } 187 // Handle any decryption errors and return the key 188 if err != nil { 189 return nil, err 190 } 191 key := crypto.ToECDSAUnsafe(keyBytes) 192 193 return &Key{ 194 Id: uuid.UUID(keyId), 195 Address: crypto.PubkeyToAddress(key.PublicKey), 196 PrivateKey: key, 197 }, nil 198 } 199 200 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 201 if keyProtected.Version != version { 202 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 203 } 204 205 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 206 return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 207 } 208 209 keyId = uuid.Parse(keyProtected.Id) 210 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 211 if err != nil { 212 return nil, nil, err 213 } 214 215 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 216 if err != nil { 217 return nil, nil, err 218 } 219 220 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 221 if err != nil { 222 return nil, nil, err 223 } 224 225 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 226 if err != nil { 227 return nil, nil, err 228 } 229 230 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 231 if !bytes.Equal(calculatedMAC, mac) { 232 return nil, nil, ErrDecrypt 233 } 234 235 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 236 if err != nil { 237 return nil, nil, err 238 } 239 return plainText, keyId, err 240 } 241 242 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 243 keyId = uuid.Parse(keyProtected.Id) 244 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 245 if err != nil { 246 return nil, nil, err 247 } 248 249 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 250 if err != nil { 251 return nil, nil, err 252 } 253 254 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 255 if err != nil { 256 return nil, nil, err 257 } 258 259 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 260 if err != nil { 261 return nil, nil, err 262 } 263 264 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 265 if !bytes.Equal(calculatedMAC, mac) { 266 return nil, nil, ErrDecrypt 267 } 268 269 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 270 if err != nil { 271 return nil, nil, err 272 } 273 return plainText, keyId, err 274 } 275 276 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 277 authArray := []byte(auth) 278 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 279 if err != nil { 280 return nil, err 281 } 282 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 283 284 if cryptoJSON.KDF == keyHeaderKDF { 285 n := ensureInt(cryptoJSON.KDFParams["n"]) 286 r := ensureInt(cryptoJSON.KDFParams["r"]) 287 p := ensureInt(cryptoJSON.KDFParams["p"]) 288 return scrypt.Key(authArray, salt, n, r, p, dkLen) 289 290 } else if cryptoJSON.KDF == "pbkdf2" { 291 c := ensureInt(cryptoJSON.KDFParams["c"]) 292 prf := cryptoJSON.KDFParams["prf"].(string) 293 if prf != "hmac-sha256" { 294 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 295 } 296 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 297 return key, nil 298 } 299 300 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 301 } 302 303 // TODO: can we do without this when unmarshalling dynamic JSON? 304 // why do integers in KDF params end up as float64 and not int after 305 // unmarshal? 306 func ensureInt(x interface{}) int { 307 res, ok := x.(int) 308 if !ok { 309 res = int(x.(float64)) 310 } 311 return res 312 }