github.com/bcskill/bcschain/v3@v3.4.9-beta2/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 "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/bcskill/bcschain/v3/accounts" 42 "github.com/bcskill/bcschain/v3/common" 43 "github.com/bcskill/bcschain/v3/common/math" 44 "github.com/bcskill/bcschain/v3/crypto" 45 "github.com/pborman/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/bcskill/bcschain/v3/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 hex.EncodeToString(key.Address[:]), 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 232 return &Key{ 233 Id: uuid.UUID(keyId), 234 Address: crypto.PubkeyToAddress(key.PublicKey), 235 PrivateKey: key, 236 }, nil 237 } 238 239 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 240 if cryptoJson.Cipher != "aes-128-ctr" { 241 return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher) 242 } 243 mac, err := hex.DecodeString(cryptoJson.MAC) 244 if err != nil { 245 return nil, err 246 } 247 248 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 249 if err != nil { 250 return nil, err 251 } 252 253 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 254 if err != nil { 255 return nil, err 256 } 257 258 derivedKey, err := getKDFKey(cryptoJson, auth) 259 if err != nil { 260 return nil, err 261 } 262 263 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 264 if !bytes.Equal(calculatedMAC, mac) { 265 return nil, ErrDecrypt 266 } 267 268 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 269 if err != nil { 270 return nil, err 271 } 272 return plainText, err 273 } 274 275 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 276 if keyProtected.Version != version { 277 return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) 278 } 279 keyId = uuid.Parse(keyProtected.Id) 280 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 281 if err != nil { 282 return nil, nil, err 283 } 284 return plainText, keyId, err 285 } 286 287 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 288 keyId = uuid.Parse(keyProtected.Id) 289 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 290 if err != nil { 291 return nil, nil, err 292 } 293 294 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 295 if err != nil { 296 return nil, nil, err 297 } 298 299 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 300 if err != nil { 301 return nil, nil, err 302 } 303 304 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 305 if err != nil { 306 return nil, nil, err 307 } 308 309 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 310 if !bytes.Equal(calculatedMAC, mac) { 311 return nil, nil, ErrDecrypt 312 } 313 314 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 315 if err != nil { 316 return nil, nil, err 317 } 318 return plainText, keyId, err 319 } 320 321 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 322 authArray := []byte(auth) 323 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 324 if err != nil { 325 return nil, err 326 } 327 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 328 329 if cryptoJSON.KDF == keyHeaderKDF { 330 n := ensureInt(cryptoJSON.KDFParams["n"]) 331 r := ensureInt(cryptoJSON.KDFParams["r"]) 332 p := ensureInt(cryptoJSON.KDFParams["p"]) 333 return scrypt.Key(authArray, salt, n, r, p, dkLen) 334 335 } else if cryptoJSON.KDF == "pbkdf2" { 336 c := ensureInt(cryptoJSON.KDFParams["c"]) 337 prf := cryptoJSON.KDFParams["prf"].(string) 338 if prf != "hmac-sha256" { 339 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 340 } 341 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 342 return key, nil 343 } 344 345 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 346 } 347 348 // TODO: can we do without this when unmarshalling dynamic JSON? 349 // why do integers in KDF params end up as float64 and not int after 350 // unmarshal? 351 func ensureInt(x interface{}) int { 352 res, ok := x.(int) 353 if !ok { 354 res = int(x.(float64)) 355 } 356 return res 357 }