Skip to content

Instantly share code, notes, and snippets.

@rikonor
Created February 3, 2021 04:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rikonor/aea81f6834ec15e5cad8db4bdb4031c3 to your computer and use it in GitHub Desktop.
Save rikonor/aea81f6834ec15e5cad8db4bdb4031c3 to your computer and use it in GitHub Desktop.
ETH Convert JSON Keystore to Private Key
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"golang.org/x/crypto/scrypt"
"golang.org/x/crypto/sha3"
)
type Config struct {
Password string
Verify bool
}
func main() {
cfg := &Config{}
flag.StringVar(&cfg.Password, "password", "", "Keystore Password")
flag.BoolVar(&cfg.Verify, "verify", true, "Verify Derivation Key")
flag.Parse()
k := KeyStore{}
if err := json.NewDecoder(os.Stdin).Decode(&k); err != nil {
log.Fatal(err)
}
pk, err := Convert(k, cfg.Password, cfg.Verify)
if err != nil {
log.Fatal(err)
}
fmt.Printf("0x%s\n", hex.EncodeToString(pk))
}
type CipherParams struct {
IV string `json:"iv"`
}
type KDFParams struct {
DKLen int `json:"dklen"`
N int `json:"n"`
P int `json:"p"`
R int `json:"r"`
Salt string `json:"salt"`
}
type Crypto struct {
Cipher string `json:"cipher"`
CipherText string `json:"ciphertext"`
CipherParams CipherParams `json:"cipherparams"`
KDF string `json:"kdf"`
KDFParams KDFParams `json:"kdfparams"`
MAC string `json:"mac"`
}
type KeyStore struct {
Address string `json:"address"`
Crypto Crypto `json:"crypto"`
ID string `json:"id"`
Version int `json:"version"`
}
func Convert(k KeyStore, pw string, verify bool) ([]byte, error) {
getDerivationKey := func(pw string, kdfParams KDFParams) ([]byte, error) {
s, err := hex.DecodeString(kdfParams.Salt)
if err != nil {
return nil, err
}
dk, err := scrypt.Key(
[]byte(pw), // password
s, // salt
k.Crypto.KDFParams.N, // N
k.Crypto.KDFParams.R, // r
k.Crypto.KDFParams.P, // p
k.Crypto.KDFParams.DKLen, // keyLen
)
if err != nil {
return nil, err
}
return dk, nil
}
dk, err := getDerivationKey(pw, k.Crypto.KDFParams)
if err != nil {
return nil, err
}
verifyDerivationKey := func(dk []byte, cipherText string, mac string) error {
c, err := hex.DecodeString(cipherText)
if err != nil {
return err
}
m, err := hex.DecodeString(mac)
if err != nil {
return err
}
h := sha3.NewLegacyKeccak256()
h.Write(dk[16:])
h.Write(c)
out := h.Sum(nil)
if !bytes.Equal(out, m) {
return fmt.Errorf("derivation key verification failed")
}
return nil
}
if verify {
sha3.NewLegacyKeccak256()
if err := verifyDerivationKey(dk, k.Crypto.CipherText, k.Crypto.MAC); err != nil {
return nil, err
}
}
decryptCipherText := func(dk []byte, iv string, cipherText string) ([]byte, error) {
ivb, err := hex.DecodeString(iv)
if err != nil {
return nil, err
}
c, err := hex.DecodeString(cipherText)
if err != nil {
return nil, err
}
b, err := aes.NewCipher(dk[0:16])
if err != nil {
return nil, err
}
s := cipher.NewCTR(
b, // block
ivb, // iv
)
out := make([]byte, len(c))
s.XORKeyStream(
out, // dst
c, // src
)
return out, nil
}
pk, err := decryptCipherText(dk, k.Crypto.CipherParams.IV, k.Crypto.CipherText)
if err != nil {
return nil, err
}
return pk, nil
}
package main
import (
"encoding/hex"
"fmt"
"testing"
)
func TestConvert(t *testing.T) {
type testCase struct {
k KeyStore
pw string
out string
}
testCases := []testCase{
testCase{
k: KeyStore{
Address: "8e3fb75a5a2b150df32f0a9e5107fa21969c838c",
Crypto: Crypto{
Cipher: "aes-128-ctr",
CipherText: "02b7d60b1940943f847e3184623553319a522125e903d16c48622c7e87506011",
CipherParams: CipherParams{
IV: "7dc1e9ffbcfb894712713ac8ed3a3a49",
},
KDF: "scrypt",
KDFParams: KDFParams{
DKLen: 32,
N: 262144,
P: 1,
R: 8,
Salt: "ac85ef9c5b07026c482a9cce49d5685d35c9ff876b8507c67edd437d2e7e34a0",
},
MAC: "23eb6987b5d5cc26a5cc180f2f67c50bd4caa1fc0556492b4e9af2c6587270e9",
},
ID: "1bd782c9-e103-48f0-b483-c484a5be85ea",
Version: 3,
},
pw: "123456",
out: "0xf8668027023d051e86d987938d3cd3d1aff88ea9d3851470ba2d5ba0939c1998",
},
}
for _, tc := range testCases {
pk, err := Convert(
tc.k, // k
tc.pw, // password
true, // verify
)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if out := fmt.Sprintf("0x%s", hex.EncodeToString(pk)); out != tc.out {
t.Fatalf("wrong output: %s, expected %s", out, tc.out)
}
}
}
@terrorizers
Copy link

any python version?

when doing
>>> plain_key = dec_suite.decrypt(bytes.fromhex('f97975cb858242372a7c910de23976be4f545ad6b4d6ddb86e54b7d9b3b1c6a1'))
i keep getting
AttributeError: module 'hashlib' has no attribute 'scrypt'
really ethereum feels like an abandoned project sometimes...

@rikonor
Copy link
Author

rikonor commented May 6, 2021

@terrorizers You should be able to use the Go version above by just compiling it locally. Copy main.go locally and run go build on it.

@Oluwatobilobaoke
Copy link

Please how can i run this, i seem to be having problem using it @rikonor

@rikonor
Copy link
Author

rikonor commented Dec 16, 2022

@Oluwatobilobaoke What kind of problems are you having? Do you have Go installed?

@Oluwatobilobaoke
Copy link

@rikonor i have go installed when i ran the code it says a package is deprecated
Screenshot 2022-12-16 at 15 38 12

@rikonor
Copy link
Author

rikonor commented Dec 16, 2022

@Oluwatobilobaoke What's maintest.go? You should really just need to copy main.go from this gist.

@Oluwatobilobaoke
Copy link

Oluwatobilobaoke commented Dec 16, 2022 via email

@rikonor
Copy link
Author

rikonor commented Dec 16, 2022

Hmm try this - create a new directory and put main.go in it. Then run:

$ go mod init tmp
$ go mod tidy
$ go run main.go

@Oluwatobilobaoke
Copy link

Screenshot 2022-12-16 at 16 35 37

see no result

@rikonor
Copy link
Author

rikonor commented Dec 16, 2022

Ok, you're very close! Do you have a a key-store JSON file? Assuming you do, run the following:

$ go build
$ cat key-store.json | ./tmp --password "<PASSWORD>"

@Oluwatobilobaoke
Copy link

thank you it worked..

but i got a lot of ffff in the key what do you think could be wrong?
0xfffffffffffffffffffffffffffffff.....364141

@rikonor
Copy link
Author

rikonor commented Dec 18, 2022

@Oluwatobilobaoke I honestly have not touched this stuff in ages, so it's hard for me to say. If I could make a suggestion, it would be to join the CryptoDevs Discord - you are very likely to get better help there.

@peacecodes12
Copy link

peacecodes12 commented Apr 11, 2023

thank you it worked..

but i got a lot of ffff in the key what do you think could be wrong? 0xfffffffffffffffffffffffffffffff.....364141

what is the steps u followed to run it, please share them

@Oluwatobilobaoke
Copy link

@peacecodes12 it's highlighted above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment