Coverage for admin/onboard.py: 90%

92 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 

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 

42from comm.platform import Platform 

43 

44# TODO: this could perhaps be done with a different value. 

45# Currently unused but necessary for the attestation setup process. 

46ENDORSEMENT_CERTIFICATE = b"RSK_ENDORSEMENT_OK" 

47SEED_SIZE = 32 

48 

49 

50def do_onboard(options): 

51 head("### -> Onboarding and attestation setup", fill="#") 

52 hsm = None 

53 

54 # Ledger-only: require an output file 

55 if Platform.is_ledger() and options.output_file_path is None: 

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

57 

58 # Validate pin (if given) 

59 pin = None 

60 if options.pin is not None: 

61 if not BasePin.is_valid(options.pin.encode()): 

62 raise AdminError(PIN_ERROR_MESSAGE) 

63 pin = options.pin.encode() 

64 

65 # Connection 

66 hsm = get_hsm(options.verbose) 

67 

68 # Mode check 

69 info("Finding mode... ", options.verbose) 

70 mode = hsm.get_current_mode() 

71 info(f"Mode: {mode.name.capitalize()}") 

72 

73 # Require bootloader mode for onboarding 

74 if mode != HSM2Dongle.MODE.BOOTLOADER: 

75 raise AdminError("Device not in bootloader mode. " 

76 f"{Platform.message('restart').capitalize()} and try again") 

77 

78 # Echo check 

79 info("Sending echo... ", options.verbose) 

80 if not hsm.echo(): 

81 raise AdminError("Echo error") 

82 info("Echo OK") 

83 

84 info("Is device onboarded? ... ", options.verbose) 

85 is_onboarded = hsm.is_onboarded() 

86 info(f"Onboarded: {bls(is_onboarded)}") 

87 

88 if is_onboarded: 

89 raise AdminError("Device already onboarded") 

90 

91 message = "The following operation will onboard the device." 

92 head([ 

93 message, 

94 "Do you want to proceed? Yes/No", 

95 ]) 

96 while True: 

97 info("> ", False) 

98 answer = sys.stdin.readline().rstrip() 

99 if answer.lower() in ["n", "no"]: 

100 raise AdminError("Cancelled by user") 

101 if answer.lower() == "yes": 

102 break 

103 info("Please answer 'Yes' or 'No'") 

104 

105 # If we get here, then it means we need to onboard 

106 

107 # Ask the user for a pin if one not given 

108 if pin is None: 

109 info("Please select a pin for the device.") 

110 if not options.any_pin: 

111 info("The pin must be 8 characters long and contain " 

112 "at least one alphabetic character.") 

113 pin = ask_for_pin(any_pin=options.any_pin) 

114 

115 # Generate a random seed 

116 info("Generating a random seed... ", options.verbose) 

117 seed = gen_seed() 

118 info("Seed generated") 

119 

120 # Onboard 

121 info("Onboarding... ", options.verbose) 

122 hsm.onboard(seed, pin) 

123 info("Onboarded") 

124 

125 dispose_hsm(hsm) 

126 

127 if Platform.is_sgx(): 

128 head(["Onboarding done"]) 

129 return 

130 

131 if not Platform.is_ledger(): 

132 raise AdminError("Unsupported platform") 

133 

134 # **** Attestation setup is Ledger only (for now) **** 

135 head( 

136 [ 

137 "Onboarding done", 

138 "Please disconnect and re-connect the ledger to proceed with the attestation setup", # noqa E501 

139 "Press [Enter] to continue", 

140 ], 

141 nl=False, 

142 ) 

143 sys.stdin.readline() 

144 

145 # Wait for the dongle 

146 wait_for_reconnection() 

147 

148 # Unlock without executing the signer 

149 try: 

150 do_unlock(options, no_exec=True, label=False) 

151 except Exception as e: 

152 raise AdminError(f"Failed to unlock device: {str(e)}") 

153 

154 # Wait for the UI 

155 wait_for_reconnection() 

156 

157 # Connection for admin operations 

158 hsm = get_admin_hsm(options.verbose) 

159 

160 # Attestation setup 

161 info("Handshaking... ", options.verbose) 

162 hsm.handshake() 

163 info("Handshaking done") 

164 

165 info("Gathering device key... ", options.verbose) 

166 device_key_info = hsm.get_device_key() 

167 info("Device key gathered") 

168 

169 info("Setting up the attestation key... ", options.verbose) 

170 attestation_key_info = hsm.setup_endorsement_key( 

171 DongleAdmin.ENDORSEMENT_SCHEME.SCHEME_TWO, ENDORSEMENT_CERTIFICATE) 

172 info("Attestation key setup complete") 

173 

174 dispose_hsm(hsm) 

175 

176 # Generate and save the attestation certificate 

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

178 

179 att_cert = HSMCertificate() 

180 att_cert.add_element( 

181 HSMCertificateElement({ 

182 "name": "attestation", 

183 "message": attestation_key_info["message"], 

184 "signature": attestation_key_info["signature"], 

185 "signed_by": "device", 

186 })) 

187 att_cert.add_element( 

188 HSMCertificateElement({ 

189 "name": "device", 

190 "message": device_key_info["message"], 

191 "signature": device_key_info["signature"], 

192 "signed_by": "root", 

193 })) 

194 att_cert.add_target("attestation") 

195 att_cert.save_to_jsonfile(options.output_file_path) 

196 

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

198 

199 head([ 

200 "Onboarding and attestation setup done", 

201 "Please disconnect and re-connect the ledger before the first use", 

202 ]) 

203 

204 

205def gen_seed(): 

206 return os.urandom(SEED_SIZE)