[tool]: accept stdin input for selected utils

[bitcoin/seed]: input sanitizing and checking for base conversion routines
[test]: replace env var with --popen-spawn option
This commit is contained in:
philemon 2016-12-15 14:08:03 +03:00
commit c7fcf448d9
Signed by untrusted user who does not match committer: mmgen
GPG key ID: 62DBE9E5212F05BE
10 changed files with 75 additions and 46 deletions

View file

@ -54,12 +54,12 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
from mmgen.globalvars import g
def pubhex2hexaddr(pubhex):
step1 = sha256(unhexlify(pubhex)).digest()
step1 = sha256(unhexlify(pubhex.strip())).digest()
return hashlib_new('ripemd160',step1).hexdigest()
def hexaddr2addr(hexaddr,p2sh=False):
# devdoc/ref_transactions.md:
hexaddr2 = ('00','6f','05','c4')[g.testnet+(2*p2sh)] + hexaddr
hexaddr2 = ('00','6f','05','c4')[g.testnet+(2*p2sh)] + hexaddr.strip()
step1 = sha256(unhexlify(hexaddr2)).digest()
step2 = sha256(step1).hexdigest()
pubkey = hexaddr2 + step2[:8]
@ -67,6 +67,7 @@ def hexaddr2addr(hexaddr,p2sh=False):
return ('1' * lzeroes) + _numtob58(int(pubkey,16))
def verify_addr(addr,verbose=False,return_hex=False):
addr = addr.strip()
for vers_num,ldigit in ('00','1'),('05','3'),('6f','mn'),('c4','2'):
if addr[0] not in ldigit: continue
num = _b58tonum(addr)
@ -95,6 +96,7 @@ def _numtob58(num):
return ''.join(ret)[::-1]
def _b58tonum(b58num):
b58num = b58num.strip()
for i in b58num:
if not i in b58a: return False
return sum([b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1]))])
@ -110,6 +112,7 @@ def b58encode(s):
return _numtob58(num)
def b58decode(b58num):
b58num = b58num.strip()
if b58num == '': return ''
# Zap all spaces:
# Use translate() only with str, not unicode
@ -146,6 +149,7 @@ def b58decode_pad(s):
# Compressed address support:
def wif2hex(wif):
wif = wif.strip()
compressed = wif[0] != ('5','9')[g.testnet]
idx = (66,68)[bool(compressed)]
num = _b58tonum(wif)
@ -157,7 +161,7 @@ def wif2hex(wif):
return key[2:66] if (key[:2] == ('80','ef')[g.testnet] and key[idx:] == round2[:8]) else False
def hex2wif(hexpriv,compressed=False):
step1 = ('80','ef')[g.testnet] + hexpriv + ('','01')[bool(compressed)]
step1 = ('80','ef')[g.testnet] + hexpriv.strip() + ('','01')[bool(compressed)]
step2 = sha256(unhexlify(step1)).digest()
step3 = sha256(step2).hexdigest()
key = step1 + step3[:8]

View file

@ -52,7 +52,7 @@ for c in _colors:
else:
globals()['_16_'+c] = '\033[{};{}m'.format(*e[1])
globals()['_clr_'+c] = ''; _reset = ''
exec "def {c}(s): return _clr_{c}+s+_reset".format(c=c)
exec 'def {c}(s): return _clr_{c}+s+_reset'.format(c=c)
def nocolor(s): return s

View file

@ -31,7 +31,7 @@ def launch(what):
__import__('mmgen.main_' + what)
else:
import sys,os,atexit
if not os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
if sys.stdin.isatty():
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
def at_exit(): termios.tcsetattr(fd, termios.TCSADRAIN, old)

View file

@ -145,6 +145,7 @@ class BitcoinRPCConnection(object):
'createrawtransaction',
'backupwallet',
'decoderawtransaction',
'disconnectnode',
'estimatefee',
'getaddressesbyaccount',
'getbalance',
@ -152,6 +153,7 @@ class BitcoinRPCConnection(object):
'getblockcount',
'getblockhash',
'getinfo',
'getpeerinfo',
'importaddress',
'listaccounts',
'listunspent',

