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