Coverage for admin/pubkeys.py: 97%
65 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 os
24import json
25import ecdsa
26from ledger.hsm2dongle import HSM2Dongle
27from .misc import info, get_hsm, dispose_hsm, AdminError, wait_for_reconnection
28from .unlock import do_unlock
29from comm.bip32 import BIP32Path
31SIGNER_WAIT_TIME = 1 # second
33PATHS = {
34 "btc": BIP32Path("m/44'/0'/0'/0/0"),
35 "rsk": BIP32Path("m/44'/137'/0'/0/0"),
36 "mst": BIP32Path("m/44'/137'/1'/0/0"),
37 "tbtc": BIP32Path("m/44'/1'/0'/0/0"),
38 "trsk": BIP32Path("m/44'/1'/1'/0/0"),
39 "tmst": BIP32Path("m/44'/1'/2'/0/0"),
40}
43def do_get_pubkeys(options):
44 info("### -> Get public keys")
45 hsm = None
47 # Attempt to unlock and open the signing app
48 if not options.no_unlock:
49 try:
50 do_unlock(options, label=False)
51 except Exception as e:
52 raise AdminError(f"Failed to unlock device: {str(e)}")
54 # Wait for the signer
55 wait_for_reconnection()
57 # Connection
58 hsm = get_hsm(options.verbose)
60 # Mode check
61 info("Finding mode... ", options.verbose)
62 mode = hsm.get_current_mode()
63 info(f"Mode: {mode.name.capitalize()}")
65 # Modes for which we can't get the public keys
66 if mode in [HSM2Dongle.MODE.UNKNOWN, HSM2Dongle.MODE.BOOTLOADER]:
67 raise AdminError(
68 "Device not in app mode. Disconnect and re-connect the ledger and try again")
70 # Gather public keys
71 pubkeys = {}
72 for path_name in PATHS:
73 info(f"Getting public key for path '{path_name}'... ", options.verbose)
74 path = PATHS[path_name]
75 pubkeys[path_name] = hsm.get_public_key(path)
76 info("OK")
78 try:
79 output_file = None
80 save_to_json = False
81 if options.output_file_path is not None:
82 output_file = open(options.output_file_path, "w")
84 def do_output(s):
85 return output_file.write(f"{s}\n")
87 save_to_json = True
88 json_dict = {}
89 else:
91 def do_output(s):
92 return info(s)
94 do_output("*" * 80)
95 do_output("Name \t\t\t Path \t\t\t\t Pubkey")
96 do_output("==== \t\t\t ==== \t\t\t\t ======")
97 path_name_padding = max(map(len, PATHS))
98 for path_name in PATHS:
99 path = PATHS[path_name]
100 # Compress public key
101 pk = ecdsa.VerifyingKey.from_string(bytes.fromhex(pubkeys[path_name]),
102 curve=ecdsa.SECP256k1)
103 pubkey = pk.to_string("compressed").hex()
104 do_output(f"{path_name.ljust(path_name_padding)} \t\t {path} \t\t {pubkey}")
105 if save_to_json:
106 json_dict[str(path)] = pk.to_string("uncompressed").hex()
107 do_output("*" * 80)
109 if output_file is not None:
110 output_file.close()
111 info(f"Public keys saved to {options.output_file_path}")
113 if save_to_json:
114 json_output_file_path = (os.path.splitext(options.output_file_path)[0] +
115 ".json")
116 output_file = open(json_output_file_path, "w")
117 output_file.write("%s\n" % json.dumps(json_dict, indent=2))
118 output_file.close()
119 info(f"JSON-formatted public keys saved to {json_output_file_path}")
120 except Exception as e:
121 raise AdminError(f"Error writing output: {str(e)}")
123 dispose_hsm(hsm)