main_autosign.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2023 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. autosign: Auto-sign MMGen transactions, message files and XMR wallet output files
  20. """
  21. import sys
  22. from .cfg import Config
  23. from .util import msg,die,fmt_list,exit_if_mswin,async_run
  24. exit_if_mswin('autosigning')
  25. opts_data = {
  26. 'sets': [('stealth_led', True, 'led', True)],
  27. 'text': {
  28. 'desc': 'Auto-sign MMGen transactions, message files and XMR wallet output files',
  29. 'usage':'[opts] [operation]',
  30. 'options': """
  31. -h, --help Print this help message
  32. --, --longhelp Print help message for long options (common options)
  33. -c, --coins=c Coins to sign for (comma-separated list)
  34. -C, --clean Remove unneeded files on the removable device
  35. -I, --no-insert-check Don’t check for device insertion
  36. -l, --led Use status LED to signal standby, busy and error
  37. -m, --mountpoint=M Specify an alternate mountpoint 'M'
  38. (default: {asi.dfl_mountpoint!r})
  39. -M, --mnemonic-fmt=F During setup, prompt for mnemonic seed phrase of format
  40. 'F' (choices: {mn_fmts}; default: {asi.dfl_mn_fmt!r})
  41. -n, --no-summary Don’t print a transaction summary
  42. -s, --stealth-led Stealth LED mode - signal busy and error only, and only
  43. after successful authorization.
  44. -S, --full-summary Print a full summary of each signed transaction after
  45. each autosign run. The default list of non-MMGen outputs
  46. will not be printed.
  47. -q, --quiet Produce quieter output
  48. -v, --verbose Produce more verbose output
  49. -w, --wallet-dir=D Specify an alternate wallet dir
  50. (default: {asi.dfl_wallet_dir!r})
  51. -x, --xmrwallets=L Range or list of wallets to be used for XMR autosigning
  52. """,
  53. 'notes': """
  54. OPERATIONS
  55. gen_key - generate the wallet encryption key and copy it to the mountpoint
  56. {asi.mountpoint!r} (as currently configured)
  57. setup - generate both wallet encryption key and temporary signing wallet
  58. wait - start in loop mode: wait-mount-sign-unmount-wait
  59. USAGE NOTES
  60. If no operation is specified, this program mounts a removable device
  61. (typically a USB flash drive) containing unsigned MMGen transactions, message
  62. files, and/or XMR wallet output files, signs them, unmounts the removable
  63. device and exits.
  64. If invoked with ‘wait’, the program waits in a loop, mounting the removable
  65. device, performing signing operations and unmounting the device every time it
  66. is inserted.
  67. On supported platforms (currently Orange Pi, Rock Pi and Raspberry Pi boards),
  68. the status LED indicates whether the program is busy or in standby mode, i.e.
  69. ready for device insertion or removal.
  70. The removable device must have a partition labeled MMGEN_TX with a user-
  71. writable root directory and a directory named ‘/tx’, where unsigned MMGen
  72. transactions are placed. Optionally, the directory ‘/msg’ may be created
  73. and unsigned message files produced by ‘mmgen-msg’ placed there.
  74. On both the signing and online machines the mountpoint ‘{asi.mountpoint}’
  75. (as currently configured) must exist and ‘/etc/fstab’ must contain the
  76. following entry:
  77. LABEL=MMGEN_TX {asi.mountpoint} auto noauto,user 0 0
  78. Signing is performed with a temporary wallet created in volatile memory in
  79. the directory ‘{asi.wallet_dir}’ (as currently configured). The wallet is
  80. encrypted with a 32-byte password saved in the file ‘autosign.key’ in the
  81. root of the removable device’s filesystem.
  82. The password and temporary wallet may be created in one operation by invoking
  83. ‘mmgen-autosign setup’ with the removable device inserted. In this case, the
  84. temporary wallet is created from the user’s default wallet, if it exists and
  85. the user so desires. If not, the user is prompted to enter a seed phrase.
  86. Alternatively, the password and temporary wallet may be created separately by
  87. first invoking ‘mmgen-autosign gen_key’ and then creating and encrypting the
  88. wallet using the -P (--passwd-file) option:
  89. $ mmgen-walletconv -iwords -d{asi.wallet_dir} -p1 -N -P{asi.mountpoint}/autosign.key -Lfoo
  90. Note that the hash preset must be ‘1’. To use a wallet file as the source
  91. instead of an MMGen seed phrase, omit the ‘-i’ option and add the wallet
  92. file path to the end of the command line. Multiple temporary wallets may
  93. be created in this way and used for signing (note, however, that for XMR
  94. operations only one wallet is supported).
  95. Autosigning is currently available only on Linux-based platforms.
  96. SECURITY NOTE
  97. By placing wallet and password on separate devices, this program creates
  98. a two-factor authentication setup whereby an attacker must gain physical
  99. control of both the removable device and signing machine in order to sign
  100. transactions. It’s therefore recommended to always keep the removable device
  101. secure, separated from the signing machine and hidden (in your pocket, for
  102. example) when not transacting. In addition, since login access on the
  103. signing machine is required to steal the user’s seed, it’s good practice
  104. to lock the signing machine’s screen once the setup process is complete.
  105. As a last resort, cutting power to the signing machine will destroy the
  106. volatile memory where the temporary wallet resides and foil any attack,
  107. even if you’ve lost control of the removable device.
  108. Always remember to power off the signing machine when your signing session
  109. is over.
  110. """
  111. },
  112. 'code': {
  113. 'options': lambda s: s.format(
  114. asi = asi,
  115. mn_fmts = fmt_list( asi.mn_fmts, fmt='no_spc' ),
  116. ),
  117. 'notes': lambda s: s.format(asi=asi)
  118. }
  119. }
  120. def main(do_loop):
  121. asi.init_led()
  122. asi.init_exit_handler()
  123. async def do():
  124. await asi.check_daemons_running()
  125. if do_loop:
  126. await asi.do_loop()
  127. else:
  128. ret = await asi.do_sign()
  129. asi.at_exit(not ret)
  130. async_run(do())
  131. from .autosign import Autosign,AutosignConfig
  132. cfg = AutosignConfig(
  133. opts_data = opts_data,
  134. init_opts = {
  135. 'quiet': True,
  136. 'out_fmt': 'wallet',
  137. 'usr_randchars': 0,
  138. 'hash_preset': '1',
  139. 'label': 'Autosign Wallet',
  140. },
  141. do_post_init = True )
  142. cmd_args = cfg._args
  143. asi = Autosign(cfg)
  144. cfg._post_init()
  145. if len(cmd_args) not in (0,1):
  146. cfg._opts.usage()
  147. if cmd_args:
  148. cmd = cmd_args[0]
  149. if cmd == 'gen_key':
  150. asi.gen_key()
  151. elif cmd == 'setup':
  152. asi.setup()
  153. from .ui import keypress_confirm
  154. if cfg.xmrwallets and keypress_confirm( cfg, '\nContinue with Monero setup?', default_yes=True ):
  155. msg('')
  156. asi.xmr_setup()
  157. elif cmd == 'wait':
  158. main(do_loop=True)
  159. elif cmd == 'clean':
  160. asi.do_mount()
  161. asi.clean_old_files()
  162. asi.do_umount()
  163. else:
  164. die(1,f'{cmd!r}: unrecognized command')
  165. else:
  166. main(do_loop=False)