arti/subcommands/
keys.rs
1use std::str::FromStr;
4
5use anyhow::Result;
6
7use arti_client::{InertTorClient, TorClient, TorClientConfig};
8use clap::{ArgMatches, Args, FromArgMatches, Parser, Subcommand};
9use tor_keymgr::{KeyMgr, KeystoreEntry, KeystoreEntryResult, KeystoreId, UnrecognizedEntryError};
10use tor_rtcompat::Runtime;
11
12const LINE_LEN: usize = 80;
15
16#[derive(Debug, Parser)]
18pub(crate) enum KeysSubcommands {
19 #[command(subcommand)]
21 Keys(KeysSubcommand),
22}
23
24#[derive(Subcommand, Debug, Clone)]
25pub(crate) enum KeysSubcommand {
26 List(ListArgs),
33
34 ListKeystores,
36}
37
38#[derive(Debug, Clone, Args)]
40pub(crate) struct ListArgs {
41 #[arg(short, long)]
45 keystore_id: Option<String>,
46}
47
48pub(crate) fn run<R: Runtime>(
50 runtime: R,
51 keys_matches: &ArgMatches,
52 config: &TorClientConfig,
53) -> Result<()> {
54 let subcommand =
55 KeysSubcommand::from_arg_matches(keys_matches).expect("Could not parse keys subcommand");
56 let client = TorClient::with_runtime(runtime)
57 .config(config.clone())
58 .create_inert()?;
59
60 match subcommand {
61 KeysSubcommand::List(args) => run_list_keys(&args, &client),
62 KeysSubcommand::ListKeystores => run_list_keystores(&client),
63 }
64}
65
66fn display_entry(entry: &KeystoreEntry, keymgr: &KeyMgr) {
68 let raw_entry = entry.raw_entry();
69 match keymgr.describe(entry.key_path()) {
70 Ok(e) => {
71 println!(" Keystore ID: {}", entry.keystore_id());
72 println!(" Role: {}", e.role());
73 println!(" Summary: {}", e.summary());
74 println!(" KeystoreItemType: {:?}", entry.key_type());
75 println!(" Location: {}", raw_entry.raw_id());
76 let extra_info = e.extra_info();
77 println!(" Extra info:");
78 for (key, value) in extra_info {
79 println!(" - {key}: {value}");
80 }
81 }
82 Err(_) => {
83 println!(" Unrecognized path {}", raw_entry.raw_id());
84 }
85 }
86 println!("\n {}\n", "-".repeat(LINE_LEN));
87}
88
89fn display_unrecognized_entry(entry: &UnrecognizedEntryError) {
91 let raw_entry = entry.entry();
92 println!(" Unrecognized entry");
93 #[allow(clippy::single_match)]
94 match raw_entry.raw_id() {
95 tor_keymgr::RawEntryId::Path(p) => {
96 println!(" Keystore ID: {}", raw_entry.keystore_id());
97 println!(" Location: {}", p.to_string_lossy());
98 println!(" Error: {}", entry.error());
99 }
100 other => {
104 panic!("Unhandled enum variant: {:?}", other);
105 }
106 }
107 println!("\n {}\n", "-".repeat(LINE_LEN));
108}
109
110fn run_list_keys(args: &ListArgs, client: &InertTorClient) -> Result<()> {
112 let keymgr = client.keymgr()?;
113 match &args.keystore_id {
118 Some(s) => {
119 let id = KeystoreId::from_str(s)?;
120 let empty_err_msg = format!("Currently there are no entries in the keystore {}.", s);
121 display_keystore_entries(keymgr.list_by_id(&id)?, keymgr, &empty_err_msg);
122 }
123 None => {
124 let entries = keymgr.list()?;
125 display_keystore_entries(
126 entries,
127 keymgr,
128 "Currently there are no entries in any of the keystores.",
129 );
130 }
131 }
132 Ok(())
133}
134
135fn run_list_keystores(client: &InertTorClient) -> Result<()> {
137 let keymgr = client.keymgr()?;
138 let entries = keymgr.list_keystores();
139
140 if entries.is_empty() {
141 println!("Currently there are no keystores available.");
142 } else {
143 println!(" Keystores:\n");
144 for entry in entries {
145 println!(" - {:?}\n", entry.as_ref());
148 }
149 }
150
151 Ok(())
152}
153
154fn display_keystore_entries(
156 entries: Vec<KeystoreEntryResult<KeystoreEntry>>,
157 keymgr: &KeyMgr,
158 empty_err_msg: &str,
159) {
160 if entries.is_empty() {
161 println!("{empty_err_msg}");
162 }
163 println!(" ===== Keystore entries =====\n\n");
164 for entry in entries {
165 match entry {
166 Ok(entry) => {
167 display_entry(&entry, keymgr);
168 }
169 Err(entry) => {
170 display_unrecognized_entry(&entry);
171 }
172 }
173 }
174}