github.com/Gessiux/neatchain@v1.3.1/chain/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/Gessiux/neatchain/utilities/common" 40 "github.com/Gessiux/neatchain/utilities/common/math" 41 "github.com/Gessiux/neatchain/utilities/crypto" 42 "github.com/Gessiux/neatchain/utilities/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 key.Address.String(), 157 cryptoStruct, 158 key.Id.String(), 159 version, 160 } 161 return json.Marshal(encryptedKeyJSONV3) 162 } 163 164 // DecryptKey decrypts a key from a json blob, returning the private key itself. 165 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 166 // Parse the json into a simple map to fetch the key version 167 m := make(map[string]interface{}) 168 if err := json.Unmarshal(keyjson, &m); err != nil { 169 return nil, err 170 } 171 // Depending on the version try to parse one way or another 172 var ( 173 keyBytes, keyId []byte 174 err error 175 ) 176 if version, ok := m["version"].(string); ok && version == "1" { 177 k := new(encryptedKeyJSONV1) 178 if err := json.Unmarshal(keyjson, k); err != nil { 179 return nil, err 180 } 181 keyBytes, keyId, err = decryptKeyV1(k, auth) 182 } else { 183 k := new(encryptedKeyJSONV3) 184 if err := json.Unmarshal(keyjson, k); err != nil { 185 return nil, err 186 } 187 keyBytes, keyId, err = decryptKeyV3(k, auth) 188 } 189 // Handle any decryption errors and return the key 190 if err != nil { 191 return nil, err 192 } 193 key := crypto.ToECDSAUnsafe(keyBytes) 194 195 return &Key{ 196 Id: uuid.UUID(keyId), 197 Address: crypto.PubkeyToAddress(key.PublicKey), 198 PrivateKey: key, 199 }, nil 200 } 201 202 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 203 if keyProtected.Version != version { 204 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 205 } 206 207 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 208 return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 209 } 210 211 keyId = uuid.Parse(keyProtected.Id) 212 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 213 if err != nil { 214 return nil, nil, err 215 } 216 217 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 218 if err != nil { 219 return nil, nil, err 220 } 221 222 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 223 if err != nil { 224 return nil, nil, err 225 } 226 227 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 228 if err != nil { 229 return nil, nil, err 230 } 231 232 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 233 if !bytes.Equal(calculatedMAC, mac) { 234 return nil, nil, ErrDecrypt 235 } 236 237 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 238 if err != nil { 239 return nil, nil, err 240 } 241 return plainText, keyId, err 242 } 243 244 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 245 keyId = uuid.Parse(keyProtected.Id) 246 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 247 if err != nil { 248 return nil, nil, err 249 } 250 251 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 252 if err != nil { 253 return nil, nil, err 254 } 255 256 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 257 if err != nil { 258 return nil, nil, err 259 } 260 261 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 262 if err != nil { 263 return nil, nil, err 264 } 265 266 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 267 if !bytes.Equal(calculatedMAC, mac) { 268 return nil, nil, ErrDecrypt 269 } 270 271 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 272 if err != nil { 273 return nil, nil, err 274 } 275 return plainText, keyId, err 276 } 277 278 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 279 authArray := []byte(auth) 280 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 281 if err != nil { 282 return nil, err 283 } 284 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 285 286 if cryptoJSON.KDF == keyHeaderKDF { 287 n := ensureInt(cryptoJSON.KDFParams["n"]) 288 r := ensureInt(cryptoJSON.KDFParams["r"]) 289 p := ensureInt(cryptoJSON.KDFParams["p"]) 290 return scrypt.Key(authArray, salt, n, r, p, dkLen) 291 292 } else if cryptoJSON.KDF == "pbkdf2" { 293 c := ensureInt(cryptoJSON.KDFParams["c"]) 294 prf := cryptoJSON.KDFParams["prf"].(string) 295 if prf != "hmac-sha256" { 296 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 297 } 298 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 299 return key, nil 300 } 301 302 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 303 } 304 305 // TODO: can we do without this when unmarshalling dynamic JSON? 306 // why do integers in KDF params end up as float64 and not int after 307 // unmarshal? 308 func ensureInt(x interface{}) int { 309 res, ok := x.(int) 310 if !ok { 311 res = int(x.(float64)) 312 } 313 return res 314 }