mmgen-tool: add extract_key_from_geth_wallet command
- decrypt the encrypted private key in a Geth keystore wallet and output
the decrypted key in hexadecimal format
Usage:
$ mmgen-tool extract_key_from_geth_wallet geth-keystore-wallet.json
Testing:
$ test/test.py -e tool_extract_key_from_geth_wallet
This commit is contained in:
parent
09ab734ab3
commit
096f363dbc
5 changed files with 88 additions and 0 deletions
68
mmgen/base_proto/ethereum/misc.py
Executable file
68
mmgen/base_proto/ethereum/misc.py
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
base_proto.ethereum.misc: miscellaneous utilities for Ethereum base protocol
|
||||
"""
|
||||
|
||||
from ...util import die
|
||||
|
||||
def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True):
|
||||
"""
|
||||
Decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key
|
||||
"""
|
||||
import json
|
||||
|
||||
with open(wallet_fn) as fp:
|
||||
wallet_data = json.loads(fp.read())
|
||||
|
||||
cdata = wallet_data['crypto']
|
||||
|
||||
assert cdata['cipher'] == 'aes-128-ctr', f'incorrect cipher: "{cdata["cipher"]}" != "aes-128-ctr"'
|
||||
assert cdata['kdf'] == 'scrypt', f'incorrect KDF: "{cdata["kdf"]}" != "scrypt"'
|
||||
|
||||
# Derive encryption key from password
|
||||
from hashlib import scrypt
|
||||
sp = cdata['kdfparams']
|
||||
hashed_pw = scrypt(
|
||||
password = passwd,
|
||||
salt = bytes.fromhex( sp['salt'] ),
|
||||
n = sp['n'],
|
||||
r = sp['r'],
|
||||
p = sp['p'],
|
||||
maxmem = 0,
|
||||
dklen = sp['dklen'] )
|
||||
|
||||
# Check password by comparing generated MAC to stored MAC
|
||||
from ...util import get_keccak
|
||||
mac_chk = get_keccak()(hashed_pw[16:32] + bytes.fromhex( cdata['ciphertext'] )).digest().hex()
|
||||
if mac_chk != cdata['mac']:
|
||||
die(1,'Incorrect passphrase')
|
||||
|
||||
# Decrypt Ethereum private key
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
c = Cipher(
|
||||
algorithms.AES(hashed_pw[:16]),
|
||||
modes.CTR(bytes.fromhex( cdata['cipherparams']['iv'] )),
|
||||
backend = default_backend() )
|
||||
encryptor = c.encryptor()
|
||||
key = encryptor.update( bytes.fromhex(cdata['ciphertext']) ) + encryptor.finalize()
|
||||
|
||||
# Optionally check that Ethereum private key produces correct address
|
||||
if check_addr:
|
||||
from ...tool.coin import tool_cmd
|
||||
from ...protocol import init_proto
|
||||
t = tool_cmd( proto=init_proto('eth') )
|
||||
addr = t.wif2addr(key.hex())
|
||||
addr_chk = wallet_data['address']
|
||||
assert addr == addr_chk, f'incorrect address: ({addr} != {addr_chk})'
|
||||
|
||||
return key
|
||||
|
|
@ -32,6 +32,7 @@ opts_data = {
|
|||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-h, --help Print this help message
|
||||
--, --longhelp Print help message for long options (common options)
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
|
||||
-k, --use-internal-keccak-module Force use of the internal keccak module
|
||||
-K, --keygen-backend=n Use backend 'n' for public key generation. Options
|
||||
for {coin_id}: {kgs}
|
||||
|
|
@ -83,6 +84,7 @@ mods = {
|
|||
'bytespec',
|
||||
'bytestob58',
|
||||
'eth_checksummed_addr',
|
||||
'extract_key_from_geth_wallet',
|
||||
'hash160',
|
||||
'hash256',
|
||||
'hexdump',
|
||||
|
|
|
|||
|
|
@ -178,3 +178,11 @@ class tool_cmd(tool_cmd_base):
|
|||
"create a checksummed Ethereum address"
|
||||
from ..protocol import init_proto
|
||||
return init_proto('eth').checksummed_addr(addr)
|
||||
|
||||
def extract_key_from_geth_wallet( self, wallet_file:str, check_addr=True ):
|
||||
"decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key"
|
||||
from ..util import line_input
|
||||
from ..opts import opt
|
||||
from ..base_proto.ethereum.misc import extract_key_from_geth_keystore_wallet
|
||||
passwd = line_input( 'Enter passphrase: ', echo=opt.echo_passphrase ).strip().encode()
|
||||
return extract_key_from_geth_keystore_wallet( wallet_file, passwd, check_addr ).hex()
|
||||
|
|
|
|||
1
test/ref/ethereum/geth-wallet.json
Normal file
1
test/ref/ethereum/geth-wallet.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"address":"50f8b08aadb66d5e6d9df924ec1173ab4540ef82","crypto":{"cipher":"aes-128-ctr","ciphertext":"2fefcef71b5f7f16a04b6b76b6f6db145a242f4f79e1cda75633d0d0d46a7419","cipherparams":{"iv":"0f47f4bcd638d2e2d5e4997e382c15fc"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"fd29f4b7f22b5dd0fcc554a91cc46da6e27cd854cf98d84105487b9c13f6e968"},"mac":"40323ca744ef7b43cd672c5129dd49f7ad68e4ad6f9a38874a1d92f9509da12d"},"id":"5c4d8652-874c-4838-be13-33666a2c2b8d","version":3}
|
||||
|
|
@ -26,6 +26,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
|
|||
('tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [])),
|
||||
('tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[enc_infn+'.mmenc'],9]])),
|
||||
('tool_twview_bad_comment',(9,"'mmgen-tool twview' (with bad comment)", [])),
|
||||
('tool_extract_key_from_geth_wallet',(9,"'mmgen-tool extract_key_from_geth_wallet'", [])),
|
||||
('tool_api', (9,'tool API (initialization, config methods, wif2addr)',[])),
|
||||
# ('tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])),
|
||||
)
|
||||
|
|
@ -85,6 +86,14 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
|
|||
t.req_exit_val = 2
|
||||
return t
|
||||
|
||||
def tool_extract_key_from_geth_wallet(self):
|
||||
fn = 'test/ref/ethereum/geth-wallet.json'
|
||||
key = '9627ddb68354f5e0ff45fb2da49d7a20a013b7257a83ef4adbbbd87aeaccc75e'
|
||||
t = self.spawn('mmgen-tool',['-d',self.tmpdir,'extract_key_from_geth_wallet',fn])
|
||||
t.expect('Enter passphrase: ','\n')
|
||||
t.expect(key)
|
||||
return t
|
||||
|
||||
def tool_api(self):
|
||||
t = self.spawn('tool_api_test.py',cmd_dir='test/misc')
|
||||
t.expect('legacy.*compressed.*segwit.*bech32',regex=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue