123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- #!/usr/bin/env python3
- #
- # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
- # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- autosign: Auto-sign MMGen transactions, message files and XMR wallet output files
- """
- from .util import msg,die,fmt_list,exit_if_mswin,async_run
- exit_if_mswin('autosigning')
- opts_data = {
- 'sets': [('stealth_led', True, 'led', True)],
- 'text': {
- 'desc': 'Auto-sign MMGen transactions, message files and XMR wallet output files',
- 'usage':'[opts] [operation]',
- 'options': """
- -h, --help Print this help message
- --, --longhelp Print help message for long options (common options)
- -c, --coins=c Coins to sign for (comma-separated list)
- -I, --no-insert-check Don’t check for device insertion
- -l, --seed-len=N Specify wallet seed length of ‘N’ bits (for setup only)
- -L, --led Use status LED to signal standby, busy and error
- -m, --mountpoint=M Specify an alternate mountpoint 'M'
- (default: {asi.dfl_mountpoint!r})
- -M, --mnemonic-fmt=F During setup, prompt for mnemonic seed phrase of format
- 'F' (choices: {mn_fmts}; default: {asi.dfl_mn_fmt!r})
- -n, --no-summary Don’t print a transaction summary
- -s, --stealth-led Stealth LED mode - signal busy and error only, and only
- after successful authorization.
- -S, --full-summary Print a full summary of each signed transaction after
- each autosign run. The default list of non-MMGen outputs
- will not be printed.
- -q, --quiet Produce quieter output
- -v, --verbose Produce more verbose output
- -w, --wallet-dir=D Specify an alternate wallet dir
- (default: {asi.dfl_wallet_dir!r})
- -x, --xmrwallets=L Range or list of wallets to be used for XMR autosigning
- """,
- 'notes': """
- OPERATIONS
- clean - clean the removable device of unneeded files, removing only non-
- essential data
- gen_key - generate the wallet encryption key and copy it to the removable
- device mounted at mountpoint ‘{asi.mountpoint}’ (as currently
- configured)
- setup - generate both wallet encryption key and temporary signing wallet
- xmr_setup - set up temporary Monero signing wallets. This operation needn’t
- be performed by the user directly in most cases, as Monero setup
- is done by the ‘setup’ command when --xmrwallets is specified
- wait - start in loop mode: wait-mount-sign-unmount-wait
- wipe_key - wipe the wallet encryption key on the removable device, making
- signing transactions or stealing the user’s seed impossible.
- The operation is intended as a ‘kill switch’ and thus performed
- without prompting
- USAGE NOTES
- If no operation is specified, this program mounts a removable device
- (typically a USB flash drive) containing unsigned MMGen transactions, message
- files, and/or XMR wallet output files, signs them, unmounts the removable
- device and exits.
- If invoked with ‘wait’, the program waits in a loop, mounting the removable
- device, performing signing operations and unmounting the device every time it
- is inserted.
- On supported platforms (currently Orange Pi, Rock Pi and Raspberry Pi boards),
- the status LED indicates whether the program is busy or in standby mode, i.e.
- ready for device insertion or removal.
- The removable device must have a partition with a filesystem labeled MMGEN_TX
- and a user-writable root directory. For interoperability between OS-es, it’s
- recommended to use the exFAT file system.
- On both the signing and online machines the mountpoint ‘{asi.mountpoint}’
- (as currently configured) must exist. Linux (not macOS) machines must have
- an ‘/etc/fstab’ with the following entry:
- LABEL=MMGEN_TX {asi.mountpoint} auto noauto,user 0 0
- Signing is performed with a temporary wallet created in volatile memory in
- the directory ‘{asi.wallet_dir}’ (as currently configured). The wallet is
- encrypted with a 32-byte password saved in the file ‘autosign.key’ in the
- root of the removable device’s filesystem.
- The password and temporary wallet may be created in one operation by invoking
- ‘mmgen-autosign setup’ with the removable device inserted. In this case, the
- temporary wallet is created from the user’s default wallet, if it exists and
- the user so desires. If not, the user is prompted to enter a seed phrase.
- Alternatively, the password and temporary wallet may be created separately by
- first invoking ‘mmgen-autosign gen_key’ and then creating and encrypting the
- wallet using the -P (--passwd-file) option:
- $ mmgen-walletconv -iwords -d{asi.wallet_dir} -p1 -N -P{asi.mountpoint}/autosign.key -Lfoo
- Note that the hash preset must be ‘1’. To use a wallet file as the source
- instead of an MMGen seed phrase, omit the ‘-i’ option and add the wallet
- file path to the end of the command line. Multiple temporary wallets may
- be created in this way and used for signing (note, however, that for XMR
- operations only one wallet is supported).
- Autosigning is currently supported on Linux and macOS only.
- SECURITY NOTE
- By placing wallet and password on separate devices, this program creates
- a two-factor authentication setup whereby an attacker must gain physical
- control of both the removable device and signing machine in order to sign
- transactions. It’s therefore recommended to always keep the removable device
- secure, separated from the signing machine and hidden (in your pocket, for
- example) when not transacting. In addition, since login access on the
- signing machine is required to steal the user’s seed, it’s good practice
- to lock the signing machine’s screen once the setup process is complete.
- As a last resort, cutting power to the signing machine will destroy the
- volatile memory where the temporary wallet resides and foil any attack,
- even if you’ve lost control of the removable device.
- Always remember to power off the signing machine when your signing session
- is over.
- """
- },
- 'code': {
- 'options': lambda s: s.format(
- asi = asi,
- mn_fmts = fmt_list( asi.mn_fmts, fmt='no_spc' ),
- ),
- 'notes': lambda s: s.format(asi=asi)
- }
- }
- def main(do_loop):
- asi.init_led()
- asi.init_exit_handler()
- async def do():
- await asi.check_daemons_running()
- if do_loop:
- await asi.main_loop()
- else:
- ret = await asi.do_sign()
- asi.at_exit(not ret)
- async_run(do())
- from .cfg import Config
- from .autosign import Autosign
- cfg = Config(
- opts_data = opts_data,
- init_opts = {
- 'quiet': True,
- 'out_fmt': 'wallet',
- 'usr_randchars': 0,
- 'hash_preset': '1',
- 'label': 'Autosign Wallet',
- },
- do_post_init = True )
- cmd = cfg._args[0] if len(cfg._args) == 1 else 'sign' if not cfg._args else cfg._opts.usage()
- valid_cmds = ('gen_key', 'setup', 'xmr_setup', 'sign', 'wait', 'clean', 'wipe_key')
- if cmd not in valid_cmds:
- die(1,f'‘{cmd}’: unrecognized command')
- if cmd != 'setup':
- for opt in ('seed_len', 'mnemonic_fmt'):
- if getattr(cfg,opt):
- die(1, f'--{opt.replace("_","-")} makes sense only for the ‘setup’ operation')
- if cmd not in ('sign', 'wait'):
- for opt in ('no_summary', 'led', 'stealth_led', 'full_summary'):
- if getattr(cfg,opt):
- die(1, f'--{opt.replace("_","-")} makes no sense for the ‘{cmd}’ operation')
- asi = Autosign(cfg,cmd)
- cfg._post_init()
- if cmd == 'gen_key':
- asi.gen_key()
- elif cmd == 'setup':
- asi.setup()
- from .ui import keypress_confirm
- if cfg.xmrwallets and keypress_confirm( cfg, '\nContinue with Monero setup?', default_yes=True ):
- msg('')
- asi.xmr_setup()
- asi.do_umount()
- elif cmd == 'xmr_setup':
- if not cfg.xmrwallets:
- die(1,'Please specify a wallet or range of wallets with the --xmrwallets option')
- asi.do_mount()
- asi.xmr_setup()
- asi.do_umount()
- elif cmd == 'sign':
- main(do_loop=False)
- elif cmd == 'wait':
- main(do_loop=True)
- elif cmd == 'clean':
- asi.do_mount()
- asi.clean_old_files()
- asi.do_umount()
- elif cmd == 'wipe_key':
- asi.do_mount()
- asi.wipe_encryption_key()
- asi.do_umount()
|