View file

@ -368,7 +368,12 @@ class Mnemonic (SeedSourceUnenc):
def _hex2mn_pad(hexnum): return len(hexnum) * 3 / 8
@staticmethod
def baseNtohex(base,words,wl,pad=0):
def baseNtohex(base,words_arg,wl,pad=0): # accepts both string and list input
words = words_arg
if type(words) not in (list,tuple):
words = tuple(words.strip())
if not set(words).issubset(set(wl)):
die(2,'{} is not in base-{} format'.format(repr(words_arg),base))
deconv = [wl.index(words[::-1][i])*(base**i)
for i in range(len(words))]
ret = ('{:0%sx}' % pad).format(sum(deconv))
@ -376,6 +381,9 @@ class Mnemonic (SeedSourceUnenc):
@staticmethod
def hextobaseN(base,hexnum,wl,pad=0):
hexnum = hexnum.strip()
if not is_hexstring(hexnum):
die(2,"'%s': not a hexadecimal number" % hexnum)
num,ret = int(hexnum,16),[]
while num:
ret.append(num % base)

View file

@ -33,7 +33,7 @@ except:
_platform = 'win'
except:
die(2,'Unable to set terminal mode')
if os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
if not sys.stdin.isatty():
msvcrt.setmode(sys.stdin.fileno(),os.O_BINARY)
def _kb_hold_protect_unix():
@ -140,7 +140,7 @@ def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None):
if ord(ch) == 3: raise KeyboardInterrupt
return ch
def _get_keypress_mswin_emu(prompt='',immed_chars='',prehold_protect=None):
def _get_keypress_mswin_stub(prompt='',immed_chars='',prehold_protect=None):
msg_r(prompt)
return sys.stdin.read(1)
@ -200,12 +200,12 @@ def set_terminal_vars():
if _platform == 'linux':
get_char = (_get_keypress_unix_raw,_get_keypress_unix)[g.hold_protect]
kb_hold_protect = (_kb_hold_protect_unix_raw,_kb_hold_protect_unix)[g.hold_protect]
if os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
if not sys.stdin.isatty():
get_char,kb_hold_protect = _get_keypress_unix_stub,_kb_hold_protect_unix_raw
get_terminal_size = _get_terminal_size_linux
else:
get_char = (_get_keypress_mswin_raw,_get_keypress_mswin)[g.hold_protect]
kb_hold_protect = (_kb_hold_protect_mswin_raw,_kb_hold_protect_mswin)[g.hold_protect]
if os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
get_char = _get_keypress_mswin_emu
if not sys.stdin.isatty():
get_char = _get_keypress_mswin_stub
get_terminal_size = _get_terminal_size_mswin

View file

@ -38,7 +38,7 @@ def cleandir(d):
rmtree(os.path.join(d,f))
def getrandnum(n): return int(hexlify(os.urandom(n)),16)
def getrandhex(n): return hexlify(os.urandom(n))
def getrandhex(n): return hexlify(os.urandom(n).lstrip('0'))
def getrandstr(num_chars,no_space=False):
n,m = 95,32
if no_space: n,m = 94,33

View file

