Port sq pki {authenticate,identify} to the cert designator framework.

- Port `sq pki authenticate` and `sq pki identify` to the cert
    designator framework.  See #207.

  - This changes the certificate parameter from a positional parameter
    to a named parameter.  See #318.
This commit is contained in:
Neal H. Walfield 2024-11-15 10:27:48 +01:00
parent d561fad1a6
commit 2e17dec9ad
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
9 changed files with 68 additions and 103 deletions

6
NEWS
View File

@ -68,6 +68,12 @@
- The argument `sq encrypt --set-metadata-time` has been removed.
- The argument `sq encrypt --set-metadata-filename` now takes a
string that specifies the file name to be set.
- `sq pki authenticate`'s positional argument for specifying the
certificate to authenticate must now be specified using a named
argument, `--cert`.
- `sq pki identify`'s positional argument for specifying the
certificate to identify must now be specified using a named
argument, `--cert`.
* Changes in 0.39.0
** Notable changes

View File

@ -5,7 +5,6 @@ use std::ops::Deref;
use clap::Parser;
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use openpgp::packet::UserID;
use crate::cli::types::TrustAmount;
@ -57,21 +56,6 @@ pub enum Subcommands {
Path(path::Command),
}
#[derive(clap::Args, Debug)]
pub struct CertArg {
/// The fingerprint or Key ID of the certificate to authenticate.
#[arg(value_name="FINGERPRINT|KEYID")]
cert: KeyHandle
}
impl Deref for CertArg {
type Target = KeyHandle;
fn deref(&self) -> &Self::Target {
&self.cert
}
}
#[derive(clap::Args, Debug)]
pub struct UserIDArg {
/// The User ID to authenticate.

View File

@ -8,7 +8,9 @@ use examples::Actions;
use examples::Example;
use examples::Setup;
use super::CertArg;
use crate::cli::types::cert_designator;
use crate::cli::types::CertDesignators;
use super::CertificationNetworkArg;
use super::EmailArg;
use super::GossipArg;
@ -37,6 +39,18 @@ use super::UserIDArg;
after_help = EXAMPLES,
)]
pub struct Command {
// Note: don't add --cert-file: the certificate needs to be merged
// into the certificate store, and --cert-file doesn't do that.
// Instead, the user should use --keyring FILE and --cert FPR.
#[command(flatten)]
pub cert: CertDesignators<
cert_designator::CertArg,
cert_designator::CertPrefix,
cert_designator::OneValue>,
#[command(flatten)]
pub userid: UserIDArg,
#[command(flatten)]
pub show_paths: ShowPathsArg,
@ -51,12 +65,6 @@ pub struct Command {
#[command(flatten)]
pub trust_amount: RequiredTrustAmountArg,
#[command(flatten)]
pub cert: CertArg,
#[command(flatten)]
pub userid: UserIDArg,
}
const EXAMPLES: Actions = Actions {
@ -74,7 +82,7 @@ const EXAMPLES: Actions = Actions {
Authenticate a specific binding.",
command: &[
"sq", "pki", "authenticate",
"EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"Alice <alice@example.org>",
]
}),
@ -84,7 +92,7 @@ Check whether we can authenticate any user ID with the specified email \
address for the given certificate.",
command: &[
"sq", "pki", "authenticate",
"EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--email", "alice@example.org",
],
}),

View File

