1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! PGP key management commands for the Zoi CLI.
//!
//! These commands allow users to manage PGP keys used for verifying package
//! signatures, ensuring the authenticity and integrity of installed software.
use std::path::Path;
use anyhow::{Result, anyhow};
use clap::{ArgGroup, Parser, Subcommand};
use crate::pkg;
/// The root PGP management command.
#[derive(Parser, Debug)]
#[command(long_about = "Manages PGP keys for package signature verification.")]
pub struct PgpCommand {
/// The specific PGP subcommand to execute.
#[command(subcommand)]
pub command: PgpCommands
}
/// Available PGP subcommands.
#[derive(Subcommand, Debug)]
pub enum PgpCommands {
/// Add a PGP key from a file, URL, or a keyserver
Add(AddKey),
/// Remove a PGP key
#[command(alias = "rm")]
Remove(RemoveKey),
/// List all imported PGP keys
#[command(alias = "ls")]
List,
/// Search for a PGP key by user ID or fingerprint
Search(SearchKey),
/// Show the public key of a stored PGP key
Show(ShowKey),
/// Verify a file's detached signature
Verify(VerifySig)
}
/// Arguments for the add-key command.
#[derive(Parser, Debug)]
#[command(group(
ArgGroup::new("source")
.required(true)
.args(["path", "fingerprint", "url"]),
))]
pub struct AddKey {
/// Path to the PGP key file (.asc)
#[arg(long)]
pub path: Option<String>,
/// Fingerprint of the PGP key to fetch from keys.openpgp.org
#[arg(long)]
pub fingerprint: Option<String>,
/// URL of the PGP key to import
#[arg(long)]
pub url: Option<String>,
/// Name to associate with the key (defaults to filename if adding from
/// path/url)
#[arg(long)]
pub name: Option<String>
}
/// Arguments for the remove-key command.
#[derive(Parser, Debug)]
#[command(group(
ArgGroup::new("key_id")
.required(true)
.args(["name", "fingerprint"]),
))]
pub struct RemoveKey {
/// Name of the key to remove
pub name: Option<String>,
/// Fingerprint of the key to remove
#[arg(long)]
pub fingerprint: Option<String>
}
/// Arguments for the search-key command.
#[derive(Parser, Debug)]
pub struct SearchKey {
/// The user ID (name, email) or fingerprint to search for
#[arg(required = true)]
pub term: String
}
/// Arguments for the show-key command.
#[derive(Parser, Debug)]
pub struct ShowKey {
/// The name of the key to show
#[arg(required = true)]
pub name: String
}
/// Arguments for the verify-signature command.
#[derive(Parser, Debug)]
pub struct VerifySig {
/// Path to the file to verify
#[arg(long)]
pub file: String,
/// Path to the detached signature file
#[arg(long)]
pub sig: String,
/// Name of the key in the local store to use for verification
#[arg(long)]
pub key: String
}
/// Run the PGP management command.
///
/// # Errors
///
/// Returns an error if the PGP operation (key generation, signing, etc.) fails.
pub fn run(args: PgpCommand) -> Result<()> {
match args.command {
PgpCommands::Add(add_args) => {
if let Some(path) = add_args.path {
pkg::pgp::add_key_from_path(
&path,
add_args.name.as_deref(),
false
)?;
} else if let Some(fingerprint) = add_args.fingerprint {
if let Some(name) = add_args.name {
pkg::pgp::add_key_from_fingerprint(
&fingerprint,
&name,
false
)?;
} else {
return Err(anyhow!(
"A name must be provided when adding a key by \
fingerprint."
));
}
} else if let Some(url) = add_args.url {
let name = if let Some(n) = add_args.name {
n
} else {
Path::new(&url)
.file_stem()
.and_then(|s| s.to_str())
.ok_or(anyhow!("Could not derive name from URL"))?
.to_string()
};
pkg::pgp::add_key_from_url(&url, &name, false)?;
}
}
PgpCommands::Remove(remove_args) => {
if let Some(name) = remove_args.name {
pkg::pgp::remove_key_by_name(&name)?;
} else if let Some(fingerprint) = remove_args.fingerprint {
pkg::pgp::remove_key_by_fingerprint(&fingerprint)?;
}
}
PgpCommands::List => {
pkg::pgp::list_keys()?;
}
PgpCommands::Search(search_args) => {
pkg::pgp::search_keys(&search_args.term)?;
}
PgpCommands::Show(show_args) => {
pkg::pgp::show_key(&show_args.name)?;
}
PgpCommands::Verify(verify_args) => {
pkg::pgp::cli_verify_signature(
&verify_args.file,
&verify_args.sig,
&verify_args.key
)?;
}
}
Ok(())
}