github.com/klaytn/klaytn@v1.12.1/accounts/keystore/keystore_passphrase.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2014 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from accounts/keystore/keystore_passphrase.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package keystore 22 23 import ( 24 "bytes" 25 "crypto/aes" 26 "crypto/ecdsa" 27 "crypto/rand" 28 "crypto/sha256" 29 "encoding/hex" 30 "encoding/json" 31 "fmt" 32 "io" 33 "os" 34 "path/filepath" 35 36 "github.com/klaytn/klaytn/common" 37 "github.com/klaytn/klaytn/common/math" 38 "github.com/klaytn/klaytn/crypto" 39 "github.com/pborman/uuid" 40 "golang.org/x/crypto/pbkdf2" 41 "golang.org/x/crypto/scrypt" 42 ) 43 44 const ( 45 keyHeaderKDF = "scrypt" 46 47 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 48 // memory and taking approximately 1s CPU time on a modern processor. 49 StandardScryptN = 1 << 18 50 51 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 52 // memory and taking approximately 1s CPU time on a modern processor. 53 StandardScryptP = 1 54 55 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 56 // memory and taking approximately 100ms CPU time on a modern processor. 57 LightScryptN = 1 << 12 58 59 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 60 // memory and taking approximately 100ms CPU time on a modern processor. 61 LightScryptP = 6 62 63 scryptR = 8 64 scryptDKLen = 32 65 ) 66 67 type keyStorePassphrase struct { 68 keysDirPath string 69 scryptN int 70 scryptP int 71 // skipKeyFileVerification disables the security-feature which does 72 // reads and decrypts any newly created keyfiles. This should be 'false' in all 73 // cases except tests -- setting this to 'true' is not recommended. 74 skipKeyFileVerification bool 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 := os.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.GetAddress() != addr { 89 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.GetAddress(), 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, false}, 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 // Write into temporary file 106 tmpName, err := writeTemporaryKeyFile(filename, keyjson) 107 if err != nil { 108 return err 109 } 110 if ks.skipKeyFileVerification == false { // do not skip file verification 111 // Verify that we can decrypt the file with the given password. 112 _, err = ks.GetKey(key.GetAddress(), tmpName, auth) 113 if err != nil { 114 msg := "An error was encountered when saving and verifying the keystore file. \n" + 115 "This indicates that the keystore is corrupted. \n" + 116 "The corrupted file is stored at \n%v\n" + 117 "Please file a ticket at:\n\n" + 118 "https://github.com/klaytn/klaytn/issues" + 119 "The error was : %w" 120 return fmt.Errorf(msg, tmpName, err) 121 } 122 } 123 return os.Rename(tmpName, filename) 124 } 125 126 func (ks keyStorePassphrase) JoinPath(filename string) string { 127 if filepath.IsAbs(filename) { 128 return filename 129 } 130 return filepath.Join(ks.keysDirPath, filename) 131 } 132 133 // encryptCrypto encrypts a private key to a cryptoJSON object. 134 func encryptCrypto(keyBytes []byte, auth string, scryptN, scryptP int) (*cryptoJSON, error) { 135 authArray := []byte(auth) 136 137 salt := make([]byte, 32) 138 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 139 panic("reading from crypto/rand failed: " + err.Error()) 140 } 141 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 142 if err != nil { 143 return nil, err 144 } 145 encryptKey := derivedKey[:16] 146 147 iv := make([]byte, aes.BlockSize) // 16 148 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 149 panic("reading from crypto/rand failed: " + err.Error()) 150 } 151 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 152 if err != nil { 153 return nil, err 154 } 155 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 156 157 scryptParamsJSON := make(map[string]interface{}, 5) 158 scryptParamsJSON["n"] = scryptN 159 scryptParamsJSON["r"] = scryptR 160 scryptParamsJSON["p"] = scryptP 161 scryptParamsJSON["dklen"] = scryptDKLen 162 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 163 164 cipherParamsJSON := cipherparamsJSON{ 165 IV: hex.EncodeToString(iv), 166 } 167 168 return &cryptoJSON{ 169 Cipher: "aes-128-ctr", 170 CipherText: hex.EncodeToString(cipherText), 171 CipherParams: cipherParamsJSON, 172 KDF: keyHeaderKDF, 173 KDFParams: scryptParamsJSON, 174 MAC: hex.EncodeToString(mac), 175 }, nil 176 } 177 178 // EncryptKey encrypts a key using the specified scrypt parameters into a json 179 // blob that can be decrypted later on. It uses the keystore v4 format. 180 func EncryptKey(key Key, auth string, scryptN, scryptP int) ([]byte, error) { 181 pks := key.GetPrivateKeys() 182 crypto := make([][]cryptoJSON, len(pks)) 183 for i, keys := range pks { 184 crypto[i] = make([]cryptoJSON, len(keys)) 185 for j, k := range keys { 186 keyBytes := math.PaddedBigBytes(k.D, 32) 187 c, err := encryptCrypto(keyBytes, auth, scryptN, scryptP) 188 if err != nil { 189 return nil, err 190 } 191 crypto[i][j] = *c 192 } 193 } 194 encryptedKeyJSONV4 := encryptedKeyJSONV4{ 195 hex.EncodeToString(key.GetAddress().Bytes()), 196 crypto, 197 key.GetId().String(), 198 4, 199 } 200 return json.Marshal(encryptedKeyJSONV4) 201 } 202 203 // EncryptKeyV3 encrypts a key using the specified scrypt parameters into a json 204 // blob that can be decrypted later on. It uses the keystore v3 format. 205 func EncryptKeyV3(key Key, auth string, scryptN, scryptP int) ([]byte, error) { 206 authArray := []byte(auth) 207 208 salt := make([]byte, 32) 209 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 210 panic("reading from crypto/rand failed: " + err.Error()) 211 } 212 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 213 if err != nil { 214 return nil, err 215 } 216 encryptKey := derivedKey[:16] 217 keyBytes := math.PaddedBigBytes(key.GetPrivateKey().D, 32) 218 219 iv := make([]byte, aes.BlockSize) // 16 220 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 221 panic("reading from crypto/rand failed: " + err.Error()) 222 } 223 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 224 if err != nil { 225 return nil, err 226 } 227 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 228 229 scryptParamsJSON := make(map[string]interface{}, 5) 230 scryptParamsJSON["n"] = scryptN 231 scryptParamsJSON["r"] = scryptR 232 scryptParamsJSON["p"] = scryptP 233 scryptParamsJSON["dklen"] = scryptDKLen 234 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 235 236 cipherParamsJSON := cipherparamsJSON{ 237 IV: hex.EncodeToString(iv), 238 } 239 240 cryptoStruct := cryptoJSON{ 241 Cipher: "aes-128-ctr", 242 CipherText: hex.EncodeToString(cipherText), 243 CipherParams: cipherParamsJSON, 244 KDF: keyHeaderKDF, 245 KDFParams: scryptParamsJSON, 246 MAC: hex.EncodeToString(mac), 247 } 248 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 249 hex.EncodeToString(key.GetAddress().Bytes()), 250 cryptoStruct, 251 key.GetId().String(), 252 3, 253 } 254 return json.Marshal(encryptedKeyJSONV3) 255 } 256 257 // DecryptKey decrypts a key from a json blob, returning the private key itself. 258 // TODO: use encryptedKeyJSON object directly instead of double unmarshalling. 259 func DecryptKey(keyjson []byte, auth string) (Key, error) { 260 // Parse the json into a simple map to fetch the key version 261 m := make(map[string]interface{}) 262 if err := json.Unmarshal(keyjson, &m); err != nil { 263 return nil, err 264 } 265 // Depending on the version try to parse one way or another 266 var ( 267 keyBytes [][][]byte 268 keyId []byte 269 err error 270 address common.Address 271 ) 272 273 switch v := m["version"].(type) { 274 case string: 275 if v == "1" { 276 k := new(encryptedKeyJSONV1) 277 if err := json.Unmarshal(keyjson, k); err != nil { 278 return nil, err 279 } 280 keyBytes = make([][][]byte, 1) 281 keyBytes[0] = make([][]byte, 1) 282 keyBytes[0][0], keyId, err = decryptKeyV1(k, auth) 283 address = common.HexToAddress(k.Address) 284 } 285 286 case float64: 287 switch v { 288 case 3: 289 k := new(encryptedKeyJSONV3) 290 if err := json.Unmarshal(keyjson, k); err != nil { 291 return nil, err 292 } 293 keyBytes = make([][][]byte, 1) 294 keyBytes[0] = make([][]byte, 1) 295 keyBytes[0][0], keyId, err = decryptKeyV3(k, auth) 296 address = common.HexToAddress(k.Address) 297 case 4: 298 // At first, try to decrypt using encryptedKeyJSONV4 299 k := new(encryptedKeyJSONV4) 300 if err := json.Unmarshal(keyjson, k); err != nil { 301 // If it fails, try to decrypt using encryptedKeyJSONV4Single 302 kSingle := new(encryptedKeyJSONV4Single) 303 if err = json.Unmarshal(keyjson, kSingle); err != nil { 304 return nil, err 305 } 306 307 // If succeeded, copy the values of kSingle to k 308 k.Id = kSingle.Id 309 k.Address = kSingle.Address 310 k.Keyring = [][]cryptoJSON{kSingle.Keyring} 311 k.Version = kSingle.Version 312 } 313 314 keyBytes, keyId, err = decryptKeyV4(k, auth) 315 address = common.HexToAddress(k.Address) 316 default: 317 return nil, fmt.Errorf("undefined version: %f", v) 318 } 319 320 default: 321 return nil, fmt.Errorf("undefined type of version: %s", m) 322 } 323 324 // Handle any decryption errors and return the key 325 if err != nil { 326 return nil, err 327 } 328 privateKeys := make([][]*ecdsa.PrivateKey, len(keyBytes)) 329 for i, keys := range keyBytes { 330 privateKeys[i] = make([]*ecdsa.PrivateKey, len(keys)) 331 for j, key := range keys { 332 privateKeys[i][j], err = crypto.ToECDSA(key) 333 if err != nil { 334 return nil, err 335 } 336 } 337 } 338 339 return &KeyV4{ 340 Id: uuid.UUID(keyId), 341 Address: address, 342 PrivateKeys: privateKeys, 343 }, nil 344 } 345 346 func decryptKeyV4(keyProtected *encryptedKeyJSONV4, auth string) (keyBytes [][][]byte, keyId []byte, err error) { 347 if keyProtected.Version != 4 { 348 return nil, nil, fmt.Errorf("version not supported: %v (should be 4)", keyProtected.Version) 349 } 350 351 keyId = uuid.Parse(keyProtected.Id) 352 keyBytes = make([][][]byte, len(keyProtected.Keyring)) 353 354 for i, keys := range keyProtected.Keyring { 355 keyBytes[i] = make([][]byte, len(keys)) 356 for j, key := range keys { 357 keyBytes[i][j], err = decryptKey(key, auth) 358 if err != nil { 359 return nil, nil, err 360 } 361 } 362 } 363 364 return keyBytes, keyId, err 365 } 366 367 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 368 if keyProtected.Version != 3 { 369 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 370 } 371 keyId = uuid.Parse(keyProtected.Id) 372 373 plainText, err := decryptKey(keyProtected.Crypto, auth) 374 if err != nil { 375 return nil, nil, err 376 } 377 return plainText, keyId, err 378 } 379 380 func decryptKey(cryptoJson cryptoJSON, auth string) (keyBytes []byte, err error) { 381 if cryptoJson.Cipher != "aes-128-ctr" { 382 return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher) 383 } 384 385 mac, err := hex.DecodeString(cryptoJson.MAC) 386 if err != nil { 387 return nil, err 388 } 389 390 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 391 if err != nil { 392 return nil, err 393 } 394 395 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 396 if err != nil { 397 return nil, err 398 } 399 400 derivedKey, err := getKDFKey(cryptoJson, auth) 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 := aesCTRXOR(derivedKey[:16], cipherText, iv) 411 if err != nil { 412 return nil, err 413 } 414 415 return plainText, err 416 } 417 418 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 419 keyId = uuid.Parse(keyProtected.Id) 420 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 421 if err != nil { 422 return nil, nil, err 423 } 424 425 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 426 if err != nil { 427 return nil, nil, err 428 } 429 430 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 431 if err != nil { 432 return nil, nil, err 433 } 434 435 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 436 if err != nil { 437 return nil, nil, err 438 } 439 440 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 441 if !bytes.Equal(calculatedMAC, mac) { 442 return nil, nil, ErrDecrypt 443 } 444 445 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 446 if err != nil { 447 return nil, nil, err 448 } 449 return plainText, keyId, err 450 } 451 452 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 453 authArray := []byte(auth) 454 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 455 if err != nil { 456 return nil, err 457 } 458 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 459 460 if cryptoJSON.KDF == keyHeaderKDF { 461 n := ensureInt(cryptoJSON.KDFParams["n"]) 462 r := ensureInt(cryptoJSON.KDFParams["r"]) 463 p := ensureInt(cryptoJSON.KDFParams["p"]) 464 return scrypt.Key(authArray, salt, n, r, p, dkLen) 465 466 } else if cryptoJSON.KDF == "pbkdf2" { 467 c := ensureInt(cryptoJSON.KDFParams["c"]) 468 prf := cryptoJSON.KDFParams["prf"].(string) 469 if prf != "hmac-sha256" { 470 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 471 } 472 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 473 return key, nil 474 } 475 476 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 477 } 478 479 // TODO: can we do without this when unmarshalling dynamic JSON? 480 // why do integers in KDF params end up as float64 and not int after 481 // unmarshal? 482 func ensureInt(x interface{}) int { 483 res, ok := x.(int) 484 if !ok { 485 res = int(x.(float64)) 486 } 487 return res 488 }