@ -33,38 +33,38 @@ from collections import OrderedDict
cmd_data = OrderedDict([
('help', ['<tool command> [str]']),
('usage', ['<tool command> [str]']),
('strtob58', ['<string> [str]']),
('b58tostr', ['<b58 number> [str]']),
('hextob58', ['<hex number> [str]']),
('b58tohex', ['<b58 number> [str]']),
('strtob58', ['<string> [str-]']),
('b58tostr', ['<b58 number> [str-]']),
('hextob58', ['<hex number> [str-]']),
('b58tohex', ['<b58 number> [str-]']),
('b58randenc', []),
('b32tohex', ['<b32 num> [str]']),
('hextob32', ['<hex num> [str]']),
('b32tohex', ['<b32 num> [str-]']),
('hextob32', ['<hex num> [str-]']),
('randhex', ['nbytes [int=32]']),
('id8', ['<infile> [str]']),
('id6', ['<infile> [str]']),
('sha256x2', ['<str, hexstr or filename> [str]',
('sha256x2', ['<str, hexstr or filename> [str]', # TODO handle stdin
'hex_input [bool=False]','file_input [bool=False]']),
('str2id6', ['<string (spaces are ignored)> [str]']),
('str2id6', ['<string (spaces are ignored)> [str-]']),
('hexdump', ['<infile> [str]', 'cols [int=8]', 'line_nums [bool=True]']),
('unhexdump', ['<infile> [str]']),
('hexreverse', ['<hexadecimal string> [str]']),
('hexlify', ['<string> [str]']),
('hexreverse', ['<hexadecimal string> [str-]']),
('hexlify', ['<string> [str-]']),
('rand2file', ['<outfile> [str]','<nbytes> [str]','threads [int=4]','silent [bool=False]']),
('randwif', ['compressed [bool=False]']),
('randpair', ['compressed [bool=False]']),
('hex2wif', ['<private key in hex format> [str]', 'compressed [bool=False]']),
('wif2hex', ['<wif> [str]', 'compressed [bool=False]']),
('wif2addr', ['<wif> [str]', 'compressed [bool=False]']),
('hexaddr2addr', ['<btc address in hex format> [str]']),
('addr2hexaddr', ['<btc address> [str]']),
('pubkey2addr', ['<public key in hex format> [str]']),
('pubkey2hexaddr', ['<public key in hex format> [str]']),
('privhex2addr', ['<private key in hex format> [str]','compressed [bool=False]']),
('hex2wif', ['<private key in hex format> [str-]', 'compressed [bool=False]']),
('wif2hex', ['<wif> [str-]', 'compressed [bool=False]']),
('wif2addr', ['<wif> [str-]', 'compressed [bool=False]']),
('hexaddr2addr', ['<btc address in hex format> [str-]']),
('addr2hexaddr', ['<btc address> [str-]']),
('pubkey2addr', ['<public key in hex format> [str-]']),
('pubkey2hexaddr', ['<public key in hex format> [str-]']),
('privhex2addr', ['<private key in hex format> [str-]','compressed [bool=False]']),
('hex2mn', ['<hexadecimal string> [str]',"wordlist [str='electrum']"]),
('mn2hex', ['<mnemonic> [str]', "wordlist [str='electrum']"]),
('hex2mn', ['<hexadecimal string> [str-]',"wordlist [str='electrum']"]),
('mn2hex', ['<mnemonic> [str-]', "wordlist [str='electrum']"]),
('mn_rand128', ["wordlist [str='electrum']"]),
('mn_rand192', ["wordlist [str='electrum']"]),
('mn_rand256', ["wordlist [str='electrum']"]),
@ -162,7 +162,10 @@ def tool_usage(prog_name, command):
if ' ' + command in line:
c,h = line.split('-',1)
Msg('MMGEN-TOOL {}: {}'.format(c.strip().upper(),h.strip()))
msg('USAGE: %s %s %s' % (prog_name, command, ' '.join(cmd_data[command])))
cd = cmd_data[command]
if cd and cd[0][-2:] == '-]':
cd[0] = cd[0][:-2] + ' or STDIN]'
msg('USAGE: %s %s %s' % (prog_name, command, ' '.join(cd)))
else:
msg("'%s': no such tool command" % command)
sys.exit(1)
@ -176,10 +179,19 @@ def process_args(prog_name, command, cmd_args):
] for i in cmd_data[command] if '=' in i])
u_args = [a for a in cmd_args[:len(c_args)]]
if c_args and c_args[0][1][-1] == '-':
c_args[0][1] = c_args[0][1][:-1] # [str-] -> [str]
# If we're reading from a pipe, make the input the first argument
if len(u_args) < len(c_kwargs) + len(c_args):
if not sys.stdin.isatty():
u_args = [sys.stdin.read()] + u_args
if len(u_args) < len(c_args):
msg('Command requires exactly %s non-keyword argument%s' % (len(c_args),suf(c_args,'k')))
m1 = 'Command requires exactly %s non-keyword argument%s'
msg(m1 % (len(c_args),suf(c_args,'k')))
tool_usage(prog_name,command)
# print u_args
extra_args = len(cmd_args) - len(c_args)
u_kwargs = {}
if extra_args > 0:
@ -260,20 +272,23 @@ def strtob58(s):
print_convert_results(s,enc,dec,'str')
def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode):
s = s.strip()
enc = f_enc(ba.unhexlify(s))
dec = ba.hexlify(f_dec(enc))
print_convert_results(s,enc,dec,'hex')
def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
s = s.strip()
tmp = f_enc(s)
if tmp == False: sys.exit(1)
if tmp == False: die(1,"Unable to decode string '%s'" % s)
enc = ba.hexlify(tmp)
dec = f_dec(ba.unhexlify(enc))
print_convert_results(s,enc,dec,'b58')
def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
s = s.strip()
enc = f_enc(s)
if enc == False: sys.exit(1)
if enc == False: die(1,"Unable to decode string '%s'" % s)
dec = f_dec(enc)
print_convert_results(s,enc,dec,'b58')
@ -334,7 +349,7 @@ def mn2hex(s,wordlist=dfl_wordlist):
def b32tohex(s):
b32a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
Msg(Mnemonic.baseNtohex(32,s,b32a))
Msg(Mnemonic.baseNtohex(32,s.upper(),b32a))
def hextob32(s):
b32a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
@ -355,7 +370,8 @@ def id6(infile):
Msg(make_chksum_6(
get_data_from_file(infile,dash=True,silent=True,binary=True)
))
def str2id6(s): Msg(make_chksum_6(''.join(s.split())))
def str2id6(s): # retain ignoring of space for backwards compat
Msg(make_chksum_6(''.join(s.split())))
# List MMGen addresses and their balances:
def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=False):
@ -487,8 +503,8 @@ def keyaddrfile_chksum(infile):
from mmgen.addr import KeyAddrList
KeyAddrList(infile,chksum_only=True)
def hexreverse(hex_str):
Msg(ba.hexlify(decode_pretty_hexdump(hex_str)[::-1]))
def hexreverse(s):
Msg(ba.hexlify(ba.unhexlify(s.strip())[::-1]))
def hexlify(s):
Msg(ba.hexlify(s))

