github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/keystore.go (about) 1 // Copyright 2015 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 package accounts 18 19 import ( 20 "bytes" 21 "crypto/aes" 22 "crypto/ecdsa" 23 "crypto/rand" 24 "crypto/sha256" 25 "encoding/hex" 26 "encoding/json" 27 "fmt" 28 "io/ioutil" 29 "os" 30 "path/filepath" 31 "time" 32 33 "golang.org/x/crypto/pbkdf2" 34 "golang.org/x/crypto/scrypt" 35 36 "github.com/ethereumproject/go-ethereum/common" 37 "github.com/ethereumproject/go-ethereum/crypto" 38 "github.com/ethereumproject/go-ethereum/crypto/randentropy" 39 "github.com/ethereumproject/go-ethereum/crypto/secp256k1" 40 ) 41 42 type key struct { 43 UUID string 44 // to simplify lookups we also store the address 45 Address common.Address 46 // we only store privkey as pubkey/address can be derived from it 47 // privkey in this struct is always in plaintext 48 PrivateKey *ecdsa.PrivateKey 49 } 50 51 type plainKeyJSON struct { 52 ID string `json:"id"` 53 Address string `json:"address"` 54 PrivateKey string `json:"privatekey"` 55 Version int `json:"version"` 56 } 57 58 func (k *key) MarshalJSON() (j []byte, err error) { 59 jStruct := plainKeyJSON{ 60 ID: k.UUID, 61 Address: hex.EncodeToString(k.Address[:]), 62 PrivateKey: hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), 63 Version: 3, 64 } 65 j, err = json.Marshal(jStruct) 66 return j, err 67 } 68 69 func (k *key) UnmarshalJSON(j []byte) (err error) { 70 keyJSON := new(plainKeyJSON) 71 err = json.Unmarshal(j, &keyJSON) 72 if err != nil { 73 return err 74 } 75 76 k.UUID = keyJSON.ID 77 addr, err := hex.DecodeString(keyJSON.Address) 78 if err != nil { 79 return err 80 } 81 k.Address = common.BytesToAddress(addr) 82 83 privkey, err := hex.DecodeString(keyJSON.PrivateKey) 84 if err != nil { 85 return err 86 } 87 k.PrivateKey = crypto.ToECDSA(privkey) 88 89 return nil 90 } 91 92 // newKeyUUID returns an identifier for key. 93 func newKeyUUID() (string, error) { 94 var u [16]byte 95 if _, err := rand.Read(u[:]); err != nil { 96 return "", err 97 } 98 99 u[6] = (u[6] & 0x0f) | 0x40 // version 4 100 u[8] = (u[8] & 0x3f) | 0x80 // variant 10 101 102 return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]), nil 103 } 104 105 func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) (*key, error) { 106 id, err := newKeyUUID() 107 if err != nil { 108 return nil, err 109 } 110 111 return &key{ 112 UUID: id, 113 Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), 114 PrivateKey: privateKeyECDSA, 115 }, nil 116 } 117 118 func storeNewKey(store *keyStore, secret string) (*key, Account, error) { 119 privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) 120 if err != nil { 121 return nil, Account{}, err 122 } 123 key, err := newKeyFromECDSA(privateKeyECDSA) 124 if err != nil { 125 return nil, Account{}, err 126 } 127 128 file, err := store.Insert(key, secret) 129 if err != nil { 130 return nil, Account{}, err 131 } 132 133 return key, Account{Address: key.Address, File: file}, err 134 } 135 136 type keyStore struct { 137 baseDir string // absolute filepath to default/flagged value for eg datadir/mainnet/keystore 138 scryptN int 139 scryptP int 140 } 141 142 func newKeyStore(dir string, scryptN, scryptP int) (*keyStore, error) { 143 if !filepath.IsAbs(dir) { 144 var err error 145 dir, err = filepath.Abs(dir) 146 if err != nil { 147 return nil, err 148 } 149 } 150 if err := os.MkdirAll(dir, 0700); err != nil { 151 return nil, err 152 } 153 154 return &keyStore{ 155 baseDir: dir, 156 scryptN: scryptN, 157 scryptP: scryptP, 158 }, nil 159 } 160 161 func (store *keyStore) DecryptKey(data []byte, secret string) (*key, error) { 162 key, err := decryptKey(data, secret) 163 if err != nil { 164 return nil, err 165 } 166 return key, nil 167 } 168 169 func (store *keyStore) Lookup(file string, secret string) (*key, error) { 170 if !filepath.IsAbs(file) { 171 file = filepath.Join(store.baseDir, file) 172 } 173 174 data, err := ioutil.ReadFile(file) 175 if err != nil { 176 return nil, err 177 } 178 179 key, err := decryptKey(data, secret) 180 if err != nil { 181 return nil, err 182 } 183 184 return key, nil 185 } 186 187 func (store *keyStore) Insert(key *key, secret string) (file string, err error) { 188 data, err := encryptKey(key, secret, store.scryptN, store.scryptP) 189 if err != nil { 190 return "", err 191 } 192 193 timestamp := time.Now().UTC().Format("2006-01-02T15-04-05.999999999") 194 file = fmt.Sprintf("UTC--%sZ--%x", timestamp, key.Address[:]) 195 file = filepath.Join(store.baseDir, file) 196 197 if err := writeKeyFile(file, data); err != nil { 198 return "", err 199 } 200 return file, nil 201 } 202 203 func (store keyStore) Update(file string, key *key, secret string) error { 204 data, err := encryptKey(key, secret, store.scryptN, store.scryptP) 205 if err != nil { 206 return err 207 } 208 209 if !filepath.IsAbs(file) { 210 file = filepath.Join(store.baseDir, file) 211 } 212 return writeKeyFile(file, data) 213 } 214 215 // https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition 216 217 // web3v3 is a version 3 encrypted key store record. 218 type web3v3 struct { 219 ID string `json:"id"` 220 Address string `json:"address"` 221 Crypto cryptoJSON `json:"crypto"` 222 Version int `json:"version"` 223 } 224 225 // web3v3 is a version 1 encrypted key store record. 226 type web3v1 struct { 227 ID string `json:"id"` 228 Address string `json:"address"` 229 Crypto cryptoJSON `json:"crypto"` 230 Version string `json:"version"` 231 } 232 233 type cryptoJSON struct { 234 Cipher string `json:"cipher"` 235 CipherText string `json:"ciphertext"` 236 CipherParams cipherparamsJSON `json:"cipherparams"` 237 KDF string `json:"kdf"` 238 KDFParams map[string]interface{} `json:"kdfparams"` 239 MAC string `json:"mac"` 240 } 241 242 type cipherparamsJSON struct { 243 IV string `json:"iv"` 244 } 245 246 // encryptKey encrypts key as version 3. 247 func encryptKey(key *key, secret string, scryptN, scryptP int) ([]byte, error) { 248 salt := randentropy.GetEntropyCSPRNG(32) 249 derivedKey, err := scrypt.Key([]byte(secret), salt, scryptN, scryptR, scryptP, scryptDKLen) 250 if err != nil { 251 return nil, err 252 } 253 encryptKey := derivedKey[:16] 254 keyBytes := crypto.FromECDSA(key.PrivateKey) 255 256 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 257 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 258 if err != nil { 259 return nil, err 260 } 261 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 262 263 return json.Marshal(web3v3{ 264 ID: key.UUID, 265 Address: hex.EncodeToString(key.Address[:]), 266 Crypto: cryptoJSON{ 267 Cipher: "aes-128-ctr", 268 CipherText: hex.EncodeToString(cipherText), 269 CipherParams: cipherparamsJSON{ 270 IV: hex.EncodeToString(iv), 271 }, 272 KDF: "scrypt", 273 KDFParams: map[string]interface{}{ 274 "n": scryptN, 275 "r": scryptR, 276 "p": scryptP, 277 "dklen": scryptDKLen, 278 "salt": hex.EncodeToString(salt), 279 }, 280 MAC: hex.EncodeToString(mac), 281 }, 282 Version: 3, 283 }) 284 } 285 286 // Web3PrivateKey decrypts the record with secret and returns the private key. 287 func Web3PrivateKey(web3JSON []byte, secret string) (*ecdsa.PrivateKey, error) { 288 k, err := decryptKey(web3JSON, secret) 289 if err != nil { 290 return nil, err 291 } 292 return k.PrivateKey, nil 293 } 294 295 // decryptKey decrypts a key from a JSON blob, returning the private key itself. 296 func decryptKey(web3JSON []byte, secret string) (*key, error) { 297 // Parse the JSON into a simple map to fetch the key version 298 m := make(map[string]interface{}) 299 if err := json.Unmarshal(web3JSON, &m); err != nil { 300 return nil, err 301 } 302 303 // Depending on the version try to parse one way or another 304 var ( 305 keyBytes []byte 306 keyUUID string 307 ) 308 if version, ok := m["version"].(string); ok && version == "1" { 309 w := new(web3v1) 310 if err := json.Unmarshal(web3JSON, w); err != nil { 311 return nil, err 312 } 313 314 keyUUID = w.ID 315 316 var err error 317 keyBytes, err = decryptKeyV1(w, secret) 318 if err != nil { 319 return nil, err 320 } 321 } else { 322 w := new(web3v3) 323 if err := json.Unmarshal(web3JSON, w); err != nil { 324 return nil, err 325 } 326 if w.Version != 3 { 327 return nil, fmt.Errorf("unsupported Web3 version: %v", version) 328 } 329 330 keyUUID = w.ID 331 332 var err error 333 keyBytes, err = decryptKeyV3(w, secret) 334 if err != nil { 335 return nil, err 336 } 337 } 338 339 k := crypto.ToECDSA(keyBytes) 340 return &key{ 341 UUID: keyUUID, 342 Address: crypto.PubkeyToAddress(k.PublicKey), 343 PrivateKey: k, 344 }, nil 345 } 346 347 func decryptKeyV3(keyProtected *web3v3, secret string) (keyBytes []byte, err error) { 348 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 349 return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 350 } 351 352 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 353 if err != nil { 354 return nil, err 355 } 356 357 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 358 if err != nil { 359 return nil, err 360 } 361 362 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 363 if err != nil { 364 return nil, err 365 } 366 367 derivedKey, err := getKDFKey(keyProtected.Crypto, secret) 368 if err != nil { 369 return nil, err 370 } 371 372 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 373 if !bytes.Equal(calculatedMAC, mac) { 374 return nil, ErrDecrypt 375 } 376 377 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 378 if err != nil { 379 return nil, err 380 } 381 return plainText, err 382 } 383 384 func decryptKeyV1(keyProtected *web3v1, secret string) (keyBytes []byte, err error) { 385 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 386 if err != nil { 387 return nil, err 388 } 389 390 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 391 if err != nil { 392 return nil, err 393 } 394 395 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 396 if err != nil { 397 return nil, err 398 } 399 400 derivedKey, err := getKDFKey(keyProtected.Crypto, secret) 401 if err != nil { 402 return nil, err 403 } 404 405 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 406 if !bytes.Equal(calculatedMAC, mac) { 407 return nil, ErrDecrypt 408 } 409 410 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 411 if err != nil { 412 return nil, err 413 } 414 return plainText, err 415 } 416 417 func getKDFKey(cryptoJSON cryptoJSON, secret string) ([]byte, error) { 418 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 419 if err != nil { 420 return nil, err 421 } 422 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 423 424 if cryptoJSON.KDF == "scrypt" { 425 n := ensureInt(cryptoJSON.KDFParams["n"]) 426 r := ensureInt(cryptoJSON.KDFParams["r"]) 427 p := ensureInt(cryptoJSON.KDFParams["p"]) 428 return scrypt.Key([]byte(secret), salt, n, r, p, dkLen) 429 430 } else if cryptoJSON.KDF == "pbkdf2" { 431 c := ensureInt(cryptoJSON.KDFParams["c"]) 432 prf := cryptoJSON.KDFParams["prf"].(string) 433 if prf != "hmac-sha256" { 434 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 435 } 436 key := pbkdf2.Key([]byte(secret), salt, c, dkLen, sha256.New) 437 return key, nil 438 } 439 440 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 441 } 442 443 // TODO: can we do without this when unmarshalling dynamic JSON? 444 // why do integers in KDF params end up as float64 and not int after 445 // unmarshal? 446 func ensureInt(x interface{}) int { 447 res, ok := x.(int) 448 if !ok { 449 res = int(x.(float64)) 450 } 451 return res 452 } 453 454 func writeKeyFile(file string, content []byte) error { 455 dir, basename := filepath.Split(file) 456 457 // Atomic write: create a temporary hidden file first 458 // then move it into place. TempFile assigns mode 0600. 459 f, err := ioutil.TempFile(dir, "."+basename+".tmp") 460 if err != nil { 461 return err 462 } 463 464 if _, err := f.Write(content); err != nil { 465 f.Close() 466 os.Remove(f.Name()) 467 return err 468 } 469 if err := f.Close(); err != nil { 470 return err 471 } 472 473 // BUG(pascaldekloe): Windows won't allow updates to a keyfile when it is being read. 474 return os.Rename(f.Name(), file) 475 }