github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/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 "io/ioutil" 38 "os" 39 "path/filepath" 40 41 "github.com/google/uuid" 42 "golang.org/x/crypto/pbkdf2" 43 "golang.org/x/crypto/scrypt" 44 45 "github.com/scroll-tech/go-ethereum/accounts" 46 "github.com/scroll-tech/go-ethereum/common" 47 "github.com/scroll-tech/go-ethereum/common/math" 48 "github.com/scroll-tech/go-ethereum/crypto" 49 ) 50 51 const ( 52 keyHeaderKDF = "scrypt" 53 54 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 55 // memory and taking approximately 1s CPU time on a modern processor. 56 StandardScryptN = 1 << 18 57 58 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 59 // memory and taking approximately 1s CPU time on a modern processor. 60 StandardScryptP = 1 61 62 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 63 // memory and taking approximately 100ms CPU time on a modern processor. 64 LightScryptN = 1 << 12 65 66 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 67 // memory and taking approximately 100ms CPU time on a modern processor. 68 LightScryptP = 6 69 70 scryptR = 8 71 scryptDKLen = 32 72 ) 73 74 type keyStorePassphrase struct { 75 keysDirPath string 76 scryptN int 77 scryptP int 78 // skipKeyFileVerification disables the security-feature which does 79 // reads and decrypts any newly created keyfiles. This should be 'false' in all 80 // cases except tests -- setting this to 'true' is not recommended. 81 skipKeyFileVerification bool 82 } 83 84 func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { 85 // Load the key from the keystore and decrypt its contents 86 keyjson, err := ioutil.ReadFile(filename) 87 if err != nil { 88 return nil, err 89 } 90 key, err := DecryptKey(keyjson, auth) 91 if err != nil { 92 return nil, err 93 } 94 // Make sure we're really operating on the requested key (no swap attacks) 95 if key.Address != addr { 96 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) 97 } 98 return key, nil 99 } 100 101 // StoreKey generates a key, encrypts with 'auth' and stores in the given directory 102 func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) { 103 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth) 104 return a, err 105 } 106 107 func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { 108 keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 109 if err != nil { 110 return err 111 } 112 // Write into temporary file 113 tmpName, err := writeTemporaryKeyFile(filename, keyjson) 114 if err != nil { 115 return err 116 } 117 if !ks.skipKeyFileVerification { 118 // Verify that we can decrypt the file with the given password. 119 _, err = ks.GetKey(key.Address, tmpName, auth) 120 if err != nil { 121 msg := "An error was encountered when saving and verifying the keystore file. \n" + 122 "This indicates that the keystore is corrupted. \n" + 123 "The corrupted file is stored at \n%v\n" + 124 "Please file a ticket at:\n\n" + 125 "https://github.com/scroll-tech/go-ethereum/issues." + 126 "The error was : %s" 127 //lint:ignore ST1005 This is a message for the user 128 return fmt.Errorf(msg, tmpName, err) 129 } 130 } 131 return os.Rename(tmpName, filename) 132 } 133 134 func (ks keyStorePassphrase) JoinPath(filename string) string { 135 if filepath.IsAbs(filename) { 136 return filename 137 } 138 return filepath.Join(ks.keysDirPath, filename) 139 } 140 141 // Encryptdata encrypts the data given as 'data' with the password 'auth'. 142 func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { 143 144 salt := make([]byte, 32) 145 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 146 panic("reading from crypto/rand failed: " + err.Error()) 147 } 148 derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) 149 if err != nil { 150 return CryptoJSON{}, err 151 } 152 encryptKey := derivedKey[:16] 153 154 iv := make([]byte, aes.BlockSize) // 16 155 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 156 panic("reading from crypto/rand failed: " + err.Error()) 157 } 158 cipherText, err := aesCTRXOR(encryptKey, data, iv) 159 if err != nil { 160 return CryptoJSON{}, err 161 } 162 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 163 164 scryptParamsJSON := make(map[string]interface{}, 5) 165 scryptParamsJSON["n"] = scryptN 166 scryptParamsJSON["r"] = scryptR 167 scryptParamsJSON["p"] = scryptP 168 scryptParamsJSON["dklen"] = scryptDKLen 169 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 170 cipherParamsJSON := cipherparamsJSON{ 171 IV: hex.EncodeToString(iv), 172 } 173 174 cryptoStruct := CryptoJSON{ 175 Cipher: "aes-128-ctr", 176 CipherText: hex.EncodeToString(cipherText), 177 CipherParams: cipherParamsJSON, 178 KDF: keyHeaderKDF, 179 KDFParams: scryptParamsJSON, 180 MAC: hex.EncodeToString(mac), 181 } 182 return cryptoStruct, nil 183 } 184 185 // EncryptKey encrypts a key using the specified scrypt parameters into a json 186 // blob that can be decrypted later on. 187 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 188 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 189 cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) 190 if err != nil { 191 return nil, err 192 } 193 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 194 hex.EncodeToString(key.Address[:]), 195 cryptoStruct, 196 key.Id.String(), 197 version, 198 } 199 return json.Marshal(encryptedKeyJSONV3) 200 } 201 202 // DecryptKey decrypts a key from a json blob, returning the private key itself. 203 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 204 // Parse the json into a simple map to fetch the key version 205 m := make(map[string]interface{}) 206 if err := json.Unmarshal(keyjson, &m); err != nil { 207 return nil, err 208 } 209 // Depending on the version try to parse one way or another 210 var ( 211 keyBytes, keyId []byte 212 err error 213 ) 214 if version, ok := m["version"].(string); ok && version == "1" { 215 k := new(encryptedKeyJSONV1) 216 if err := json.Unmarshal(keyjson, k); err != nil { 217 return nil, err 218 } 219 keyBytes, keyId, err = decryptKeyV1(k, auth) 220 } else { 221 k := new(encryptedKeyJSONV3) 222 if err := json.Unmarshal(keyjson, k); err != nil { 223 return nil, err 224 } 225 keyBytes, keyId, err = decryptKeyV3(k, auth) 226 } 227 // Handle any decryption errors and return the key 228 if err != nil { 229 return nil, err 230 } 231 key := crypto.ToECDSAUnsafe(keyBytes) 232 id, err := uuid.FromBytes(keyId) 233 if err != nil { 234 return nil, err 235 } 236 return &Key{ 237 Id: id, 238 Address: crypto.PubkeyToAddress(key.PublicKey), 239 PrivateKey: key, 240 }, nil 241 } 242 243 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 244 if cryptoJson.Cipher != "aes-128-ctr" { 245 return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher) 246 } 247 mac, err := hex.DecodeString(cryptoJson.MAC) 248 if err != nil { 249 return nil, err 250 } 251 252 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 253 if err != nil { 254 return nil, err 255 } 256 257 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 258 if err != nil { 259 return nil, err 260 } 261 262 derivedKey, err := getKDFKey(cryptoJson, auth) 263 if err != nil { 264 return nil, err 265 } 266 267 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 268 if !bytes.Equal(calculatedMAC, mac) { 269 return nil, ErrDecrypt 270 } 271 272 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 273 if err != nil { 274 return nil, err 275 } 276 return plainText, err 277 } 278 279 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 280 if keyProtected.Version != version { 281 return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) 282 } 283 keyUUID, err := uuid.Parse(keyProtected.Id) 284 if err != nil { 285 return nil, nil, err 286 } 287 keyId = keyUUID[:] 288 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 289 if err != nil { 290 return nil, nil, err 291 } 292 return plainText, keyId, err 293 } 294 295 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 296 keyUUID, err := uuid.Parse(keyProtected.Id) 297 if err != nil { 298 return nil, nil, err 299 } 300 keyId = keyUUID[:] 301 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 302 if err != nil { 303 return nil, nil, err 304 } 305 306 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 307 if err != nil { 308 return nil, nil, err 309 } 310 311 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 312 if err != nil { 313 return nil, nil, err 314 } 315 316 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 317 if err != nil { 318 return nil, nil, err 319 } 320 321 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 322 if !bytes.Equal(calculatedMAC, mac) { 323 return nil, nil, ErrDecrypt 324 } 325 326 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 327 if err != nil { 328 return nil, nil, err 329 } 330 return plainText, keyId, err 331 } 332 333 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 334 authArray := []byte(auth) 335 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 336 if err != nil { 337 return nil, err 338 } 339 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 340 341 if cryptoJSON.KDF == keyHeaderKDF { 342 n := ensureInt(cryptoJSON.KDFParams["n"]) 343 r := ensureInt(cryptoJSON.KDFParams["r"]) 344 p := ensureInt(cryptoJSON.KDFParams["p"]) 345 return scrypt.Key(authArray, salt, n, r, p, dkLen) 346 347 } else if cryptoJSON.KDF == "pbkdf2" { 348 c := ensureInt(cryptoJSON.KDFParams["c"]) 349 prf := cryptoJSON.KDFParams["prf"].(string) 350 if prf != "hmac-sha256" { 351 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 352 } 353 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 354 return key, nil 355 } 356 357 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 358 } 359 360 // TODO: can we do without this when unmarshalling dynamic JSON? 361 // why do integers in KDF params end up as float64 and not int after 362 // unmarshal? 363 func ensureInt(x interface{}) int { 364 res, ok := x.(int) 365 if !ok { 366 res = int(x.(float64)) 367 } 368 return res 369 }