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