View file

@ -493,7 +493,7 @@ def write_data_to_file(
if opt.stdout or outfile in ('','-'):
do_stdout()
elif not sys.stdout.isatty() and not os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
elif sys.stdin.isatty() and not sys.stdout.isatty():
do_stdout()
else:
do_file(outfile,ask_write_prompt)
@ -581,7 +581,7 @@ def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
from mmgen.term import kb_hold_protect
kb_hold_protect()
if echo or os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
if echo or not sys.stdin.isatty():
reply = raw_input(prompt)
else:
from getpass import getpass

View file

@ -142,6 +142,7 @@ opts_data = {
-L, --log Log commands to file {lf}
-n, --names Display command names instead of descriptions
-I, --interactive Interactive mode (without pexpect)
-O, --popen-spawn Use pexpect's popen_spawn instead of popen
-p, --pause Pause between tests, resuming on keypress
-P, --profile Record the execution time of each script
-q, --quiet Produce minimal output. Suppress dependency info
@ -668,7 +669,7 @@ if opt.list_cmds:
import time,re
if g.platform == 'linux':
import pexpect
if os.getenv('MMGEN_PEXPECT_POPEN_SPAWN'):
if opt.popen_spawn:
import termios,atexit
def at_exit(): os.system('stty sane')
atexit.register(at_exit)
@ -690,8 +691,6 @@ else: # Windows
if not keypress_confirm(green(m1)+grnbg(m2)+green(m3),default_yes=True):
errmsg('Exiting at user request')
sys.exit()
else:
os.environ['MMGEN_PEXPECT_POPEN_SPAWN'] = '1'
def my_send(p,t,delay=send_delay,s=False):
if delay: time.sleep(delay)