Coverage for admin/onboard.py: 93%
84 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 20:41 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 20:41 +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.
23import sys
24import os
25from ledger.hsm2dongle import HSM2Dongle
26from ledger.pin import BasePin
27from .misc import (
28 info,
29 head,
30 bls,
31 get_hsm,
32 get_admin_hsm,
33 dispose_hsm,
34 PIN_ERROR_MESSAGE,
35 AdminError,
36 ask_for_pin,
37 wait_for_reconnection,
38)
39from .dongle_admin import DongleAdmin
40from .unlock import do_unlock
41from .certificate import HSMCertificate, HSMCertificateElement
43# TODO: this could perhaps be done with a different value.
44# Currently unused but necessary for the attestation setup process.
45ENDORSEMENT_CERTIFICATE = b"RSK_ENDORSEMENT_OK"
46SEED_SIZE = 32
49def do_onboard(options):
50 head("### -> Onboarding and attestation setup", fill="#")
51 hsm = None
53 # Require an output file
54 if options.output_file_path is None:
55 raise AdminError("No output file path given")
57 # Validate pin (if given)
58 pin = None
59 if options.pin is not None:
60 if not BasePin.is_valid(options.pin.encode()):
61 raise AdminError(PIN_ERROR_MESSAGE)
62 pin = options.pin.encode()
64 # Connection
65 hsm = get_hsm(options.verbose)
67 # Mode check
68 info("Finding mode... ", options.verbose)
69 mode = hsm.get_current_mode()
70 info(f"Mode: {mode.name.capitalize()}")
72 # Require bootloader mode for onboarding
73 if mode != HSM2Dongle.MODE.BOOTLOADER:
74 raise AdminError("Device not in bootloader mode. Disconnect and re-connect the "
75 "ledger and try again")
77 # Echo check
78 info("Sending echo... ", options.verbose)
79 if not hsm.echo():
80 raise AdminError("Echo error")
81 info("Echo OK")
83 info("Is device onboarded? ... ", options.verbose)
84 is_onboarded = hsm.is_onboarded()
85 info(f"Onboarded: {bls(is_onboarded)}")
87 if is_onboarded:
88 raise AdminError("Device already onboarded")
90 message = "The following operation will onboard the device."
91 head([
92 message,
93 "Do you want to proceed? Yes/No",
94 ])
95 while True:
96 info("> ", False)
97 answer = sys.stdin.readline().rstrip()
98 if answer.lower() in ["n", "no"]:
99 raise AdminError("Cancelled by user")
100 if answer.lower() == "yes":
101 break
102 info("Please answer 'Yes' or 'No'")
104 # If we get here, then it means we need to onboard
106 # Ask the user for a pin if one not given
107 if pin is None:
108 info("Please select a pin for the device.")
109 pin = ask_for_pin(any_pin=options.any_pin)
111 # Generate a random seed
112 info("Generating a random seed... ", options.verbose)
113 seed = gen_seed()
114 info("Seed generated")
116 # Onboard
117 info("Onboarding... ", options.verbose)
118 hsm.onboard(seed, pin)
119 info("Onboarded")
121 dispose_hsm(hsm)
123 head(
124 [
125 "Onboarding done",
126 "Please disconnect and re-connect the ledger to proceed with the attestation setup", # noqa E501
127 "Press [Enter] to continue",
128 ],
129 nl=False,
130 )
131 sys.stdin.readline()
133 # Wait for the dongle
134 wait_for_reconnection()
136 # Unlock without executing the signer
137 try:
138 do_unlock(options, no_exec=True, label=False)
139 except Exception as e:
140 raise AdminError(f"Failed to unlock device: {str(e)}")
142 # Wait for the UI
143 wait_for_reconnection()
145 # Connection for admin operations
146 hsm = get_admin_hsm(options.verbose)
148 # Attestation setup
149 info("Handshaking... ", options.verbose)
150 hsm.handshake()
151 info("Handshaking done")
153 info("Gathering device key... ", options.verbose)
154 device_key_info = hsm.get_device_key()
155 info("Device key gathered")
157 info("Setting up the attestation key... ", options.verbose)
158 attestation_key_info = hsm.setup_endorsement_key(
159 DongleAdmin.ENDORSEMENT_SCHEME.SCHEME_TWO, ENDORSEMENT_CERTIFICATE)
160 info("Attestation key setup complete")
162 dispose_hsm(hsm)
164 # Generate and save the attestation certificate
165 info("Generating the attestation certificate... ", options.verbose)
167 att_cert = HSMCertificate()
168 att_cert.add_element(
169 HSMCertificateElement({
170 "name": "attestation",
171 "message": attestation_key_info["message"],
172 "signature": attestation_key_info["signature"],
173 "signed_by": "device",
174 }))
175 att_cert.add_element(
176 HSMCertificateElement({
177 "name": "device",
178 "message": device_key_info["message"],
179 "signature": device_key_info["signature"],
180 "signed_by": "root",
181 }))
182 att_cert.add_target("attestation")
183 att_cert.save_to_jsonfile(options.output_file_path)
185 info(f"Attestation certificate saved to {options.output_file_path}")
187 head([
188 "Onboarding and attestation setup done",
189 "Please disconnect and re-connect the ledger before the first use",
190 ])
193def gen_seed():
194 return os.urandom(SEED_SIZE)