github.com/theQRL/go-zond@v0.2.1/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/cipher" 32 "crypto/rand" 33 "crypto/sha256" 34 "encoding/hex" 35 "encoding/json" 36 "fmt" 37 "io" 38 "os" 39 "path/filepath" 40 41 "github.com/google/uuid" 42 "github.com/theQRL/go-zond/accounts" 43 "github.com/theQRL/go-zond/common" 44 "github.com/theQRL/go-zond/crypto" 45 "github.com/theQRL/go-zond/crypto/pqcrypto" 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 := os.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}, 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/theQRL/go-zond/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 salt := make([]byte, 32) 143 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 144 panic("reading from crypto/rand failed: " + err.Error()) 145 } 146 derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) 147 if err != nil { 148 return CryptoJSON{}, err 149 } 150 encryptKey := derivedKey[:16] 151 152 iv := make([]byte, aes.BlockSize) // 16 153 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 154 panic("reading from crypto/rand failed: " + err.Error()) 155 } 156 cipherText, err := aesCTRXOR(encryptKey, data, iv) 157 if err != nil { 158 return CryptoJSON{}, err 159 } 160 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 161 162 scryptParamsJSON := make(map[string]interface{}, 5) 163 scryptParamsJSON["n"] = scryptN 164 scryptParamsJSON["r"] = scryptR 165 scryptParamsJSON["p"] = scryptP 166 scryptParamsJSON["dklen"] = scryptDKLen 167 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 168 cipherParamsJSON := cipherparamsJSON{ 169 IV: hex.EncodeToString(iv), 170 } 171 172 cryptoStruct := CryptoJSON{ 173 Cipher: "aes-128-ctr", 174 CipherText: hex.EncodeToString(cipherText), 175 CipherParams: cipherParamsJSON, 176 KDF: keyHeaderKDF, 177 KDFParams: scryptParamsJSON, 178 MAC: hex.EncodeToString(mac), 179 } 180 return cryptoStruct, nil 181 } 182 183 // EncryptKey encrypts a key using the specified scrypt parameters into a json 184 // blob that can be decrypted later on. 185 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 186 seed := key.Dilithium.GetSeed() 187 cryptoStruct, err := EncryptDataV3(seed[:], []byte(auth), scryptN, scryptP) 188 if err != nil { 189 return nil, err 190 } 191 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 192 fmt.Sprintf("%#x", key.Address), 193 cryptoStruct, 194 key.Id.String(), 195 version, 196 } 197 return json.Marshal(encryptedKeyJSONV3) 198 } 199 200 // DecryptKey decrypts a key from a json blob, returning the private key itself. 201 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 202 // Parse the json into a simple map to fetch the key version 203 m := make(map[string]interface{}) 204 if err := json.Unmarshal(keyjson, &m); err != nil { 205 return nil, err 206 } 207 // Depending on the version try to parse one way or another 208 var ( 209 keyBytes, keyId []byte 210 err error 211 ) 212 213 k := new(encryptedKeyJSONV3) 214 if err := json.Unmarshal(keyjson, k); err != nil { 215 return nil, err 216 } 217 keyBytes, keyId, err = decryptKeyV3(k, auth) 218 219 // Handle any decryption errors and return the key 220 if err != nil { 221 return nil, err 222 } 223 d := pqcrypto.ToDilithiumUnsafe(keyBytes) 224 id, err := uuid.FromBytes(keyId) 225 if err != nil { 226 return nil, err 227 } 228 return &Key{ 229 Id: id, 230 Address: d.GetAddress(), 231 Dilithium: d, 232 }, nil 233 } 234 235 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 236 if cryptoJson.Cipher != "aes-128-ctr" { 237 return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher) 238 } 239 mac, err := hex.DecodeString(cryptoJson.MAC) 240 if err != nil { 241 return nil, err 242 } 243 244 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 245 if err != nil { 246 return nil, err 247 } 248 249 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 250 if err != nil { 251 return nil, err 252 } 253 254 derivedKey, err := getKDFKey(cryptoJson, auth) 255 if err != nil { 256 return nil, err 257 } 258 259 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 260 if !bytes.Equal(calculatedMAC, mac) { 261 return nil, ErrDecrypt 262 } 263 264 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 265 if err != nil { 266 return nil, err 267 } 268 return plainText, err 269 } 270 271 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 272 if keyProtected.Version != version { 273 return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) 274 } 275 keyUUID, err := uuid.Parse(keyProtected.Id) 276 if err != nil { 277 return nil, nil, err 278 } 279 keyId = keyUUID[:] 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 getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 288 authArray := []byte(auth) 289 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 290 if err != nil { 291 return nil, err 292 } 293 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 294 295 if cryptoJSON.KDF == keyHeaderKDF { 296 n := ensureInt(cryptoJSON.KDFParams["n"]) 297 r := ensureInt(cryptoJSON.KDFParams["r"]) 298 p := ensureInt(cryptoJSON.KDFParams["p"]) 299 return scrypt.Key(authArray, salt, n, r, p, dkLen) 300 } else if cryptoJSON.KDF == "pbkdf2" { 301 c := ensureInt(cryptoJSON.KDFParams["c"]) 302 prf := cryptoJSON.KDFParams["prf"].(string) 303 if prf != "hmac-sha256" { 304 return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 305 } 306 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 307 return key, nil 308 } 309 310 return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF) 311 } 312 313 // TODO: can we do without this when unmarshalling dynamic JSON? 314 // why do integers in KDF params end up as float64 and not int after 315 // unmarshal? 316 func ensureInt(x interface{}) int { 317 res, ok := x.(int) 318 if !ok { 319 res = int(x.(float64)) 320 } 321 return res 322 } 323 324 func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { 325 // AES-128 is selected due to size of encryptKey. 326 aesBlock, err := aes.NewCipher(key) 327 if err != nil { 328 return nil, err 329 } 330 stream := cipher.NewCTR(aesBlock, iv) 331 outText := make([]byte, len(inText)) 332 stream.XORKeyStream(outText, inText) 333 return outText, err 334 } 335 336 func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { 337 aesBlock, err := aes.NewCipher(key) 338 if err != nil { 339 return nil, err 340 } 341 decrypter := cipher.NewCBCDecrypter(aesBlock, iv) 342 paddedPlaintext := make([]byte, len(cipherText)) 343 decrypter.CryptBlocks(paddedPlaintext, cipherText) 344 plaintext := pkcs7Unpad(paddedPlaintext) 345 if plaintext == nil { 346 return nil, ErrDecrypt 347 } 348 return plaintext, err 349 } 350 351 // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes 352 func pkcs7Unpad(in []byte) []byte { 353 if len(in) == 0 { 354 return nil 355 } 356 357 padding := in[len(in)-1] 358 if int(padding) > len(in) || padding > aes.BlockSize { 359 return nil 360 } else if padding == 0 { 361 return nil 362 } 363 364 for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 365 if in[i] != padding { 366 return nil 367 } 368 } 369 return in[:len(in)-int(padding)] 370 }