@ -8,7 +8,9 @@ use examples::Actions;
use examples::Example;
use examples::Setup;
use super::CertArg;
use crate::cli::types::cert_designator;
use crate::cli::types::CertDesignators;
use super::CertificationNetworkArg;
use super::GossipArg;
use super::RequiredTrustAmountArg;
@ -32,6 +34,15 @@ use super::ShowPathsArg;
after_help = EXAMPLES,
)]
pub struct Command {
// Note: don't add --cert-file: the certificate needs to be merged
// into the certificate store, and --cert-file doesn't do that.
// Instead, the user should use --keyring FILE and --cert FPR.
#[command(flatten)]
pub cert: CertDesignators<
cert_designator::CertArg,
cert_designator::CertPrefix,
cert_designator::OneValue>,
#[command(flatten)]
pub show_paths: ShowPathsArg,
@ -43,9 +54,6 @@ pub struct Command {
#[command(flatten)]
pub trust_amount: RequiredTrustAmountArg,
#[command(flatten)]
pub cert: CertArg,
}
const EXAMPLES: Actions = Actions {
@ -63,7 +71,7 @@ const EXAMPLES: Actions = Actions {
Identify the user IDs that can be authenticated for the certificate.",
command: &[
"sq", "pki", "identify",
"EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
],
}),
Action::Example(Example {
@ -71,7 +79,7 @@ Identify the user IDs that can be authenticated for the certificate.",
List all user IDs that have that have been certified by anyone.",
command: &[
"sq", "pki", "identify", "--gossip",
"511257EBBF077B7AEDAE5D093F68CB84CE537C9A",
"--cert", "511257EBBF077B7AEDAE5D093F68CB84CE537C9A",
],
}),
]

View File

@ -402,7 +402,7 @@ pub fn dispatch(sq: Sq, c: download::Command)
false, // certification network
Some(TrustAmount::Full), // trust amount
None, // user ID
Some(&cert.key_handle()),
Some(&cert),
true, // show paths
).is_ok();

View File

