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