github.com/status-im/status-go@v1.1.0/eth-node/keystore/passphrase.go (about) 1 // Imported from github.com/ethereum/go-ethereum/accounts/keystore/passphrase.go 2 // and github.com/ethereum/go-ethereum/accounts/keystore/presale.go 3 4 package keystore 5 6 import ( 7 "bytes" 8 "crypto/aes" 9 "crypto/cipher" 10 "crypto/sha256" 11 "encoding/hex" 12 "encoding/json" 13 "fmt" 14 15 "github.com/google/uuid" 16 "golang.org/x/crypto/pbkdf2" 17 "golang.org/x/crypto/scrypt" 18 19 "github.com/status-im/status-go/eth-node/crypto" 20 "github.com/status-im/status-go/eth-node/types" 21 "github.com/status-im/status-go/extkeys" 22 ) 23 24 const ( 25 keyHeaderKDF = "scrypt" 26 ) 27 28 type EncryptedKeyJSONV3 struct { 29 Address string `json:"address"` 30 Crypto CryptoJSON `json:"crypto"` 31 Id string `json:"id"` 32 Version int `json:"version"` 33 ExtendedKey CryptoJSON `json:"extendedkey"` 34 SubAccountIndex uint32 `json:"subaccountindex"` 35 } 36 37 type encryptedKeyJSONV1 struct { 38 Address string `json:"address"` 39 Crypto CryptoJSON `json:"crypto"` 40 Id string `json:"id"` 41 Version string `json:"version"` 42 } 43 44 type CryptoJSON struct { 45 Cipher string `json:"cipher"` 46 CipherText string `json:"ciphertext"` 47 CipherParams cipherparamsJSON `json:"cipherparams"` 48 KDF string `json:"kdf"` 49 KDFParams map[string]interface{} `json:"kdfparams"` 50 MAC string `json:"mac"` 51 } 52 53 type cipherparamsJSON struct { 54 IV string `json:"iv"` 55 } 56 57 // DecryptKey decrypts a key from a json blob, returning the private key itself. 58 func DecryptKey(keyjson []byte, auth string) (*types.Key, error) { 59 // Parse the json into a simple map to fetch the key version 60 m := make(map[string]interface{}) 61 if err := json.Unmarshal(keyjson, &m); err != nil { 62 return nil, err 63 } 64 // Depending on the version try to parse one way or another 65 var ( 66 keyBytes, keyId []byte 67 err error 68 extKeyBytes []byte 69 extKey *extkeys.ExtendedKey 70 ) 71 72 subAccountIndex, ok := m["subaccountindex"].(float64) 73 if !ok { 74 subAccountIndex = 0 75 } 76 77 if version, ok := m["version"].(string); ok && version == "1" { 78 k := new(encryptedKeyJSONV1) 79 if err := json.Unmarshal(keyjson, k); err != nil { 80 return nil, err 81 } 82 keyBytes, keyId, err = decryptKeyV1(k, auth) 83 if err != nil { 84 return nil, err 85 } 86 87 extKey, err = extkeys.NewKeyFromString(extkeys.EmptyExtendedKeyString) 88 } else { 89 k := new(EncryptedKeyJSONV3) 90 if err := json.Unmarshal(keyjson, k); err != nil { 91 return nil, err 92 } 93 keyBytes, keyId, err = decryptKeyV3(k, auth) 94 if err != nil { 95 return nil, err 96 } 97 98 extKeyBytes, err = decryptExtendedKey(k, auth) 99 if err != nil { 100 return nil, err 101 } 102 extKey, err = extkeys.NewKeyFromString(string(extKeyBytes)) 103 } 104 // Handle any decryption errors and return the key 105 if err != nil { 106 return nil, err 107 } 108 key := crypto.ToECDSAUnsafe(keyBytes) 109 110 id, err := uuid.FromBytes(keyId) 111 if err != nil { 112 return nil, err 113 } 114 115 return &types.Key{ 116 ID: id, 117 Address: crypto.PubkeyToAddress(key.PublicKey), 118 PrivateKey: key, 119 ExtendedKey: extKey, 120 SubAccountIndex: uint32(subAccountIndex), 121 }, nil 122 } 123 124 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 125 if cryptoJson.Cipher != "aes-128-ctr" { 126 return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher) 127 } 128 mac, err := hex.DecodeString(cryptoJson.MAC) 129 if err != nil { 130 return nil, err 131 } 132 133 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 134 if err != nil { 135 return nil, err 136 } 137 138 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 139 if err != nil { 140 return nil, err 141 } 142 143 derivedKey, err := getKDFKey(cryptoJson, auth) 144 if err != nil { 145 return nil, err 146 } 147 148 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 149 if !bytes.Equal(calculatedMAC, mac) { 150 return nil, ErrDecrypt 151 } 152 153 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 154 if err != nil { 155 return nil, err 156 } 157 return plainText, err 158 } 159 160 func decryptKeyV3(keyProtected *EncryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 161 if keyProtected.Version != version { 162 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 163 } 164 id, err := uuid.Parse(keyProtected.Id) 165 if err != nil { 166 return nil, nil, err 167 } 168 keyId = id[:] 169 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 170 if err != nil { 171 return nil, nil, err 172 } 173 return plainText, keyId, err 174 } 175 176 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 177 id, err := uuid.Parse(keyProtected.Id) 178 if err != nil { 179 return nil, nil, err 180 } 181 keyId = id[:] 182 183 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 184 if err != nil { 185 return nil, nil, err 186 } 187 188 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 189 if err != nil { 190 return nil, nil, err 191 } 192 193 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 194 if err != nil { 195 return nil, nil, err 196 } 197 198 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 199 if err != nil { 200 return nil, nil, err 201 } 202 203 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 204 if !bytes.Equal(calculatedMAC, mac) { 205 return nil, nil, ErrDecrypt 206 } 207 208 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 209 if err != nil { 210 return nil, nil, err 211 } 212 return plainText, keyId, err 213 } 214 215 func decryptExtendedKey(keyProtected *EncryptedKeyJSONV3, auth string) (plainText []byte, err error) { 216 if len(keyProtected.ExtendedKey.CipherText) == 0 { 217 return []byte(extkeys.EmptyExtendedKeyString), nil 218 } 219 220 if keyProtected.Version != version { 221 return nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 222 } 223 224 if keyProtected.ExtendedKey.Cipher != "aes-128-ctr" { 225 return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.ExtendedKey.Cipher) 226 } 227 228 mac, err := hex.DecodeString(keyProtected.ExtendedKey.MAC) 229 if err != nil { 230 return nil, err 231 } 232 233 iv, err := hex.DecodeString(keyProtected.ExtendedKey.CipherParams.IV) 234 if err != nil { 235 return nil, err 236 } 237 238 cipherText, err := hex.DecodeString(keyProtected.ExtendedKey.CipherText) 239 if err != nil { 240 return nil, err 241 } 242 243 derivedKey, err := getKDFKey(keyProtected.ExtendedKey, auth) 244 if err != nil { 245 return nil, err 246 } 247 248 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 249 if !bytes.Equal(calculatedMAC, mac) { 250 return nil, ErrDecrypt 251 } 252 253 plainText, err = aesCTRXOR(derivedKey[:16], cipherText, iv) 254 if err != nil { 255 return nil, err 256 } 257 return plainText, err 258 } 259 260 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 261 authArray := []byte(auth) 262 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 263 if err != nil { 264 return nil, err 265 } 266 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 267 268 if cryptoJSON.KDF == keyHeaderKDF { 269 n := ensureInt(cryptoJSON.KDFParams["n"]) 270 r := ensureInt(cryptoJSON.KDFParams["r"]) 271 p := ensureInt(cryptoJSON.KDFParams["p"]) 272 return scrypt.Key(authArray, salt, n, r, p, dkLen) 273 274 } else if cryptoJSON.KDF == "pbkdf2" { 275 c := ensureInt(cryptoJSON.KDFParams["c"]) 276 prf := cryptoJSON.KDFParams["prf"].(string) 277 if prf != "hmac-sha256" { 278 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 279 } 280 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 281 return key, nil 282 } 283 284 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 285 } 286 287 // TODO: can we do without this when unmarshalling dynamic JSON? 288 // why do integers in KDF params end up as float64 and not int after 289 // unmarshal? 290 func ensureInt(x interface{}) int { 291 res, ok := x.(int) 292 if !ok { 293 res = int(x.(float64)) 294 } 295 return res 296 } 297 298 func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { 299 // AES-128 is selected due to size of encryptKey. 300 aesBlock, err := aes.NewCipher(key) 301 if err != nil { 302 return nil, err 303 } 304 stream := cipher.NewCTR(aesBlock, iv) 305 outText := make([]byte, len(inText)) 306 stream.XORKeyStream(outText, inText) 307 return outText, err 308 } 309 310 func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { 311 aesBlock, err := aes.NewCipher(key) 312 if err != nil { 313 return nil, err 314 } 315 decrypter := cipher.NewCBCDecrypter(aesBlock, iv) 316 paddedPlaintext := make([]byte, len(cipherText)) 317 decrypter.CryptBlocks(paddedPlaintext, cipherText) 318 plaintext := pkcs7Unpad(paddedPlaintext) 319 if plaintext == nil { 320 return nil, ErrDecrypt 321 } 322 return plaintext, err 323 } 324 325 // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes 326 func pkcs7Unpad(in []byte) []byte { 327 if len(in) == 0 { 328 return nil 329 } 330 331 padding := in[len(in)-1] 332 if int(padding) > len(in) || padding > aes.BlockSize { 333 return nil 334 } else if padding == 0 { 335 return nil 336 } 337 338 for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 339 if in[i] != padding { 340 return nil 341 } 342 } 343 return in[:len(in)-int(padding)] 344 } 345 346 func RawKeyToCryptoJSON(rawKeyFile []byte) (cj CryptoJSON, e error) { 347 var keyJSON EncryptedKeyJSONV3 348 if e := json.Unmarshal(rawKeyFile, &keyJSON); e != nil { 349 return cj, fmt.Errorf("failed to read key file: %s", e) 350 } 351 352 return keyJSON.Crypto, e 353 }