util.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. # mmgen.util2.bytespec_map
  25. def bytespec(self,dd_style_byte_specifier:str):
  26. """
  27. convert a byte specifier such as ‘4GB’ into an integer
  28. Valid specifiers:
  29. c = 1
  30. w = 2
  31. b = 512
  32. kB = 1000
  33. K = 1024
  34. MB = 1000000
  35. M = 1048576
  36. GB = 1000000000
  37. G = 1073741824
  38. TB = 1000000000000
  39. T = 1099511627776
  40. PB = 1000000000000000
  41. P = 1125899906842624
  42. EB = 1000000000000000000
  43. E = 1152921504606846976
  44. """
  45. from ..util2 import parse_bytespec
  46. return parse_bytespec(dd_style_byte_specifier)
  47. # mmgen.util2.bytespec_map
  48. def to_bytespec(self,
  49. n: int,
  50. dd_style_byte_specifier: str,
  51. fmt: 'width and precision of output' = '0.2',
  52. print_sym: 'print the specifier after the numerical value' = True ):
  53. """
  54. convert an integer to a byte specifier such as ‘4GB’
  55. Supported specifiers:
  56. c = 1
  57. w = 2
  58. b = 512
  59. kB = 1000
  60. K = 1024
  61. MB = 1000000
  62. M = 1048576
  63. GB = 1000000000
  64. G = 1073741824
  65. TB = 1000000000000
  66. T = 1099511627776
  67. PB = 1000000000000000
  68. P = 1125899906842624
  69. EB = 1000000000000000000
  70. E = 1152921504606846976
  71. """
  72. from ..util2 import int2bytespec
  73. return int2bytespec( n, dd_style_byte_specifier, fmt, print_sym )
  74. def randhex(self,
  75. nbytes: 'number of bytes to output' = 32 ):
  76. "print 'n' bytes (default 32) of random data in hex format"
  77. from ..crypto import get_random
  78. return get_random( nbytes ).hex()
  79. def hexreverse(self,hexstr:'sstr'):
  80. "reverse bytes of a hexadecimal string"
  81. return bytes.fromhex( hexstr.strip() )[::-1].hex()
  82. def hexlify(self,infile:str):
  83. "convert bytes in file to hexadecimal (use '-' for stdin)"
  84. from ..fileutil import get_data_from_file
  85. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  86. return data.hex()
  87. def unhexlify(self,hexstr:'sstr'):
  88. "convert a hexadecimal string to bytes (warning: outputs binary data)"
  89. return bytes.fromhex(hexstr)
  90. def hexdump(self,
  91. infile: str,
  92. cols: 'number of columns in output' = 8,
  93. line_nums: "format for line numbers (valid choices: 'hex','dec')" = 'hex'):
  94. "create hexdump of data from file (use '-' for stdin)"
  95. from ..fileutil import get_data_from_file
  96. from ..util2 import pretty_hexdump
  97. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  98. return pretty_hexdump( data, cols=cols, line_nums=line_nums ).rstrip()
  99. def unhexdump(self,infile:str):
  100. "decode hexdump from file (use '-' for stdin) (warning: outputs binary data)"
  101. from ..globalvars import g
  102. if g.platform == 'win':
  103. import sys,os,msvcrt
  104. msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
  105. from ..fileutil import get_data_from_file
  106. from ..util2 import decode_pretty_hexdump
  107. hexdata = get_data_from_file( infile, dash=True, quiet=True )
  108. return decode_pretty_hexdump(hexdata)
  109. def hash160(self,hexstr:'sstr'):
  110. "compute ripemd160(sha256(data)) (convert hex pubkey to hex addr)"
  111. from ..proto.btc.common import hash160
  112. return hash160( bytes.fromhex(hexstr) ).hex()
  113. # TODO: handle stdin
  114. def hash256(self,
  115. data: str,
  116. file_input: 'first arg is the name of a file containing the data' = False,
  117. hex_input: 'first arg is a hexadecimal string' = False ):
  118. "compute sha256(sha256(data)) (double sha256)"
  119. from hashlib import sha256
  120. if file_input:
  121. from ..fileutil import get_data_from_file
  122. b = get_data_from_file( data, binary=True )
  123. elif hex_input:
  124. from ..util2 import decode_pretty_hexdump
  125. b = decode_pretty_hexdump(data)
  126. else:
  127. b = data
  128. return sha256(sha256(b.encode()).digest()).hexdigest()
  129. def id6(self,infile:str):
  130. "generate 6-character MMGen ID for a file (use '-' for stdin)"
  131. from ..util import make_chksum_6
  132. from ..fileutil import get_data_from_file
  133. return make_chksum_6(
  134. get_data_from_file( infile, dash=True, quiet=True, binary=True ))
  135. def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
  136. "generate 6-character MMGen ID for a string, ignoring spaces in string"
  137. from ..util import make_chksum_6
  138. return make_chksum_6( ''.join(string.split()) )
  139. def id8(self,infile:str):
  140. "generate 8-character MMGen ID for a file (use '-' for stdin)"
  141. from ..util import make_chksum_8
  142. from ..fileutil import get_data_from_file
  143. return make_chksum_8(
  144. get_data_from_file( infile, dash=True, quiet=True, binary=True ))
  145. def randb58(self,
  146. nbytes: 'number of bytes to output' = 32,
  147. pad: 'pad output to this width' = 0 ):
  148. "generate random data (default: 32 bytes) and convert it to base 58"
  149. from ..crypto import get_random
  150. from ..baseconv import baseconv
  151. return baseconv('b58').frombytes( get_random(nbytes), pad=pad, tostr=True )
  152. def bytestob58(self,infile:str,pad: 'pad output to this width' = 0):
  153. "convert bytes to base 58 (supply data via STDIN)"
  154. from ..fileutil import get_data_from_file
  155. from ..baseconv import baseconv
  156. data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
  157. return baseconv('b58').frombytes( data, pad=pad, tostr=True )
  158. def b58tobytes(self,b58_str:'sstr',pad: 'pad output to this width' = 0):
  159. "convert a base 58 string to bytes (warning: outputs binary data)"
  160. from ..baseconv import baseconv
  161. return baseconv('b58').tobytes( b58_str, pad=pad )
  162. def hextob58(self,hexstr:'sstr',pad: 'pad output to this width' = 0):
  163. "convert a hexadecimal string to base 58"
  164. from ..baseconv import baseconv
  165. return baseconv('b58').fromhex( hexstr, pad=pad, tostr=True )
  166. def b58tohex(self,b58_str:'sstr',pad: 'pad output to this width' = 0):
  167. "convert a base 58 string to hexadecimal"
  168. from ..baseconv import baseconv
  169. return baseconv('b58').tohex( b58_str, pad=pad )
  170. def hextob58chk(self,hexstr:'sstr'):
  171. "convert a hexadecimal string to base58-check encoding"
  172. from ..proto.btc.common import b58chk_encode
  173. return b58chk_encode( bytes.fromhex(hexstr) )
  174. def b58chktohex(self,b58chk_str:'sstr'):
  175. "convert a base58-check encoded string to hexadecimal"
  176. from ..proto.btc.common import b58chk_decode
  177. return b58chk_decode(b58chk_str).hex()
  178. def hextob32(self,hexstr:'sstr',pad: 'pad output to this width' = 0):
  179. "convert a hexadecimal string to an MMGen-flavor base 32 string"
  180. from ..baseconv import baseconv
  181. return baseconv('b32').fromhex( hexstr, pad, tostr=True )
  182. def b32tohex(self,b32_str:'sstr',pad: 'pad output to this width' = 0):
  183. "convert an MMGen-flavor base 32 string to hexadecimal"
  184. from ..baseconv import baseconv
  185. return baseconv('b32').tohex( b32_str.upper(), pad )
  186. def hextob6d(self,
  187. hexstr:'sstr',
  188. pad: 'pad output to this width' = 0,
  189. add_spaces: 'add a space after every 5th character' = True):
  190. "convert a hexadecimal string to die roll base6 (base6d)"
  191. from ..baseconv import baseconv
  192. from ..util2 import block_format
  193. ret = baseconv('b6d').fromhex(hexstr,pad,tostr=True)
  194. return block_format( ret, gw=5, cols=None ).strip() if add_spaces else ret
  195. def b6dtohex(self,b6d_str:'sstr',pad: 'pad output to this width' = 0):
  196. "convert a die roll base6 (base6d) string to hexadecimal"
  197. from ..baseconv import baseconv
  198. from ..util import remove_whitespace
  199. return baseconv('b6d').tohex( remove_whitespace(b6d_str), pad )