@ -1,15 +1,13 @@
use anyhow::Context;
use sequoia_openpgp as openpgp;
use openpgp::Cert;
use openpgp::KeyID;
use openpgp::Fingerprint;
use openpgp::KeyHandle;
use openpgp::Result;
use openpgp::packet::UserID;
use sequoia_cert_store as cert_store;
use cert_store::store::StoreError;
use sequoia_wot as wot;
use wot::store::Backend;
use wot::store::Store;
@ -85,7 +83,7 @@ pub fn authenticate<'store, 'rstore>(
certification_network: bool,
trust_amount: Option<TrustAmount<usize>>,
userid: Option<&UserID>,
certificate: Option<&KeyHandle>,
certificate: Option<&Cert>,
show_paths: bool,
) -> Result<()>
where 'store: 'rstore,
@ -114,57 +112,7 @@ pub fn authenticate<'store, 'rstore>(
let required_amount =
required_trust_amount(trust_amount, certification_network)?;
let mut certificate_dealiased = None;
let fingerprint: Option<Fingerprint> = if let Some(kh) = certificate {
match sq.lookup(std::iter::once(kh), None, true, true) {
Ok(certs) => {
assert!(certs.len() >= 1);
if certs.len() > 1 {
return Err(anyhow::anyhow!(
"The key ID {} is ambiguous. \
It could refer to any of the following \
certificates: {}.",
kh,
certs.into_iter()
.map(|c| c.fingerprint().to_hex())
.collect::<Vec<String>>()
.join(", ")));
}
let fpr = certs[0].fingerprint();
if ! KeyHandle::from(&fpr).aliases(kh) {
// XXX: If the subkey does not have a backsig,
// then it doesn't authenticate the primary key,
// and thus the authentication confidence must be
// 0, and the certificate should only be shown
// when `--gossip` is passed.
wprintln!("Certificate {} contains the subkey {}.",
fpr, kh);
}
certificate_dealiased = Some(KeyHandle::from(&fpr));
Some(fpr)
}
Err(err) => {
if let Some(StoreError::NotFound(_)) = err.downcast_ref() {
wprintln!("There are no certificates with the \
specified {}. \
Run `sq network fetch {}` to look for \
matching certificates on public \
directories.",
if let KeyHandle::Fingerprint(_) = kh {
"fingerprint"
} else {
"key ID"
},
kh);
}
return Err(err);
}
}
} else {
None
};
let fingerprint: Option<Fingerprint> = certificate.map(|c| c.fingerprint());
let mut bindings = Vec::new();
if matches!(userid, Some(_)) && email {
@ -216,9 +164,9 @@ pub fn authenticate<'store, 'rstore>(
None
}
}).collect();
} else if let Some(fingerprint) = fingerprint {
} else if let Some(fingerprint) = fingerprint.as_ref() {
if let Some(userid) = userid {
bindings.push((fingerprint, userid.clone()));
bindings.push((fingerprint.clone(), userid.clone()));
} else {
// Fingerprint, no User ID.
bindings = n.certified_userids_of(&fingerprint)
@ -316,7 +264,7 @@ pub fn authenticate<'store, 'rstore>(
// We didn't show anything. Try to figure out what was wrong.
if lint_input {
// See if the target certificate exists.
if let Some(kh) = certificate_dealiased {
if let Some(kh) = fingerprint.as_ref().map(|fpr| KeyHandle::from(fpr)) {
match n.lookup_synopses(&kh) {
Err(err) => {
wprintln!("Looking up target certificate ({}): {}",
@ -512,11 +460,15 @@ pub fn dispatch(sq: Sq, cli: cli::pki::Command) -> Result<()> {
Subcommands::Authenticate(authenticate::Command {
email, gossip, certification_network, trust_amount,
cert, userid, show_paths,
}) => authenticate(
&sq, false, None,
*email, *gossip, *certification_network, *trust_amount,
Some(&userid), Some(&cert), *show_paths,
)?,
}) => {
let cert = sq.resolve_cert(&cert, 0)?.0;
authenticate(
&sq, false, None,
*email, *gossip, *certification_network, *trust_amount,
Some(&userid), Some(&cert), *show_paths,
)?
}
// Find all authenticated bindings for a given User ID, list
// the certificates.
@ -533,10 +485,14 @@ pub fn dispatch(sq: Sq, cli: cli::pki::Command) -> Result<()> {
Subcommands::Identify(identify::Command {
gossip, certification_network, trust_amount,
cert, show_paths,
}) => authenticate(
&sq, false, None,
false, *gossip, *certification_network, *trust_amount,
None, Some(&cert), *show_paths)?,
}) => {
let cert = sq.resolve_cert(&cert, 0)?.0;
authenticate(
&sq, false, None,
false, *gossip, *certification_network, *trust_amount,
None, Some(&cert), *show_paths)?;
}
// Authenticates a given path.
Subcommands::Path(command) =>

View File

@ -1780,7 +1780,7 @@ impl Sq {
for arg in extra_args {
cmd.arg(arg);
}
cmd.arg(cert);
cmd.arg("--cert").arg(cert);
cmd.arg(userid);
let output = self.run(cmd, None);

View File

@ -40,7 +40,7 @@ fn sq_autocrypt_import() -> Result<()>
let mut cmd = sq.command();
cmd.arg("pki").arg("authenticate")
.arg("--amount=40")
.arg("A614C91D0392D83EE6B1C4A4DD4147FEF78AD630")
.arg("--cert").arg("A614C91D0392D83EE6B1C4A4DD4147FEF78AD630")
.arg("--email").arg("pink@probier.email");
eprintln!("Running: {:?}", cmd);
eprintln!("pre: {}", time_as_string(std::time::SystemTime::now().into()));
@ -58,14 +58,14 @@ fn sq_autocrypt_import() -> Result<()>
let mut cmd = sq.command();
cmd.arg("pki").arg("authenticate")
.arg("--gossip")
.arg("CBCD8F030588653EEDD7E2659B7DD433F254904A")
.arg("--cert").arg("CBCD8F030588653EEDD7E2659B7DD433F254904A")
.arg("--email").arg("justus@sequoia-pgp.org");
sq.run(cmd, true);
let mut cmd = sq.command();
cmd.arg("pki").arg("authenticate")
.arg("--gossip")
.arg("BB6B7E5F8343B2BE990EB7A7F3AF066F267892C1")
.arg("--cert").arg("BB6B7E5F8343B2BE990EB7A7F3AF066F267892C1")
.arg("--email").arg("hilal-maria@probier.email");
sq.run(cmd, true);

View File

@ -113,6 +113,9 @@ where
cmd.arg("--show-paths");
}
if let Some(target) = target {
if ["authenticate", "identify"].contains(&command) {
cmd.arg("--cert");
}
cmd.arg(&target.to_string());
}
if let Some(userid) = userid {