From 3510841a128b0bf6948519cd56d2fcfd01ab9468 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 29 Apr 2022 16:53:51 +0000 Subject: [PATCH] minor fixes and cleanups --- mmgen/base_proto/ethereum/misc.py | 22 ++++++++-------------- mmgen/main_msg.py | 19 +++++++++++++------ mmgen/msg.py | 12 ++++++------ mmgen/tool/coin.py | 1 - test/test_py_d/ts_ethdev.py | 1 + 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/mmgen/base_proto/ethereum/misc.py b/mmgen/base_proto/ethereum/misc.py index 9935fb76..eb9a4fc2 100755 --- a/mmgen/base_proto/ethereum/misc.py +++ b/mmgen/base_proto/ethereum/misc.py @@ -12,7 +12,7 @@ base_proto.ethereum.misc: miscellaneous utilities for Ethereum base protocol """ -from ...util import die +from ...util import die,get_keccak def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): """ @@ -41,7 +41,6 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): 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') @@ -67,19 +66,19 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): return key +def hash_message(message): + return get_keccak()( + '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ).encode() + ).digest() + def ec_sign_message_with_privkey(message,key): """ Sign an arbitrary string with an Ethereum private key, returning the signature Conforms to the standard defined by the Geth `eth_sign` JSON-RPC call """ - from ...util import get_keccak - msghash = get_keccak()( - '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ).encode() - ).digest() - from py_ecc.secp256k1 import ecdsa_raw_sign - v,r,s = ecdsa_raw_sign( msghash, key ) + v,r,s = ecdsa_raw_sign( hash_message(message), key ) return '{:064x}{:064x}{:02x}'.format(r,s,v) def ec_recover_pubkey(message,sig): @@ -89,13 +88,8 @@ def ec_recover_pubkey(message,sig): Conforms to the standard defined by the Geth `eth_sign` JSON-RPC call """ - from ...util import get_keccak - msghash = get_keccak()( - '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ).encode() - ).digest() - from py_ecc.secp256k1 import ecdsa_raw_recover r,s,v = ( sig[:64], sig[64:128], sig[128:] ) return '{:064x}{:064x}'.format( - *ecdsa_raw_recover( msghash, tuple(int(hexstr,16) for hexstr in (v,r,s)) ) + *ecdsa_raw_recover( hash_message(message), tuple(int(hexstr,16) for hexstr in (v,r,s)) ) ) diff --git a/mmgen/main_msg.py b/mmgen/main_msg.py index 8557a2b7..e717040f 100755 --- a/mmgen/main_msg.py +++ b/mmgen/main_msg.py @@ -81,14 +81,15 @@ opts_data = { 'usage2': [ '[opts] create MESSAGE_TEXT ADDRESS_SPEC [...]', '[opts] sign MESSAGE_FILE [WALLET_FILE ...]', - '[opts] verify MESSAGE_FILE or exported JSON dump', - '[opts] export MESSAGE_FILE', + '[opts] verify MESSAGE_FILE [MMGen ID]', + '[opts] verify [address]', + '[opts] export MESSAGE_FILE [MMGen ID]', ], 'options': """ --h, --help Print this help message ---, --longhelp Print help message for long options (common options) --d, --outdir=d Output file to directory 'd' instead of working dir --q, --quiet Produce quieter output +-h, --help Print this help message +--, --longhelp Print help message for long options (common options) +-d, --outdir=d Output file to directory 'd' instead of working dir +-q, --quiet Produce quieter output """, 'notes': """ @@ -168,6 +169,12 @@ $ mmgen-msg verify DEADBEEF:B:98 Export data relevant for a third-party verifier to ‘signatures.json’: $ mmgen-msg export + +Same as above, but export only one signature: +$ mmgen-msg export DEADBEEF:B:98 + +Verify and display the exported JSON signature data: +$ mmgen-msg verify signatures.json """ }, 'code': { diff --git a/mmgen/msg.py b/mmgen/msg.py index 32700506..4fd51142 100755 --- a/mmgen/msg.py +++ b/mmgen/msg.py @@ -171,10 +171,10 @@ class coin_msg: yield res hdr_data = { - 'message': ('Message:', lambda v: grnbg(v) ), - 'network': ('Network:', lambda v: v.replace('_',' ').upper() ), - 'addrlists': ('Address Ranges:', lambda v: fmt_list(v,fmt='bare') ), - 'failed_sids': ('Failed Seed IDs:', lambda v: red(fmt_list(v,fmt='bare')) ), + 'message': ('Message:', lambda v: grnbg(v) ), + 'network': ('Network:', lambda v: v.replace('_',' ').upper() ), + 'addrlists': ('Address Ranges:', lambda v: fmt_list(v,fmt='bare') ), + 'failed_sids': ('Failed Seed IDs:', lambda v: red(fmt_list(v,fmt='bare')) ), } if req_addr or type(self).__name__ == 'exported_sigs': @@ -332,8 +332,8 @@ class coin_msg: desc = self.desc ) ) - self.sigs = {sig['addr']:sig for sig in ( - [{k:v[2:] for k,v in e.items()} for e in self.data['signatures']] + self.sigs = {sig_data['addr']:sig_data for sig_data in ( + [{k:v[2:] for k,v in e.items()} for e in self.data['signatures']] # remove '0x' prefix if self.proto.base_proto == 'Ethereum' else self.data['signatures'] )} diff --git a/mmgen/tool/coin.py b/mmgen/tool/coin.py index 0c328895..becf3530 100755 --- a/mmgen/tool/coin.py +++ b/mmgen/tool/coin.py @@ -168,7 +168,6 @@ class tool_cmd(tool_cmd_base): def addr2pubhash(self,addr:'sstr'): "convert coin address to public key hash" - from ..opts import opt ap = self.proto.parse_addr(addr) assert ap, f'coin address {addr!r} could not be parsed' if ap.fmt not in MMGenAddrType.pkh_fmts: diff --git a/test/test_py_d/ts_ethdev.py b/test/test_py_d/ts_ethdev.py index 8f102198..f504e959 100755 --- a/test/test_py_d/ts_ethdev.py +++ b/test/test_py_d/ts_ethdev.py @@ -691,6 +691,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): imsg(f'Message: {self.message}') imsg(f'Signature: {sig}') cmp_or_die(sig,sig_chk,'message signatures') + imsg('Geth and MMGen signatures match') return 'ok'