Skip to content

Instantly share code, notes, and snippets.

@rikonor

rikonor/main.go

Created Feb 3, 2021
Embed
What would you like to do?
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)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment