github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/accounts/keystore/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 crand "crypto/rand" 32 "crypto/sha256" 33 "encoding/hex" 34 "encoding/json" 35 "fmt" 36 "io/ioutil" 37 "path/filepath" 38 39 "github.com/intfoundation/intchain/common" 40 "github.com/intfoundation/intchain/common/math" 41 "github.com/intfoundation/intchain/crypto" 42 "github.com/intfoundation/intchain/crypto/randentropy" 43 "github.com/pborman/uuid" 44 "golang.org/x/crypto/pbkdf2" 45 "golang.org/x/crypto/scrypt" 46 ) 47 48 const ( 49 keyHeaderKDF = "scrypt" 50 51 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 52 // memory and taking approximately 1s CPU time on a modern processor. 53 StandardScryptN = 1 << 18 54 55 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 56 // memory and taking approximately 1s CPU time on a modern processor. 57 StandardScryptP = 1 58 59 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 60 // memory and taking approximately 100ms CPU time on a modern processor. 61 LightScryptN = 1 << 12 62 63 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 64 // memory and taking approximately 100ms CPU time on a modern processor. 65 LightScryptP = 6 66 67 scryptR = 8 68 scryptDKLen = 32 69 ) 70 71 type keyStorePassphrase struct { 72 keysDirPath string 73 scryptN int 74 scryptP int 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 := ioutil.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.Address != addr { 89 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, 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}, crand.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 return writeKeyFile(filename, keyjson) 106 } 107 108 func (ks keyStorePassphrase) JoinPath(filename string) string { 109 if filepath.IsAbs(filename) { 110 return filename 111 } else { 112 return filepath.Join(ks.keysDirPath, filename) 113 } 114 } 115 116 // EncryptKey encrypts a key using the specified scrypt parameters into a json 117 // blob that can be decrypted later on. 118 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 119 authArray := []byte(auth) 120 salt := randentropy.GetEntropyCSPRNG(32) 121 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 122 if err != nil { 123 return nil, err 124 } 125 encryptKey := derivedKey[:16] 126 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 127 128 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 129 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 130 if err != nil { 131 return nil, err 132 } 133 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 134 135 scryptParamsJSON := make(map[string]interface{}, 5) 136 scryptParamsJSON["n"] = scryptN 137 scryptParamsJSON["r"] = scryptR 138 scryptParamsJSON["p"] = scryptP 139 scryptParamsJSON["dklen"] = scryptDKLen 140 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 141 142 cipherParamsJSON := cipherparamsJSON{ 143 IV: hex.EncodeToString(iv), 144 } 145 146 cryptoStruct := cryptoJSON{ 147 Cipher: "aes-128-ctr", 148 CipherText: hex.EncodeToString(cipherText), 149 CipherParams: cipherParamsJSON, 150 KDF: keyHeaderKDF, 151 KDFParams: scryptParamsJSON, 152 MAC: hex.EncodeToString(mac), 153 } 154 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 155 hex.EncodeToString(key.Address[:]), 156 cryptoStruct, 157 key.Id.String(), 158 version, 159 } 160 return json.Marshal(encryptedKeyJSONV3) 161 } 162 163 // DecryptKey decrypts a key from a json blob, returning the private key itself. 164 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 165 // Parse the json into a simple map to fetch the key version 166 m := make(map[string]interface{}) 167 if err := json.Unmarshal(keyjson, &m); err != nil { 168 return nil, err 169 } 170 // Depending on the version try to parse one way or another 171 var ( 172 keyBytes, keyId []byte 173 err error 174 ) 175 if version, ok := m["version"].(string); ok && version == "1" { 176 k := new(encryptedKeyJSONV1) 177 if err := json.Unmarshal(keyjson, k); err != nil { 178 return nil, err 179 } 180 keyBytes, keyId, err = decryptKeyV1(k, auth) 181 } else { 182 k := new(encryptedKeyJSONV3) 183 if err := json.Unmarshal(keyjson, k); err != nil { 184 return nil, err 185 } 186 keyBytes, keyId, err = decryptKeyV3(k, auth) 187 } 188 // Handle any decryption errors and return the key 189 if err != nil { 190 return nil, err 191 } 192 key := crypto.ToECDSAUnsafe(keyBytes) 193 194 return &Key{ 195 Id: uuid.UUID(keyId), 196 Address: crypto.PubkeyToAddress(key.PublicKey), 197 PrivateKey: key, 198 }, nil 199 } 200 201 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 202 if keyProtected.Version != version { 203 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 204 } 205 206 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 207 return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 208 } 209 210 keyId = uuid.Parse(keyProtected.Id) 211 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 212 if err != nil { 213 return nil, nil, err 214 } 215 216 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 217 if err != nil { 218 return nil, nil, err 219 } 220 221 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 222 if err != nil { 223 return nil, nil, err 224 } 225 226 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 227 if err != nil { 228 return nil, nil, err 229 } 230 231 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 232 if !bytes.Equal(calculatedMAC, mac) { 233 return nil, nil, ErrDecrypt 234 } 235 236 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 237 if err != nil { 238 return nil, nil, err 239 } 240 return plainText, keyId, err 241 } 242 243 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 244 keyId = uuid.Parse(keyProtected.Id) 245 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 246 if err != nil { 247 return nil, nil, err 248 } 249 250 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 251 if err != nil { 252 return nil, nil, err 253 } 254 255 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 256 if err != nil { 257 return nil, nil, err 258 } 259 260 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 261 if err != nil { 262 return nil, nil, err 263 } 264 265 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 266 if !bytes.Equal(calculatedMAC, mac) { 267 return nil, nil, ErrDecrypt 268 } 269 270 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 271 if err != nil { 272 return nil, nil, err 273 } 274 return plainText, keyId, err 275 } 276 277 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 278 authArray := []byte(auth) 279 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 280 if err != nil { 281 return nil, err 282 } 283 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 284 285 if cryptoJSON.KDF == keyHeaderKDF { 286 n := ensureInt(cryptoJSON.KDFParams["n"]) 287 r := ensureInt(cryptoJSON.KDFParams["r"]) 288 p := ensureInt(cryptoJSON.KDFParams["p"]) 289 return scrypt.Key(authArray, salt, n, r, p, dkLen) 290 291 } else if cryptoJSON.KDF == "pbkdf2" { 292 c := ensureInt(cryptoJSON.KDFParams["c"]) 293 prf := cryptoJSON.KDFParams["prf"].(string) 294 if prf != "hmac-sha256" { 295 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 296 } 297 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 298 return key, nil 299 } 300 301 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 302 } 303 304 // TODO: can we do without this when unmarshalling dynamic JSON? 305 // why do integers in KDF params end up as float64 and not int after 306 // unmarshal? 307 func ensureInt(x interface{}) int { 308 res, ok := x.(int) 309 if !ok { 310 res = int(x.(float64)) 311 } 312 return res 313 }