Use cert designators for the signer arguments of sq encrypt
.
- Fixes #429.
This commit is contained in:
parent
359245db14
commit
cc244afd79
@ -1,12 +1,7 @@
|
||||
//! Command-line parser for `sq encrypt`.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{ValueEnum, Parser};
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
|
||||
use super::types::ClapData;
|
||||
use super::types::EncryptPurpose;
|
||||
use super::types::MetadataTime;
|
||||
@ -14,8 +9,7 @@ use super::types::FileOrStdin;
|
||||
use super::types::FileOrStdout;
|
||||
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::cert_designator::CertUserIDEmailFileWithPasswordArgs;
|
||||
use crate::cli::types::cert_designator::RecipientPrefix;
|
||||
use crate::cli::types::cert_designator::*;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::*;
|
||||
@ -130,18 +124,11 @@ pub struct Command {
|
||||
)]
|
||||
pub set_metadata_time: MetadataTime,
|
||||
|
||||
#[clap(
|
||||
long = "signer-file",
|
||||
value_name = "KEY_FILE",
|
||||
help = "Sign the message using the key in KEY_FILE",
|
||||
)]
|
||||
pub signer_key_file: Vec<PathBuf>,
|
||||
#[clap(
|
||||
long = "signer",
|
||||
value_name = "KEYID|FINGERPRINT",
|
||||
help = "Sign the message using the specified key on the key store",
|
||||
)]
|
||||
pub signer_key: Vec<KeyHandle>,
|
||||
#[command(flatten)]
|
||||
pub signers: CertDesignators<CertUserIDEmailFileArgs,
|
||||
SignerPrefix,
|
||||
OptionalValue,
|
||||
SignerDoc>,
|
||||
|
||||
#[clap(
|
||||
long = "encrypt-for",
|
||||
@ -178,6 +165,24 @@ pub struct Command {
|
||||
pub use_expired_subkey: bool,
|
||||
}
|
||||
|
||||
/// Documentation for signer arguments.
|
||||
pub struct SignerDoc {}
|
||||
impl AdditionalDocs for SignerDoc {
|
||||
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
|
||||
match arg {
|
||||
"file" =>
|
||||
"Sign the message using the key read from PATH"
|
||||
.into(),
|
||||
_ => {
|
||||
debug_assert!(help.starts_with("Use certificates"));
|
||||
help.replace("Use certificates",
|
||||
"Sign the message using the key")
|
||||
.into()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
pub enum CompressionMode {
|
||||
None,
|
||||
|
@ -10,9 +10,6 @@ use sequoia_openpgp as openpgp;
|
||||
use openpgp::armor;
|
||||
use openpgp::cert::amalgamation::ValidAmalgamation;
|
||||
use openpgp::crypto;
|
||||
use openpgp::crypto::Password;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::KeyID;
|
||||
use openpgp::policy::Policy;
|
||||
use openpgp::serialize::stream::Compressor;
|
||||
use openpgp::serialize::stream::Encryptor2 as Encryptor;
|
||||
@ -25,8 +22,6 @@ use openpgp::serialize::stream::padding::Padder;
|
||||
use openpgp::types::CompressionAlgorithm;
|
||||
use openpgp::types::KeyFlags;
|
||||
|
||||
use sequoia_keystore::Protection;
|
||||
|
||||
use crate::cli;
|
||||
use crate::cli::types::EncryptPurpose;
|
||||
use crate::cli::types::FileOrStdin;
|
||||
@ -34,7 +29,6 @@ use crate::cli::types::MetadataTime;
|
||||
use crate::Sq;
|
||||
use crate::Result;
|
||||
use crate::common::password;
|
||||
use crate::load_certs;
|
||||
use crate::print_error_chain;
|
||||
|
||||
use crate::commands::CompressionMode;
|
||||
@ -58,9 +52,10 @@ pub fn dispatch(sq: Sq, command: cli::encrypt::Command) -> Result<()> {
|
||||
armor::Kind::Message,
|
||||
)?;
|
||||
|
||||
let additional_secrets =
|
||||
load_certs(command.signer_key_file.iter())?;
|
||||
let signer_keys = &command.signer_key[..];
|
||||
let signers =
|
||||
sq.resolve_certs_or_fail(&command.signers,
|
||||
sequoia_wot::FULLY_TRUSTED)?;
|
||||
let signers = sq.get_signing_keys(&signers, None)?;
|
||||
|
||||
encrypt(
|
||||
&sq,
|
||||
@ -70,8 +65,7 @@ pub fn dispatch(sq: Sq, command: cli::encrypt::Command) -> Result<()> {
|
||||
command.recipients.with_passwords(),
|
||||
command.recipients.with_password_files(),
|
||||
&recipients,
|
||||
additional_secrets,
|
||||
signer_keys,
|
||||
signers,
|
||||
command.mode,
|
||||
command.compression,
|
||||
Some(sq.time),
|
||||
@ -91,8 +85,7 @@ pub fn encrypt<'a, 'b: 'a>(
|
||||
npasswords: usize,
|
||||
password_files: &[PathBuf],
|
||||
recipients: &'b [openpgp::Cert],
|
||||
signers: Vec<openpgp::Cert>,
|
||||
signer_keys: &[KeyHandle],
|
||||
mut signers: Vec<Box<dyn crypto::Signer + Send + Sync>>,
|
||||
mode: EncryptPurpose,
|
||||
compression: CompressionMode,
|
||||
time: Option<SystemTime>,
|
||||
@ -132,88 +125,6 @@ pub fn encrypt<'a, 'b: 'a>(
|
||||
|
||||
let mode = KeyFlags::from(mode);
|
||||
|
||||
let mut signers = sq.get_signing_keys(&signers, None)?;
|
||||
|
||||
let mut signer_keys = if signer_keys.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let mut ks = sq.key_store_or_else()?.lock().unwrap();
|
||||
|
||||
signer_keys.into_iter()
|
||||
.map(|kh| {
|
||||
let keys = ks.find_key(kh.clone())?;
|
||||
|
||||
match keys.len() {
|
||||
0 => return Err(anyhow::anyhow!(
|
||||
"{} is not present on keystore", kh)),
|
||||
1 => (),
|
||||
n => {
|
||||
wprintln!("Warning: {} is present on multiple \
|
||||
({}) devices",
|
||||
kh, n);
|
||||
}
|
||||
}
|
||||
let mut key = keys.into_iter().next().expect("checked for one");
|
||||
|
||||
match key.locked() {
|
||||
Ok(Protection::Password(msg)) => {
|
||||
let fpr = key.fingerprint();
|
||||
let cert = sq.lookup_one(
|
||||
&KeyHandle::from(&fpr), None, true);
|
||||
let display = match cert {
|
||||
Ok(cert) => {
|
||||
format!(" ({})", sq.best_userid(&cert, true))
|
||||
}
|
||||
Err(_) => {
|
||||
"".to_string()
|
||||
}
|
||||
};
|
||||
let keyid = KeyID::from(&fpr);
|
||||
|
||||
if let Some(msg) = msg {
|
||||
wprintln!("{}", msg);
|
||||
}
|
||||
loop {
|
||||
let password = Password::from(rpassword::prompt_password(
|
||||
format!("Enter password to unlock {}{}: ",
|
||||
keyid, display))?);
|
||||
match key.unlock(password) {
|
||||
Ok(()) => break,
|
||||
Err(err) => {
|
||||
wprintln!("Unlocking {}: {}.", keyid, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Protection::Unlocked) => {
|
||||
// Already unlocked, nothing to do.
|
||||
}
|
||||
Ok(Protection::UnknownProtection(msg))
|
||||
| Ok(Protection::ExternalPassword(msg))
|
||||
| Ok(Protection::ExternalTouch(msg))
|
||||
| Ok(Protection::ExternalOther(msg)) =>
|
||||
{
|
||||
// Locked.
|
||||
wprintln!("Key is locked{}",
|
||||
if let Some(msg) = msg {
|
||||
format!(": {}", msg)
|
||||
} else {
|
||||
"".into()
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
// Failed to get the key's locked status. Just print
|
||||
// a warning now. We'll (probably) fail more later.
|
||||
wprintln!("Getting {}'s status: {}",
|
||||
key.keyid(), err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(key)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
};
|
||||
|
||||
// Build a vector of recipients to hand to Encryptor.
|
||||
let mut recipient_subkeys: Vec<Recipient> = Vec::new();
|
||||
for cert in recipients.iter() {
|
||||
@ -277,21 +188,15 @@ pub fn encrypt<'a, 'b: 'a>(
|
||||
}
|
||||
|
||||
// Optionally sign message.
|
||||
if ! signers.is_empty() || ! signer_keys.is_empty() {
|
||||
let mut signer = if ! signers.is_empty() {
|
||||
Signer::new(sink, signers.pop().unwrap())
|
||||
} else {
|
||||
Signer::new(sink, signer_keys.pop().unwrap())
|
||||
};
|
||||
if let Some(first) = signers.pop() {
|
||||
let mut signer = Signer::new(sink, first);
|
||||
|
||||
if let Some(time) = time {
|
||||
signer = signer.creation_time(time);
|
||||
}
|
||||
for s in signers {
|
||||
signer = signer.add_signer(s);
|
||||
}
|
||||
for s in signer_keys {
|
||||
signer = signer.add_signer(s);
|
||||
}
|
||||
for r in recipients.iter() {
|
||||
signer = signer.add_intended_recipient(r);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user