Coverage for admin/ledger_attestation.py: 96%
53 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 .misc import info, head, get_hsm, dispose_hsm, AdminError, wait_for_reconnection, \
24 get_ud_value_for_attestation
25from .unlock import do_unlock
26from .certificate import HSMCertificate, HSMCertificateElement
29def do_attestation(options):
30 head("### -> Get UI and Signer attestations", fill="#")
31 hsm = None
33 # Require an output file
34 if options.output_file_path is None:
35 raise AdminError("No output file path given")
37 # Load the given attestation key certificate
38 if options.attestation_certificate_file_path is None:
39 raise AdminError("No attestation certificate file given")
41 try:
42 att_cert = HSMCertificate.from_jsonfile(options.attestation_certificate_file_path)
43 except Exception as e:
44 raise AdminError(f"While loading the attestation certificate file: {str(e)}")
46 # Get the UD value for the attestations
47 info("Gathering user-defined attestation value... ", options.verbose)
48 ud_value = get_ud_value_for_attestation(options.attestation_ud_source)
49 info(f"Using {ud_value} as the user-defined attestation value")
51 # Attempt to unlock the device without exiting the UI
52 try:
53 do_unlock(options, label=False, exit=False)
54 except Exception as e:
55 raise AdminError(f"Failed to unlock device: {str(e)}")
57 # Connection
58 hsm = get_hsm(options.verbose)
60 # UI Attestation
61 info("Gathering UI attestation... ", options.verbose)
62 try:
63 ui_attestation = hsm.get_ui_attestation(ud_value)
64 except Exception as e:
65 raise AdminError(f"Failed to gather UI attestation: {str(e)}")
66 info("UI attestation gathered")
68 # Exit the UI and reconnect
69 info("Exiting UI... ", options.verbose)
70 try:
71 hsm.exit_menu()
72 except Exception:
73 # exit_menu() always throws due to USB disconnection. we don't care
74 pass
75 info("Exit OK")
76 dispose_hsm(hsm)
77 wait_for_reconnection()
78 hsm = get_hsm(options.verbose)
80 # Signer attestation
81 info("Gathering Signer attestation... ", options.verbose)
82 try:
83 powhsm_attestation = hsm.get_powhsm_attestation(ud_value)
84 # Health check: message and envelope must be the same
85 if powhsm_attestation["message"] != powhsm_attestation["envelope"]:
86 raise AdminError("Signer attestation message and envelope differ")
87 except Exception as e:
88 raise AdminError(f"Failed to gather Signer attestation: {str(e)}")
89 info("Signer attestation gathered")
91 # Augment and save the attestation certificate
92 info("Generating the attestation certificate... ", options.verbose)
94 att_cert.add_element(
95 HSMCertificateElement({
96 "name": "ui",
97 "message": ui_attestation["message"],
98 "signature": ui_attestation["signature"],
99 "signed_by": "attestation",
100 "tweak": ui_attestation["app_hash"],
101 }))
102 att_cert.add_element(
103 HSMCertificateElement({
104 "name": "signer",
105 "message": powhsm_attestation["message"],
106 "signature": powhsm_attestation["signature"],
107 "signed_by": "attestation",
108 "tweak": powhsm_attestation["app_hash"],
109 }))
110 att_cert.clear_targets()
111 att_cert.add_target("ui")
112 att_cert.add_target("signer")
113 att_cert.save_to_jsonfile(options.output_file_path)
115 info(f"Attestation certificate saved to {options.output_file_path}")