parent
8c47caaee9
commit
3ec39ac611
@ -8,6 +8,7 @@ use super::types::ClapData;
|
||||
use super::types::FileOrStdin;
|
||||
use super::types::FileOrStdout;
|
||||
use super::types::SessionKey;
|
||||
use super::types::cert_designator::*;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
@ -105,12 +106,13 @@ pub struct Command {
|
||||
is given, 0 otherwise]",
|
||||
)]
|
||||
pub signatures: Option<usize>,
|
||||
#[clap(
|
||||
long = "signer-file",
|
||||
value_name = "CERT_FILE",
|
||||
help = "Verify signatures using the certificates in CERT_FILE",
|
||||
)]
|
||||
pub sender_cert_file: Vec<PathBuf>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub signers: CertDesignators<CertFileArgs,
|
||||
SignerPrefix,
|
||||
OptionalValue,
|
||||
ToVerifyDoc>,
|
||||
|
||||
#[clap(
|
||||
long = "recipient-file",
|
||||
value_name = "KEY_FILE",
|
||||
|
@ -23,9 +23,9 @@ pub trait ArgumentPrefix {
|
||||
pub struct ConcreteArgumentPrefix<T>(std::marker::PhantomData<T>)
|
||||
where T: typenum::Unsigned;
|
||||
|
||||
// "--cert", "--userid", "--file", etc.
|
||||
/// "--cert", "--userid", "--file", etc.
|
||||
pub type NoPrefix = ConcreteArgumentPrefix<typenum::U0>;
|
||||
// "--cert", "--cert-userid", "--cert-file", etc.
|
||||
/// "--cert", "--cert-userid", "--cert-file", etc.
|
||||
pub type CertPrefix = ConcreteArgumentPrefix<typenum::U1>;
|
||||
|
||||
/// "--for", "--for-userid", "--for-file", etc.
|
||||
@ -61,6 +61,19 @@ impl ArgumentPrefix for RecipientPrefix {
|
||||
}
|
||||
}
|
||||
|
||||
/// "--signer", "--signer-userid", "--signer-file", etc.
|
||||
pub type SignerPrefix = ConcreteArgumentPrefix<typenum::U3>;
|
||||
|
||||
impl ArgumentPrefix for SignerPrefix {
|
||||
fn prefix() -> &'static str {
|
||||
"signer-"
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"signer"
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a `--file` argument.
|
||||
pub type FileArg = typenum::U1;
|
||||
|
||||
@ -79,6 +92,16 @@ pub type DomainArg = typenum::U32;
|
||||
/// Adds a `--grep` argument.
|
||||
pub type GrepArg = typenum::U64;
|
||||
|
||||
/// Enables --file, --cert, --userid, --email, and --domain, (i.e.,
|
||||
/// not --grep).
|
||||
#[allow(dead_code)]
|
||||
pub type FileCertUserIDEmailDomainArgs
|
||||
= <<<<FileArg
|
||||
as std::ops::BitOr<CertArg>>::Output
|
||||
as std::ops::BitOr<UserIDArg>>::Output
|
||||
as std::ops::BitOr<EmailArg>>::Output
|
||||
as std::ops::BitOr<DomainArg>>::Output;
|
||||
|
||||
/// Enables --cert, --userid, --email, --domain, and --grep (i.e., not
|
||||
/// --file).
|
||||
pub type CertUserIDEmailDomainGrepArgs
|
||||
@ -110,6 +133,51 @@ pub type OneValue = typenum::U1;
|
||||
/// completely optional.
|
||||
pub type OptionalValue = typenum::U2;
|
||||
|
||||
// Additional documentation.
|
||||
|
||||
/// The prefix for the designators.
|
||||
///
|
||||
/// See [`NoPrefix`], [`CertPrefix`], etc.
|
||||
pub trait AdditionalDocs {
|
||||
/// Text to be added to the help text.
|
||||
// XXX: This should return a Cow<'static, str>, but there is no
|
||||
// implementation of From<Cow<'static, str>> for StyledStr,
|
||||
// see https://github.com/clap-rs/clap/issues/5785
|
||||
fn help(_arg: &'static str, help: &'static str) -> String {
|
||||
help.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// No additional documentation.
|
||||
pub struct NoDoc(());
|
||||
impl AdditionalDocs for NoDoc {}
|
||||
|
||||
|
||||
/// Documentation for signer arguments.
|
||||
pub struct ToVerifyDoc {}
|
||||
impl AdditionalDocs for ToVerifyDoc {
|
||||
fn help(arg: &'static str, help: &'static str) -> String {
|
||||
match arg {
|
||||
"cert" | "file" => format!(
|
||||
"{} to verify the signatures with. \
|
||||
Note: signatures verified with a certificate \
|
||||
given here are considered authenticated.{}",
|
||||
help,
|
||||
if arg == "cert" {
|
||||
" When this option is not provided, the certificate \
|
||||
is still read from the certificate store, if it \
|
||||
exists, but it is not implicitly considered \
|
||||
authenticated."
|
||||
} else {
|
||||
""
|
||||
}),
|
||||
_ => format!(
|
||||
"{} to verify the signatures with",
|
||||
help),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A certificate designator.
|
||||
#[derive(Debug)]
|
||||
pub enum CertDesignator {
|
||||
@ -238,16 +306,18 @@ impl CertDesignator {
|
||||
/// change, e.g., `--email` to `--for-email`.
|
||||
///
|
||||
/// `Options` are the set of options to the argument parser.
|
||||
pub struct CertDesignators<Arguments, Prefix=NoPrefix, Options=typenum::U0>
|
||||
pub struct CertDesignators<Arguments, Prefix=NoPrefix, Options=typenum::U0,
|
||||
Doc=NoDoc>
|
||||
{
|
||||
/// The set of certificate designators.
|
||||
pub designators: Vec<CertDesignator>,
|
||||
|
||||
arguments: std::marker::PhantomData<(Arguments, Prefix, Options)>,
|
||||
arguments: std::marker::PhantomData<(Arguments, Prefix, Options,
|
||||
Doc)>,
|
||||
}
|
||||
|
||||
impl<Arguments, Prefix, Options> std::fmt::Debug
|
||||
for CertDesignators<Arguments, Prefix, Options>
|
||||
impl<Arguments, Prefix, Options, Doc> std::fmt::Debug
|
||||
for CertDesignators<Arguments, Prefix, Options, Doc>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("CertDesignators")
|
||||
@ -256,7 +326,7 @@ impl<Arguments, Prefix, Options> std::fmt::Debug
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arguments, Prefix, Options> CertDesignators<Arguments, Prefix, Options> {
|
||||
impl<Arguments, Prefix, Options, Doc> CertDesignators<Arguments, Prefix, Options, Doc> {
|
||||
/// Like `Vec::push`.
|
||||
pub fn push(&mut self, designator: CertDesignator) {
|
||||
self.designators.push(designator)
|
||||
@ -273,12 +343,13 @@ impl<Arguments, Prefix, Options> CertDesignators<Arguments, Prefix, Options> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arguments, Prefix, Options> clap::Args
|
||||
for CertDesignators<Arguments, Prefix, Options>
|
||||
impl<Arguments, Prefix, Options, Doc> clap::Args
|
||||
for CertDesignators<Arguments, Prefix, Options, Doc>
|
||||
where
|
||||
Arguments: typenum::Unsigned,
|
||||
Prefix: ArgumentPrefix,
|
||||
Options: typenum::Unsigned,
|
||||
Doc: AdditionalDocs,
|
||||
{
|
||||
fn augment_args(mut cmd: clap::Command) -> clap::Command
|
||||
{
|
||||
@ -382,8 +453,10 @@ where
|
||||
.value_name("FINGERPRINT|KEYID")
|
||||
.value_parser(parse_as_key_handle)
|
||||
.action(action.clone())
|
||||
.help("Use certificates with the specified \
|
||||
fingerprint or key ID"));
|
||||
.help(Doc::help(
|
||||
"cert",
|
||||
"Use certificates with the specified \
|
||||
fingerprint or key ID")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -394,7 +467,9 @@ where
|
||||
.long(&full_name)
|
||||
.value_name("USERID")
|
||||
.action(action.clone())
|
||||
.help("Use certificates with the specified user ID"));
|
||||
.help(Doc::help(
|
||||
"userid",
|
||||
"Use certificates with the specified user ID")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -406,8 +481,10 @@ where
|
||||
.value_name("EMAIL")
|
||||
.value_parser(parse_as_email)
|
||||
.action(action.clone())
|
||||
.help("Use certificates where a user ID includes \
|
||||
the specified email address"));
|
||||
.help(Doc::help(
|
||||
"email",
|
||||
"Use certificates where a user ID includes \
|
||||
the specified email address")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -419,8 +496,10 @@ where
|
||||
.value_name("DOMAIN")
|
||||
.value_parser(parse_as_domain)
|
||||
.action(action.clone())
|
||||
.help("Use certificates where a user ID includes \
|
||||
an email address for the specified domain"));
|
||||
.help(Doc::help(
|
||||
"domain",
|
||||
"Use certificates where a user ID includes \
|
||||
an email address for the specified domain")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -431,8 +510,10 @@ where
|
||||
.long(&full_name)
|
||||
.value_name("PATTERN")
|
||||
.action(action.clone())
|
||||
.help("Use certificates with a user ID that \
|
||||
matches the pattern, case insensitively"));
|
||||
.help(Doc::help(
|
||||
"grep",
|
||||
"Use certificates with a user ID that \
|
||||
matches the pattern, case insensitively")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -445,7 +526,9 @@ where
|
||||
.value_name("PATH")
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
.action(action.clone())
|
||||
.help("Read certificates from PATH"));
|
||||
.help(Doc::help(
|
||||
"file",
|
||||
"Read certificates from PATH")));
|
||||
arg_group = arg_group.arg(full_name);
|
||||
}
|
||||
|
||||
@ -460,12 +543,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arguments, Prefix, Options> clap::FromArgMatches
|
||||
for CertDesignators<Arguments, Prefix, Options>
|
||||
impl<Arguments, Prefix, Options, Doc> clap::FromArgMatches
|
||||
for CertDesignators<Arguments, Prefix, Options, Doc>
|
||||
where
|
||||
Arguments: typenum::Unsigned,
|
||||
Prefix: ArgumentPrefix,
|
||||
Options: typenum::Unsigned,
|
||||
Doc: AdditionalDocs,
|
||||
{
|
||||
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches)
|
||||
-> Result<(), clap::Error>
|
||||
|
@ -32,7 +32,6 @@ use crate::{
|
||||
},
|
||||
common::password,
|
||||
Sq,
|
||||
load_certs,
|
||||
load_keys,
|
||||
};
|
||||
|
||||
@ -42,12 +41,13 @@ pub fn dispatch(sq: Sq, command: cli::decrypt::Command) -> Result<()> {
|
||||
let mut input = command.input.open()?;
|
||||
let mut output = command.output.create_safe(&sq)?;
|
||||
|
||||
let certs = load_certs(
|
||||
command.sender_cert_file.iter())?;
|
||||
let signers =
|
||||
sq.resolve_certs_or_fail(&command.signers, sequoia_wot::FULLY_TRUSTED)?;
|
||||
|
||||
// Fancy default for --signatures. If you change this,
|
||||
// also change the description in the CLI definition.
|
||||
let signatures = command.signatures.unwrap_or_else(|| {
|
||||
if certs.is_empty() {
|
||||
if signers.is_empty() {
|
||||
// No certs are given for verification, use 0 as
|
||||
// threshold so we handle only-encrypted messages
|
||||
// gracefully.
|
||||
@ -62,7 +62,7 @@ pub fn dispatch(sq: Sq, command: cli::decrypt::Command) -> Result<()> {
|
||||
load_keys(command.secret_key_file.iter())?;
|
||||
let session_keys = command.session_key;
|
||||
decrypt(sq, &mut input, &mut output,
|
||||
signatures, certs, secrets,
|
||||
signatures, signers, secrets,
|
||||
command.dump_session_key,
|
||||
session_keys)?;
|
||||
|
||||
|
31
src/sq.rs
31
src/sq.rs
@ -1694,9 +1694,9 @@ impl<'store: 'rstore, 'rstore> Sq<'store, 'rstore> {
|
||||
/// general, designator-specific errors are returned as `Err`s in
|
||||
/// the `Vec`. General errors, like the certificate store is
|
||||
/// disabled, are returned using the outer `Result`.
|
||||
pub fn resolve_certs<Arguments, Prefix, Options>(
|
||||
pub fn resolve_certs<Arguments, Prefix, Options, Doc>(
|
||||
&self,
|
||||
designators: &CertDesignators<Arguments, Prefix, Options>,
|
||||
designators: &CertDesignators<Arguments, Prefix, Options, Doc>,
|
||||
trust_amount: usize,
|
||||
)
|
||||
-> Result<(Vec<Cert>, Vec<anyhow::Error>)>
|
||||
@ -2099,14 +2099,37 @@ impl<'store: 'rstore, 'rstore> Sq<'store, 'rstore> {
|
||||
Ok((results, errors))
|
||||
}
|
||||
|
||||
/// Like `Sq::resolve_certs`, but bails if there is an error
|
||||
/// resolving a certificate.
|
||||
pub fn resolve_certs_or_fail<Arguments, Prefix, Options, Doc>(
|
||||
&self,
|
||||
designators: &CertDesignators<Arguments, Prefix, Options, Doc>,
|
||||
trust_amount: usize,
|
||||
)
|
||||
-> Result<Vec<Cert>>
|
||||
where
|
||||
Prefix: ArgumentPrefix,
|
||||
{
|
||||
let (certs, errors) = self.resolve_certs(designators, trust_amount)?;
|
||||
|
||||
for error in errors.iter() {
|
||||
print_error_chain(error);
|
||||
}
|
||||
if ! errors.is_empty() {
|
||||
return Err(anyhow::anyhow!("Failed to resolve certificates"));
|
||||
}
|
||||
|
||||
Ok(certs)
|
||||
}
|
||||
|
||||
/// Like `Sq::resolve_certs`, but bails if there is not exactly
|
||||
/// one designator, or the designator resolves to multiple
|
||||
/// certificates.
|
||||
///
|
||||
/// Returns whether the certificate was read from a file.
|
||||
pub fn resolve_cert<Arguments, Prefix>(
|
||||
pub fn resolve_cert<Arguments, Prefix, Doc>(
|
||||
&self,
|
||||
designators: &CertDesignators<Arguments, Prefix, OneValue>,
|
||||
designators: &CertDesignators<Arguments, Prefix, OneValue, Doc>,
|
||||
trust_amount: usize,
|
||||
)
|
||||
-> Result<(Cert, bool)>
|
||||
|
Loading…
x
Reference in New Issue
Block a user