github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/keystore/keystore_passphrase.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 /* 13 14 This key store behaves as KeyStorePlain with the difference that 15 the private key is encrypted and on disk uses another JSON encoding. 16 17 The crypto is documented at https://github.com/Sberex/wiki/wiki/Web3-Secret-Storage-Definition 18 19 */ 20 21 package keystore 22 23 import ( 24 "bytes" 25 "crypto/aes" 26 crand "crypto/rand" 27 "crypto/sha256" 28 "encoding/hex" 29 "encoding/json" 30 "fmt" 31 "io/ioutil" 32 "path/filepath" 33 34 "github.com/Sberex/go-sberex/common" 35 "github.com/Sberex/go-sberex/common/math" 36 "github.com/Sberex/go-sberex/crypto" 37 "github.com/Sberex/go-sberex/crypto/randentropy" 38 "github.com/pborman/uuid" 39 "golang.org/x/crypto/pbkdf2" 40 "golang.org/x/crypto/scrypt" 41 ) 42 43 const ( 44 keyHeaderKDF = "scrypt" 45 46 // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB 47 // memory and taking approximately 1s CPU time on a modern processor. 48 StandardScryptN = 1 << 18 49 50 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB 51 // memory and taking approximately 1s CPU time on a modern processor. 52 StandardScryptP = 1 53 54 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB 55 // memory and taking approximately 100ms CPU time on a modern processor. 56 LightScryptN = 1 << 12 57 58 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB 59 // memory and taking approximately 100ms CPU time on a modern processor. 60 LightScryptP = 6 61 62 scryptR = 8 63 scryptDKLen = 32 64 ) 65 66 type keyStorePassphrase struct { 67 keysDirPath string 68 scryptN int 69 scryptP int 70 } 71 72 func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { 73 // Load the key from the keystore and decrypt its contents 74 keyjson, err := ioutil.ReadFile(filename) 75 if err != nil { 76 return nil, err 77 } 78 key, err := DecryptKey(keyjson, auth) 79 if err != nil { 80 return nil, err 81 } 82 // Make sure we're really operating on the requested key (no swap attacks) 83 if key.Address != addr { 84 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) 85 } 86 return key, nil 87 } 88 89 // StoreKey generates a key, encrypts with 'auth' and stores in the given directory 90 func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) { 91 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, crand.Reader, auth) 92 return a.Address, err 93 } 94 95 func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { 96 keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 97 if err != nil { 98 return err 99 } 100 return writeKeyFile(filename, keyjson) 101 } 102 103 func (ks keyStorePassphrase) JoinPath(filename string) string { 104 if filepath.IsAbs(filename) { 105 return filename 106 } else { 107 return filepath.Join(ks.keysDirPath, filename) 108 } 109 } 110 111 // EncryptKey encrypts a key using the specified scrypt parameters into a json 112 // blob that can be decrypted later on. 113 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 114 authArray := []byte(auth) 115 salt := randentropy.GetEntropyCSPRNG(32) 116 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 117 if err != nil { 118 return nil, err 119 } 120 encryptKey := derivedKey[:16] 121 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 122 123 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 124 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 125 if err != nil { 126 return nil, err 127 } 128 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 129 130 scryptParamsJSON := make(map[string]interface{}, 5) 131 scryptParamsJSON["n"] = scryptN 132 scryptParamsJSON["r"] = scryptR 133 scryptParamsJSON["p"] = scryptP 134 scryptParamsJSON["dklen"] = scryptDKLen 135 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 136 137 cipherParamsJSON := cipherparamsJSON{ 138 IV: hex.EncodeToString(iv), 139 } 140 141 cryptoStruct := cryptoJSON{ 142 Cipher: "aes-128-ctr", 143 CipherText: hex.EncodeToString(cipherText), 144 CipherParams: cipherParamsJSON, 145 KDF: keyHeaderKDF, 146 KDFParams: scryptParamsJSON, 147 MAC: hex.EncodeToString(mac), 148 } 149 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 150 hex.EncodeToString(key.Address[:]), 151 cryptoStruct, 152 key.Id.String(), 153 version, 154 } 155 return json.Marshal(encryptedKeyJSONV3) 156 } 157 158 // DecryptKey decrypts a key from a json blob, returning the private key itself. 159 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 160 // Parse the json into a simple map to fetch the key version 161 m := make(map[string]interface{}) 162 if err := json.Unmarshal(keyjson, &m); err != nil { 163 return nil, err 164 } 165 // Depending on the version try to parse one way or another 166 var ( 167 keyBytes, keyId []byte 168 err error 169 ) 170 if version, ok := m["version"].(string); ok && version == "1" { 171 k := new(encryptedKeyJSONV1) 172 if err := json.Unmarshal(keyjson, k); err != nil { 173 return nil, err 174 } 175 keyBytes, keyId, err = decryptKeyV1(k, auth) 176 } else { 177 k := new(encryptedKeyJSONV3) 178 if err := json.Unmarshal(keyjson, k); err != nil { 179 return nil, err 180 } 181 keyBytes, keyId, err = decryptKeyV3(k, auth) 182 } 183 // Handle any decryption errors and return the key 184 if err != nil { 185 return nil, err 186 } 187 key := crypto.ToECDSAUnsafe(keyBytes) 188 189 return &Key{ 190 Id: uuid.UUID(keyId), 191 Address: crypto.PubkeyToAddress(key.PublicKey), 192 PrivateKey: key, 193 }, nil 194 } 195 196 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 197 if keyProtected.Version != version { 198 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 199 } 200 201 if keyProtected.Crypto.Cipher != "aes-128-ctr" { 202 return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) 203 } 204 205 keyId = uuid.Parse(keyProtected.Id) 206 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 207 if err != nil { 208 return nil, nil, err 209 } 210 211 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 212 if err != nil { 213 return nil, nil, err 214 } 215 216 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 217 if err != nil { 218 return nil, nil, err 219 } 220 221 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 222 if err != nil { 223 return nil, nil, err 224 } 225 226 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 227 if !bytes.Equal(calculatedMAC, mac) { 228 return nil, nil, ErrDecrypt 229 } 230 231 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 232 if err != nil { 233 return nil, nil, err 234 } 235 return plainText, keyId, err 236 } 237 238 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 239 keyId = uuid.Parse(keyProtected.Id) 240 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 241 if err != nil { 242 return nil, nil, err 243 } 244 245 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 246 if err != nil { 247 return nil, nil, err 248 } 249 250 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 251 if err != nil { 252 return nil, nil, err 253 } 254 255 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 256 if err != nil { 257 return nil, nil, err 258 } 259 260 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 261 if !bytes.Equal(calculatedMAC, mac) { 262 return nil, nil, ErrDecrypt 263 } 264 265 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 266 if err != nil { 267 return nil, nil, err 268 } 269 return plainText, keyId, err 270 } 271 272 func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { 273 authArray := []byte(auth) 274 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 275 if err != nil { 276 return nil, err 277 } 278 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 279 280 if cryptoJSON.KDF == keyHeaderKDF { 281 n := ensureInt(cryptoJSON.KDFParams["n"]) 282 r := ensureInt(cryptoJSON.KDFParams["r"]) 283 p := ensureInt(cryptoJSON.KDFParams["p"]) 284 return scrypt.Key(authArray, salt, n, r, p, dkLen) 285 286 } else if cryptoJSON.KDF == "pbkdf2" { 287 c := ensureInt(cryptoJSON.KDFParams["c"]) 288 prf := cryptoJSON.KDFParams["prf"].(string) 289 if prf != "hmac-sha256" { 290 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 291 } 292 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 293 return key, nil 294 } 295 296 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 297 } 298 299 // TODO: can we do without this when unmarshalling dynamic JSON? 300 // why do integers in KDF params end up as float64 and not int after 301 // unmarshal? 302 func ensureInt(x interface{}) int { 303 res, ok := x.(int) 304 if !ok { 305 res = int(x.(float64)) 306 } 307 return res 308 }