util.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. tool/util.py: Utility commands for the 'mmgen-tool' utility
  20. """
  21. from .common import tool_cmd_base
  22. class tool_cmd(tool_cmd_base):
  23. "general string conversion and hashing utilities"
  24. def bytespec(self,dd_style_byte_specifier:str):
  25. "convert a byte specifier such as '1GB' into an integer"
  26. from ..util import parse_bytespec
  27. return parse_bytespec(dd_style_byte_specifier)
  28. def to_bytespec(self,
  29. n: int,
  30. dd_style_byte_specifier: str,
  31. fmt = '0.2',
  32. print_sym = True ):
  33. "convert an integer to a byte specifier such as '1GB'"
  34. from ..util import int2bytespec
  35. return int2bytespec( n, dd_style_byte_specifier, fmt, print_sym )
  36. def randhex(self,nbytes='32'):
  37. "print 'n' bytes (default 32) of random data in hex format"
  38. from ..crypto import get_random
  39. return get_random( int(nbytes) ).hex()
  40. def hexreverse(self,hexstr:'sstr'):
  41. "reverse bytes of a hexadecimal string"
  42. return bytes.fromhex( hexstr.strip() )[::-1].hex()
  43. def hexlify(self,infile:str):
  44. "convert bytes in file to hexadecimal (use '-' for stdin)"
  45. from ..fileutil import get_data_from_file
  46. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  47. return data.hex()
  48. def unhexlify(self,hexstr:'sstr'):
  49. "convert hexadecimal value to bytes (warning: outputs binary data)"
  50. return bytes.fromhex(hexstr)
  51. def hexdump(self,infile:str,cols=8,line_nums='hex'):
  52. "create hexdump of data from file (use '-' for stdin)"
  53. from ..fileutil import get_data_from_file
  54. from ..util import pretty_hexdump
  55. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  56. return pretty_hexdump( data, cols=cols, line_nums=line_nums ).rstrip()
  57. def unhexdump(self,infile:str):
  58. "decode hexdump from file (use '-' for stdin) (warning: outputs binary data)"
  59. from ..globalvars import g
  60. if g.platform == 'win':
  61. import msvcrt
  62. msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
  63. from ..fileutil import get_data_from_file
  64. from ..util import decode_pretty_hexdump
  65. hexdata = get_data_from_file( infile, dash=True, quiet=True )
  66. return decode_pretty_hexdump(hexdata)
  67. def hash160(self,hexstr:'sstr'):
  68. "compute ripemd160(sha256(data)) (convert hex pubkey to hex addr)"
  69. from ..proto.common import hash160
  70. return hash160( bytes.fromhex(hexstr) ).hex()
  71. def hash256(self,string_or_bytes:str,file_input=False,hex_input=False): # TODO: handle stdin
  72. "compute sha256(sha256(data)) (double sha256)"
  73. from hashlib import sha256
  74. if file_input:
  75. from ..fileutil import get_data_from_file
  76. b = get_data_from_file( string_or_bytes, binary=True )
  77. elif hex_input:
  78. from ..util import decode_pretty_hexdump
  79. b = decode_pretty_hexdump(string_or_bytes)
  80. else:
  81. b = string_or_bytes
  82. return sha256(sha256(b.encode()).digest()).hexdigest()
  83. def id6(self,infile:str):
  84. "generate 6-character MMGen ID for a file (use '-' for stdin)"
  85. from ..util import make_chksum_6
  86. from ..fileutil import get_data_from_file
  87. return make_chksum_6(
  88. get_data_from_file( infile, dash=True, quiet=True, binary=True ))
  89. def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
  90. "generate 6-character MMGen ID for a string, ignoring spaces"
  91. from ..util import make_chksum_6
  92. return make_chksum_6( ''.join(string.split()) )
  93. def id8(self,infile:str):
  94. "generate 8-character MMGen ID for a file (use '-' for stdin)"
  95. from ..util import make_chksum_8
  96. from ..fileutil import get_data_from_file
  97. return make_chksum_8(
  98. get_data_from_file( infile, dash=True, quiet=True, binary=True ))
  99. def randb58(self,nbytes=32,pad=0):
  100. "generate random data (default: 32 bytes) and convert it to base 58"
  101. from ..crypto import get_random
  102. from ..baseconv import baseconv
  103. return baseconv('b58').frombytes( get_random(nbytes), pad=pad, tostr=True )
  104. def bytestob58(self,infile:str,pad=0):
  105. "convert bytes to base 58 (supply data via STDIN)"
  106. from ..fileutil import get_data_from_file
  107. from ..baseconv import baseconv
  108. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  109. return baseconv('b58').frombytes( data, pad=pad, tostr=True )
  110. def b58tobytes(self,b58num:'sstr',pad=0):
  111. "convert a base 58 number to bytes (warning: outputs binary data)"
  112. from ..baseconv import baseconv
  113. return baseconv('b58').tobytes( b58num, pad=pad )
  114. def hextob58(self,hexstr:'sstr',pad=0):
  115. "convert a hexadecimal number to base 58"
  116. from ..baseconv import baseconv
  117. return baseconv('b58').fromhex( hexstr, pad=pad, tostr=True )
  118. def b58tohex(self,b58num:'sstr',pad=0):
  119. "convert a base 58 number to hexadecimal"
  120. from ..baseconv import baseconv
  121. return baseconv('b58').tohex( b58num, pad=pad )
  122. def hextob58chk(self,hexstr:'sstr'):
  123. "convert a hexadecimal number to base58-check encoding"
  124. from ..proto.common import b58chk_encode
  125. return b58chk_encode( bytes.fromhex(hexstr) )
  126. def b58chktohex(self,b58chk_num:'sstr'):
  127. "convert a base58-check encoded number to hexadecimal"
  128. from ..proto.common import b58chk_decode
  129. return b58chk_decode(b58chk_num).hex()
  130. def hextob32(self,hexstr:'sstr',pad=0):
  131. "convert a hexadecimal number to MMGen's flavor of base 32"
  132. from ..baseconv import baseconv
  133. return baseconv('b32').fromhex( hexstr, pad, tostr=True )
  134. def b32tohex(self,b32num:'sstr',pad=0):
  135. "convert an MMGen-flavor base 32 number to hexadecimal"
  136. from ..baseconv import baseconv
  137. return baseconv('b32').tohex( b32num.upper(), pad )
  138. def hextob6d(self,hexstr:'sstr',pad=0,add_spaces=True):
  139. "convert a hexadecimal number to die roll base6 (base6d)"
  140. from ..baseconv import baseconv
  141. from ..util import block_format
  142. ret = baseconv('b6d').fromhex(hexstr,pad,tostr=True)
  143. return block_format( ret, gw=5, cols=None ).strip() if add_spaces else ret
  144. def b6dtohex(self,b6d_num:'sstr',pad=0):
  145. "convert a die roll base6 (base6d) number to hexadecimal"
  146. from ..baseconv import baseconv
  147. from ..util import remove_whitespace
  148. return baseconv('b6d').tohex( remove_whitespace(b6d_num), pad )