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

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. 

22 

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 

27 

28 

29def do_attestation(options): 

30 head("### -> Get UI and Signer attestations", fill="#") 

31 hsm = None 

32 

33 # Require an output file 

34 if options.output_file_path is None: 

35 raise AdminError("No output file path given") 

36 

37 # Load the given attestation key certificate 

38 if options.attestation_certificate_file_path is None: 

39 raise AdminError("No attestation certificate file given") 

40 

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)}") 

45 

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") 

50 

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)}") 

56 

57 # Connection 

58 hsm = get_hsm(options.verbose) 

59 

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") 

67 

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) 

79 

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") 

90 

91 # Augment and save the attestation certificate 

92 info("Generating the attestation certificate... ", options.verbose) 

93 

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) 

114 

115 info(f"Attestation certificate saved to {options.output_file_path}")