Coverage for admin/unlock.py: 89%
47 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23from ledger.hsm2dongle import HSM2Dongle
24from ledger.pin import BasePin
25from .misc import (
26 info,
27 head,
28 bls,
29 get_hsm,
30 dispose_hsm,
31 PIN_ERROR_MESSAGE_ANYCHARS,
32 AdminError,
33 ask_for_pin,
34)
35from comm.platform import Platform
38def do_unlock(options, exit=True, no_exec=False, label=True):
39 if label:
40 head("### -> Unlock", fill="#")
42 hsm = None
44 # Validate pin (if given)
45 pin = None
46 if options.pin is not None:
47 if not BasePin.is_valid(options.pin.encode(), options.any_pin):
48 raise AdminError(PIN_ERROR_MESSAGE_ANYCHARS)
49 pin = options.pin.encode()
51 # Connection
52 hsm = get_hsm(options.verbose)
54 # Mode check
55 info("Finding mode... ", options.verbose)
56 mode = hsm.get_current_mode()
57 info(f"Mode: {mode.name.capitalize()}")
59 # Onboard check
60 if mode in [HSM2Dongle.MODE.BOOTLOADER, HSM2Dongle.MODE.SIGNER]:
61 info("Is device onboarded? ... ", options.verbose)
62 is_onboarded = hsm.is_onboarded()
63 info(f"Onboarded: {bls(is_onboarded)}")
64 if not is_onboarded:
65 raise AdminError("Device not onboarded")
67 # Modes for which we can't unlock
68 if mode == HSM2Dongle.MODE.UNKNOWN:
69 raise AdminError("Device mode unknown. Already unlocked? Otherwise "
70 f"{Platform.message('restart')} and try again")
71 if mode == HSM2Dongle.MODE.SIGNER or mode == HSM2Dongle.MODE.UI_HEARTBEAT:
72 raise AdminError("Device already unlocked")
74 # Echo check
75 info("Sending echo... ", options.verbose)
76 if not hsm.echo():
77 raise AdminError("Echo error")
78 info("Echo OK")
80 # Ask the user for a pin if one not given
81 if pin is None:
82 info("Please enter the pin.")
83 pin = ask_for_pin(any_pin=True)
85 # Unlock device with PIN
86 info("Unlocking with PIN... ", options.verbose)
87 if not hsm.unlock(pin):
88 raise AdminError("Unable to unlock: PIN mismatch")
89 info("PIN accepted")
91 # **** Ledger only ****
92 # Exit the bootloader, go into menu (or, if app is properly signed, into
93 # the app)
94 if Platform.is_ledger() and exit:
95 autoexec = not (options.no_exec or no_exec)
96 info(f"Exiting to menu/app (execute signer: {bls(autoexec)})... ",
97 options.verbose)
98 try:
99 hsm.exit_menu(autoexec=autoexec)
100 except Exception:
101 # exit_menu() always throws due to USB disconnection. we don't care
102 pass
103 info("Exit OK")
105 dispose_hsm(hsm)