ts_wallet.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2020 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. ts_wallet.py: Wallet conversion tests for the test.py test suite
  20. """
  21. import os
  22. from mmgen.opts import opt
  23. from mmgen.seed import *
  24. from .common import *
  25. from .ts_base import *
  26. from .ts_shared import *
  27. class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
  28. 'wallet conversion to and from reference data'
  29. networks = ('btc',)
  30. tmpdir_nums = [11,12,13]
  31. sources = { '128': {
  32. 'ref_wallet': 'FE3C6545-D782B529[128,1].mmdat',
  33. 'ic_wallet': 'FE3C6545-E29303EA-5E229E30[128,1].mmincog',
  34. 'ic_wallet_hex': 'FE3C6545-BC4BE3F2-32586837[128,1].mmincox',
  35. 'hic_wallet': 'FE3C6545-161E495F-BEB7548E[128,1].incog-offset123',
  36. 'hic_wallet_old': 'FE3C6545-161E495F-9860A85B[128,1].incog-old.offset123',
  37. },
  38. '192': {
  39. 'ref_wallet': '1378FC64-6F0F9BB4[192,1].mmdat',
  40. 'ic_wallet': '1378FC64-2907DE97-F980D21F[192,1].mmincog',
  41. 'ic_wallet_hex': '1378FC64-4DCB5174-872806A7[192,1].mmincox',
  42. 'hic_wallet': '1378FC64-B55E9958-77256FC1[192,1].incog.offset123',
  43. 'hic_wallet_old': '1378FC64-B55E9958-D85FF20C[192,1].incog-old.offset123',
  44. },
  45. '256': {
  46. 'ref_wallet': '98831F3A-{}[256,1].mmdat'.format(('27F2BF93','E2687906')[g.testnet]),
  47. 'ic_wallet': '98831F3A-5482381C-18460FB1[256,1].mmincog',
  48. 'ic_wallet_hex': '98831F3A-1630A9F2-870376A9[256,1].mmincox',
  49. 'hic_wallet': '98831F3A-F59B07A0-559CEF19[256,1].incog.offset123',
  50. 'hic_wallet_old': '98831F3A-F59B07A0-848535F3[256,1].incog-old.offset123',
  51. },
  52. }
  53. cmd_group = (
  54. # reading
  55. ('ref_wallet_conv', 'conversion of saved reference wallet'),
  56. ('ref_mn_conv', 'conversion of saved MMGen native mnemonic'),
  57. ('ref_bip39_conv', 'conversion of saved BIP39 mnemonic'),
  58. ('ref_seed_conv', 'conversion of saved seed file'),
  59. ('ref_hex_conv', 'conversion of saved MMGen hexadecimal seed file'),
  60. ('ref_plainhex_conv', 'conversion of saved plain hexadecimal seed file'),
  61. ('ref_dieroll_conv', 'conversion of saved dieroll (b6d) seed file'),
  62. ('ref_brain_conv', 'conversion of ref brainwallet'),
  63. ('ref_incog_conv', 'conversion of saved incog wallet'),
  64. ('ref_incox_conv', 'conversion of saved hex incog wallet'),
  65. ('ref_hincog_conv', 'conversion of saved hidden incog wallet'),
  66. ('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)'),
  67. # writing
  68. ('ref_wallet_conv_out', 'ref seed conversion to wallet'),
  69. ('ref_mn_conv_out', 'ref seed conversion to MMGen native mnemonic'),
  70. ('ref_bip39_conv_out', 'ref seed conversion to BIP39 mnemonic'),
  71. ('ref_hex_conv_out', 'ref seed conversion to MMGen hex seed'),
  72. ('ref_plainhex_conv_out','ref seed conversion to plain hex seed'),
  73. ('ref_dieroll_conv_out','ref seed conversion to dieroll (b6d) seed'),
  74. ('ref_seed_conv_out', 'ref seed conversion to seed'),
  75. ('ref_incog_conv_out', 'ref seed conversion to incog data'),
  76. ('ref_incox_conv_out', 'ref seed conversion to hex incog data'),
  77. ('ref_hincog_conv_out', 'ref seed conversion to hidden incog data'),
  78. ('ref_hincog_blkdev_conv_out', 'ref seed conversion to hidden incog data on block device')
  79. )
  80. def __init__(self,trunner,cfgs,spawn):
  81. for k,j in self.cmd_group:
  82. for n in (1,2,3):
  83. setattr(self,'{}_{}'.format(k,n),getattr(self,k))
  84. return TestSuiteBase.__init__(self,trunner,cfgs,spawn)
  85. def ref_wallet_conv(self):
  86. wf = joinpath(ref_dir,self.sources[str(self.seed_len)]['ref_wallet'])
  87. return self.walletconv_in(wf,oo=True)
  88. def ref_mn_conv(self,ext='mmwords'):
  89. wf = joinpath(ref_dir,self.seed_id+'.'+ext)
  90. return self.walletconv_in(wf,oo=True)
  91. def ref_bip39_conv(self): return self.ref_mn_conv(ext='bip39')
  92. def ref_seed_conv(self): return self.ref_mn_conv(ext='mmseed')
  93. def ref_hex_conv(self): return self.ref_mn_conv(ext='mmhex')
  94. def ref_plainhex_conv(self): return self.ref_mn_conv(ext='hex')
  95. def ref_dieroll_conv(self): return self.ref_mn_conv(ext='b6d')
  96. def ref_brain_conv(self):
  97. uopts = ['-i','b','-p','1','-l',str(self.seed_len)]
  98. return self.walletconv_in(None,uopts,oo=True,icls=Brainwallet)
  99. def ref_incog_conv(self,wfk='ic_wallet',in_fmt='i'):
  100. uopts = ['-i',in_fmt,'-p','1','-l',str(self.seed_len)]
  101. wf = joinpath(ref_dir,self.sources[str(self.seed_len)][wfk])
  102. return self.walletconv_in(wf,uopts,oo=True)
  103. def ref_incox_conv(self):
  104. return self.ref_incog_conv(in_fmt='xi',wfk='ic_wallet_hex')
  105. def ref_hincog_conv(self,wfk='hic_wallet',add_uopts=[]):
  106. ic_f = joinpath(ref_dir,self.sources[str(self.seed_len)][wfk])
  107. uopts = ['-i','hi','-p','1','-l',str(self.seed_len)] + add_uopts
  108. hi_opt = ['-H','{},{}'.format(ic_f,ref_wallet_incog_offset)]
  109. return self.walletconv_in(None,uopts+hi_opt,oo=True,icls=IncogWalletHidden)
  110. def ref_hincog_conv_old(self):
  111. return self.ref_hincog_conv(wfk='hic_wallet_old',add_uopts=['-O'])
  112. def ref_wallet_conv_out(self): return self.walletconv_out('w')
  113. def ref_mn_conv_out(self): return self.walletconv_out('mn')
  114. def ref_bip39_conv_out(self): return self.walletconv_out('bip39')
  115. def ref_seed_conv_out(self): return self.walletconv_out('seed')
  116. def ref_hex_conv_out(self): return self.walletconv_out('hexseed')
  117. def ref_plainhex_conv_out(self): return self.walletconv_out('hex')
  118. def ref_dieroll_conv_out(self): return self.walletconv_out('dieroll')
  119. def ref_incog_conv_out(self): return self.walletconv_out('i')
  120. def ref_incox_conv_out(self): return self.walletconv_out('xi')
  121. def ref_hincog_conv_out(self,ic_f=None):
  122. if not ic_f:
  123. ic_f = joinpath(self.tmpdir,hincog_fn)
  124. hi_parms = '{},{}'.format(ic_f,ref_wallet_incog_offset)
  125. sl_parm = '-l' + str(self.seed_len)
  126. return self.walletconv_out('hi',
  127. uopts = ['-J',hi_parms,sl_parm],
  128. uopts_chk = ['-H',hi_parms,sl_parm] )
  129. def ref_hincog_blkdev_conv_out(self):
  130. def do_run(cmd):
  131. from subprocess import run,PIPE,DEVNULL
  132. return run(cmd,stdout=PIPE,stderr=DEVNULL,check=True)
  133. if self.skip_for_win(): return 'skip'
  134. imsg('Creating block device image file')
  135. ic_img = joinpath(self.tmpdir,'hincog_blkdev_img')
  136. do_run(['dd','if=/dev/zero','of='+ic_img,'bs=1K','count=1'])
  137. ic_dev = do_run(['sudo','/sbin/losetup','-f']).stdout.strip().decode()
  138. ic_dev_mode_orig = '{:o}'.format(os.stat(ic_dev).st_mode & 0xfff)
  139. ic_dev_mode = '0666'
  140. imsg("Changing permissions on loop device to '{}'".format(ic_dev_mode))
  141. do_run(['sudo','chmod',ic_dev_mode,ic_dev])
  142. imsg("Attaching loop device '{}'".format(ic_dev))
  143. do_run(['sudo','/sbin/losetup',ic_dev,ic_img])
  144. self.ref_hincog_conv_out(ic_f=ic_dev)
  145. imsg("Detaching loop device '{}'".format(ic_dev))
  146. do_run(['sudo','/sbin/losetup','-d',ic_dev])
  147. imsg("Resetting permissions on loop device to '{}'".format(ic_dev_mode_orig))
  148. do_run(['sudo','chmod',ic_dev_mode_orig,ic_dev])
  149. return 'ok'
  150. # wallet conversion tests
  151. def walletconv_in(self,infile,uopts=[],oo=False,icls=None):
  152. ocls = MMGenMnemonic
  153. opts = ['-d',self.tmpdir,'-o',ocls.fmt_codes[0],self.usr_rand_arg]
  154. if_arg = [infile] if infile else []
  155. d = '(convert)'
  156. t = self.spawn('mmgen-walletconv',opts+uopts+if_arg,extra_desc=d)
  157. t.license()
  158. icls = icls or SeedSource.ext_to_type(get_extension(infile))
  159. if icls == Brainwallet:
  160. t.expect('Enter brainwallet: ',ref_wallet_brainpass+'\n')
  161. pw = issubclass(icls,SeedSourceEnc) and icls != Brainwallet
  162. if pw:
  163. t.passphrase(icls.desc,self.wpasswd)
  164. if self.test_name[:19] == 'ref_hincog_conv_old':
  165. t.expect('Is the Seed ID correct? (Y/n): ','\n')
  166. else:
  167. t.expect(['Passphrase is OK',' are correct'])
  168. wf = t.written_to_file(capfirst(ocls.desc),oo=oo)
  169. t.p.wait()
  170. # back check of result
  171. msg('' if opt.profile else ' OK')
  172. return self.walletchk( wf,
  173. pf = None,
  174. extra_desc = '(check)',
  175. sid = self.seed_id )
  176. def walletconv_out(self,out_fmt='w',uopts=[],uopts_chk=[]):
  177. wcls = SeedSource.fmt_code_to_type(out_fmt)
  178. opts = ['-d',self.tmpdir,'-p1','-o',out_fmt] + uopts
  179. infile = joinpath(ref_dir,self.seed_id+'.mmwords')
  180. t = self.spawn('mmgen-walletconv',[self.usr_rand_arg]+opts+[infile],extra_desc='(convert)')
  181. add_args = ['-l{}'.format(self.seed_len)]
  182. t.license()
  183. pw = issubclass(wcls,SeedSourceEnc) and wcls != Brainwallet
  184. if pw:
  185. t.passphrase_new('new '+wcls.desc,self.wpasswd)
  186. t.usr_rand(self.usr_rand_chars)
  187. if wcls in (IncogWallet,IncogWalletHex,IncogWalletHidden):
  188. for i in (1,2,3):
  189. t.expect('Encrypting OS random data with key')
  190. if wcls == IncogWalletHidden:
  191. t.hincog_create(hincog_bytes)
  192. if out_fmt == 'w':
  193. t.label()
  194. wf = t.written_to_file(capfirst(wcls.desc),oo=True)
  195. pf = None
  196. if wcls == IncogWalletHidden:
  197. add_args += uopts_chk
  198. wf = None
  199. msg('' if opt.profile else ' OK')
  200. return self.walletchk( wf,
  201. pf = pf,
  202. wcls = wcls,
  203. extra_desc = '(check)',
  204. sid = self.seed_id,
  205. add_args = add_args )