ut_baseconv.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #!/usr/bin/env python3
  2. """
  3. test/unit_tests_d/ut_baseconv.py: Base conversion unit test for the MMGen suite
  4. """
  5. from mmgen.common import *
  6. class unit_test(object):
  7. vectors = {
  8. 'b58': (
  9. (('00',None),'1'),
  10. (('00',1),'1'),
  11. (('00',2),'11'),
  12. (('01',None),'2'),
  13. (('01',1),'2'),
  14. (('01',2),'12'),
  15. (('0f',None),'G'),
  16. (('0f',1),'G'),
  17. (('0f',2),'1G'),
  18. (('deadbeef',None),'6h8cQN'),
  19. (('deadbeef',20),'111111111111116h8cQN'),
  20. (('00000000',None),'1'),
  21. (('00000000',20),'11111111111111111111'),
  22. (('ffffffff',None),'7YXq9G'),
  23. (('ffffffff',20),'111111111111117YXq9G'),
  24. (('ff'*16,'seed'),'YcVfxkQb6JRzqk5kF2tNLv'),
  25. (('ff'*24,'seed'),'QLbz7JHiBTspS962RLKV8GndWFwiEaqKL'),
  26. (('ff'*32,'seed'),'JEKNVnkbo3jma5nREBBJCDoXFVeKkD56V3xKrvRmWxFG'),
  27. (('00'*16,'seed'),'1111111111111111111111'),
  28. (('00'*24,'seed'),'111111111111111111111111111111111'),
  29. (('00'*32,'seed'),'11111111111111111111111111111111111111111111'),
  30. ),
  31. # MMGen-flavored base32 using simple base conversion
  32. 'b32': (
  33. (('00',None),'A'),
  34. (('00',1),'A'),
  35. (('00',2),'AA'),
  36. (('01',None),'B'),
  37. (('01',1),'B'),
  38. (('01',2),'AB'),
  39. (('0f',None),'P'),
  40. (('0f',1),'P'),
  41. (('0f',2),'AP'),
  42. (('deadbeef',None),'DPK3PXP'),
  43. (('deadbeef',20),'AAAAAAAAAAAAADPK3PXP'),
  44. (('00000000',None),'A'),
  45. (('00000000',20),'AAAAAAAAAAAAAAAAAAAA'),
  46. (('ffffffff',None),'D777777'),
  47. (('ffffffff',20),'AAAAAAAAAAAAAD777777'),
  48. ),
  49. 'b16': (
  50. (('00',None),'0'),
  51. (('00',1),'0'),
  52. (('00',2),'00'),
  53. (('01',None),'1'),
  54. (('01',1),'1'),
  55. (('01',2),'01'),
  56. (('0f',None),'f'),
  57. (('0f',1),'f'),
  58. (('0f',2),'0f'),
  59. (('deadbeef',None),'deadbeef'),
  60. (('deadbeef',20),'000000000000deadbeef'),
  61. (('00000000',None),'0'),
  62. (('00000000',20),'00000000000000000000'),
  63. (('ffffffff',None),'ffffffff'),
  64. (('ffffffff',20),'000000000000ffffffff'),
  65. ),
  66. 'b10': (
  67. (('00',None),'0'),
  68. (('00',1),'0'),
  69. (('00',2),'00'),
  70. (('01',None),'1'),
  71. (('01',1),'1'),
  72. (('01',2),'01'),
  73. (('0f',None),'15'),
  74. (('0f',1),'15'),
  75. (('0f',2),'15'),
  76. (('deadbeef',None),'3735928559'),
  77. (('deadbeef',20),'00000000003735928559'),
  78. (('00000000',None),'0'),
  79. (('00000000',20),'00000000000000000000'),
  80. (('ffffffff',None),'4294967295'),
  81. (('ffffffff',20),'00000000004294967295'),
  82. ),
  83. 'b8': (
  84. (('00',None),'0'),
  85. (('00',1),'0'),
  86. (('00',2),'00'),
  87. (('01',None),'1'),
  88. (('01',1),'1'),
  89. (('01',2),'01'),
  90. (('0f',None),'17'),
  91. (('0f',1),'17'),
  92. (('0f',2),'17'),
  93. (('deadbeef',None),'33653337357'),
  94. (('deadbeef',20),'00000000033653337357'),
  95. (('00000000',None),'0'),
  96. (('00000000',20),'00000000000000000000'),
  97. (('ffffffff',None),'37777777777'),
  98. (('ffffffff',20),'00000000037777777777'),
  99. ),
  100. 'b6d': (
  101. (('00',None),'1'),
  102. (('00',1),'1'),
  103. (('00',2),'11'),
  104. (('01',None),'2'),
  105. (('01',1),'2'),
  106. (('01',2),'12'),
  107. (('0f',None),'34'),
  108. (('0f',1),'34'),
  109. (('0f',2),'34'),
  110. (('010f',None),'2242'),
  111. (('010f',20),'11111111111111112242'),
  112. (('deadbeef',None),'2525524636426'),
  113. (('deadbeef',20),'11111112525524636426'),
  114. (('00000000',None),'1'),
  115. (('00000000',20),'11111111111111111111'),
  116. (('ffffffff',None),'2661215126614'),
  117. (('ffffffff',20),'11111112661215126614'),
  118. (('ff'*16,'seed'),'34164464641266661652465154654653354436664555521414'),
  119. (('ff'*24,'seed'),'246111411433323364222465566552324652566121541623426135163525451613554313654'),
  120. (('ff'*32,'seed'),'2132521653312613134145131423465414636252114131225324246311141642456513416322412146151432142242565134'),
  121. (('00'*16,'seed'),'1'*50),
  122. (('00'*24,'seed'),'1'*75),
  123. (('00'*32,'seed'),'1'*100),
  124. ),
  125. 'mmgen': (
  126. (('deadbeefdeadbeefdeadbeefdeadbeef','seed'),
  127. 'table cast forgive master funny gaze sadness ripple million paint moral match' ),
  128. (('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef','seed'),
  129. 'swirl maybe anymore mix scale stray fog use approach page crime rhyme ' +
  130. 'class former strange window snap soon'),
  131. (('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef','seed'),
  132. 'swell type milk figure cheese phone fill black test bloom heard comfort ' +
  133. 'image terrible radio lesson own reply battle goal goodbye need laugh stream'),
  134. (('ffffffffffffffffffffffffffffffff','seed'),
  135. 'yellow yeah show bowl season spider cling defeat poison law shelter reflect'),
  136. (('ffffffffffffffffffffffffffffffffffffffffffffffff','seed'),
  137. 'yeah youth quit fail perhaps drum out person young click skin ' +
  138. 'weird inside silently perfectly together anyone memory'),
  139. (('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff','seed'),
  140. 'wrote affection object cell opinion here laughter stare honest north cost begin ' +
  141. 'murder something yourself effort acid dot doubt game broke tell guilt innocent'),
  142. (('00000000000000000000000000000001','seed'),
  143. 'able ' * 11 + 'about'),
  144. (('000000000000000000000000000000000000000000000001','seed'),
  145. 'able ' * 17 + 'about'),
  146. (('0000000000000000000000000000000000000000000000000000000000000001','seed'),
  147. 'able ' * 23 + 'about'),
  148. ),
  149. }
  150. def run_test(self,name,ut):
  151. msg_r('Testing base conversion routines...')
  152. from mmgen.baseconv import baseconv
  153. perr = "length of {!r} less than pad length ({})"
  154. rerr = "return value ({!r}) does not match reference value ({!r})"
  155. qmsg_r('\nChecking hex-to-base conversion:')
  156. for base,data in self.vectors.items():
  157. fs = " {h:%s} {p:<6} {r}" % max(len(d[0][0]) for d in data)
  158. if not opt.verbose:
  159. qmsg_r(f' {base}')
  160. vmsg(f'\nBase: {base}')
  161. vmsg(fs.format(h='Input',p='Pad',r='Output'))
  162. for (hexstr,pad),ret_chk in data:
  163. ret = baseconv(base).fromhex(hexstr,pad=pad,tostr=True)
  164. if pad != 'seed':
  165. assert len(ret) >= (pad or 0), perr.format(ret,pad or 0)
  166. assert ret == ret_chk, rerr.format(ret,ret_chk)
  167. vmsg(fs.format(h=hexstr,r=ret,p=str(pad)))
  168. # msg("(('{h}',{p}),'{r}'),".format(h=hexstr,r=ret,c=ret_chk,p=pad))
  169. # msg('')
  170. # return True
  171. qmsg_r('\nChecking base-to-hex conversion:')
  172. for base,data in self.vectors.items():
  173. fs = " {h:%s} {p:<6} {r}" % max(len(d[1]) for d in data)
  174. if not opt.verbose:
  175. qmsg_r(f' {base}')
  176. vmsg(f'\nBase: {base}')
  177. vmsg(fs.format(h='Input',p='Pad',r='Output'))
  178. for (hexstr,pad),ret_chk in data:
  179. if type(pad) == int:
  180. pad = len(hexstr)
  181. ret = baseconv(base).tohex( ret_chk.split() if base == 'mmgen' else ret_chk, pad=pad )
  182. if pad == None:
  183. assert int(ret,16) == int(hexstr,16), rerr.format(int(ret,16),int(hexstr,16))
  184. else:
  185. assert ret == hexstr, rerr.format(ret,hexstr)
  186. vmsg(fs.format(h=ret_chk,r=ret,p=str(pad)))
  187. # msg("(('{h}',{p}),'{r}'),".format(h=hexstr,r=ret_chk,c=ret_chk,p=pad))
  188. qmsg_r('\nChecking wordlist checksums:')
  189. vmsg('')
  190. for wl_id in baseconv.constants['wl_chksum']:
  191. vmsg_r(f' {wl_id+":":9}')
  192. baseconv(wl_id).check_wordlist()
  193. qmsg('')
  194. qmsg('Checking error handling:')
  195. bad_b58 = 'I'*22
  196. bad_b58len = 'a'*23
  197. fr58 = baseconv('b58').fromhex
  198. to58 = baseconv('b58').tohex
  199. to32 = baseconv('b32').tohex
  200. to8 = baseconv('b8').tohex
  201. bad_data = (
  202. ('hexstr', 'HexadecimalStringError', ': not a hexadecimal str', lambda:fr58('x')),
  203. ('hexstr (seed)', 'HexadecimalStringError', 'seed data not a hexadec', lambda:fr58('x',pad='seed')),
  204. ('hexstr (empty)', 'BaseConversionError', 'empty data not allowed', lambda:fr58('')),
  205. ('b58 data', 'BaseConversionError', ': not in base58', lambda:to58('IfFzZ')),
  206. ('b58 data (seed)', 'BaseConversionError', 'seed data not in base58', lambda:to58(bad_b58,pad='seed')),
  207. ('b58 len (seed)', 'BaseConversionError', 'invalid length for', lambda:to58(bad_b58len,pad='seed')),
  208. ('b58 data (empty)','BaseConversionError', 'empty base58 data', lambda:to58('')),
  209. ('b8 data (empty)' ,'BaseConversionError', 'empty base8 string data', lambda:to8('')),
  210. ('b32 data', 'BaseConversionError', 'not in MMGen base32', lambda:to32('1az')),
  211. ('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad='foo')),
  212. ('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad=False)),
  213. ('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad=True)),
  214. ('seedlen (in)', 'SeedLengthError', 'invalid byte length', lambda:fr58('ff',pad='seed')),
  215. ('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad='foo')),
  216. ('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad=False)),
  217. ('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad=True)),
  218. ('seedlen (out)', 'BaseConversionError', 'invalid length for seed', lambda:to58('Z',pad='seed')),
  219. )
  220. ut.process_bad_data(bad_data)
  221. msg('OK')
  222. return True