Use cert designators for the signer arguments of sq encrypt.

- Fixes #429.
This commit is contained in:
Justus Winter 2024-11-13 14:08:32 +01:00
parent 359245db14
commit cc244afd79
No known key found for this signature in database
GPG Key ID: 686F55B4AB2B3386
2 changed files with 33 additions and 123 deletions

View File

@ -1,12 +1,7 @@
//! Command-line parser for `sq encrypt`. //! Command-line parser for `sq encrypt`.
use std::path::PathBuf;
use clap::{ValueEnum, Parser}; use clap::{ValueEnum, Parser};
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use super::types::ClapData; use super::types::ClapData;
use super::types::EncryptPurpose; use super::types::EncryptPurpose;
use super::types::MetadataTime; use super::types::MetadataTime;
@ -14,8 +9,7 @@ use super::types::FileOrStdin;
use super::types::FileOrStdout; use super::types::FileOrStdout;
use crate::cli::types::CertDesignators; use crate::cli::types::CertDesignators;
use crate::cli::types::cert_designator::CertUserIDEmailFileWithPasswordArgs; use crate::cli::types::cert_designator::*;
use crate::cli::types::cert_designator::RecipientPrefix;
use crate::cli::examples; use crate::cli::examples;
use examples::*; use examples::*;
@ -130,18 +124,11 @@ pub struct Command {
)] )]
pub set_metadata_time: MetadataTime, pub set_metadata_time: MetadataTime,
#[clap( #[command(flatten)]
long = "signer-file", pub signers: CertDesignators<CertUserIDEmailFileArgs,
value_name = "KEY_FILE", SignerPrefix,
help = "Sign the message using the key in KEY_FILE", OptionalValue,
)] SignerDoc>,
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>,
#[clap( #[clap(
long = "encrypt-for", long = "encrypt-for",
@ -178,6 +165,24 @@ pub struct Command {
pub use_expired_subkey: bool, 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)] #[derive(ValueEnum, Debug, Clone)]
pub enum CompressionMode { pub enum CompressionMode {
None, None,

View File

@ -10,9 +10,6 @@ use sequoia_openpgp as openpgp;
use openpgp::armor; use openpgp::armor;
use openpgp::cert::amalgamation::ValidAmalgamation; use openpgp::cert::amalgamation::ValidAmalgamation;
use openpgp::crypto; use openpgp::crypto;
use openpgp::crypto::Password;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::policy::Policy; use openpgp::policy::Policy;
use openpgp::serialize::stream::Compressor; use openpgp::serialize::stream::Compressor;
use openpgp::serialize::stream::Encryptor2 as Encryptor; use openpgp::serialize::stream::Encryptor2 as Encryptor;
@ -25,8 +22,6 @@ use openpgp::serialize::stream::padding::Padder;
use openpgp::types::CompressionAlgorithm; use openpgp::types::CompressionAlgorithm;
use openpgp::types::KeyFlags; use openpgp::types::KeyFlags;
use sequoia_keystore::Protection;
use crate::cli; use crate::cli;
use crate::cli::types::EncryptPurpose; use crate::cli::types::EncryptPurpose;
use crate::cli::types::FileOrStdin; use crate::cli::types::FileOrStdin;
@ -34,7 +29,6 @@ use crate::cli::types::MetadataTime;
use crate::Sq; use crate::Sq;
use crate::Result; use crate::Result;
use crate::common::password; use crate::common::password;
use crate::load_certs;
use crate::print_error_chain; use crate::print_error_chain;
use crate::commands::CompressionMode; use crate::commands::CompressionMode;
@ -58,9 +52,10 @@ pub fn dispatch(sq: Sq, command: cli::encrypt::Command) -> Result<()> {
armor::Kind::Message, armor::Kind::Message,
)?; )?;
let additional_secrets = let signers =
load_certs(command.signer_key_file.iter())?; sq.resolve_certs_or_fail(&command.signers,
let signer_keys = &command.signer_key[..]; sequoia_wot::FULLY_TRUSTED)?;
let signers = sq.get_signing_keys(&signers, None)?;
encrypt( encrypt(
&sq, &sq,
@ -70,8 +65,7 @@ pub fn dispatch(sq: Sq, command: cli::encrypt::Command) -> Result<()> {
command.recipients.with_passwords(), command.recipients.with_passwords(),
command.recipients.with_password_files(), command.recipients.with_password_files(),
&recipients, &recipients,
additional_secrets, signers,
signer_keys,
command.mode, command.mode,
command.compression, command.compression,
Some(sq.time), Some(sq.time),
@ -91,8 +85,7 @@ pub fn encrypt<'a, 'b: 'a>(
npasswords: usize, npasswords: usize,
password_files: &[PathBuf], password_files: &[PathBuf],
recipients: &'b [openpgp::Cert], recipients: &'b [openpgp::Cert],
signers: Vec<openpgp::Cert>, mut signers: Vec<Box<dyn crypto::Signer + Send + Sync>>,
signer_keys: &[KeyHandle],
mode: EncryptPurpose, mode: EncryptPurpose,
compression: CompressionMode, compression: CompressionMode,
time: Option<SystemTime>, time: Option<SystemTime>,
@ -132,88 +125,6 @@ pub fn encrypt<'a, 'b: 'a>(
let mode = KeyFlags::from(mode); 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. // Build a vector of recipients to hand to Encryptor.
let mut recipient_subkeys: Vec<Recipient> = Vec::new(); let mut recipient_subkeys: Vec<Recipient> = Vec::new();
for cert in recipients.iter() { for cert in recipients.iter() {
@ -277,21 +188,15 @@ pub fn encrypt<'a, 'b: 'a>(
} }
// Optionally sign message. // Optionally sign message.
if ! signers.is_empty() || ! signer_keys.is_empty() { if let Some(first) = signers.pop() {
let mut signer = if ! signers.is_empty() { let mut signer = Signer::new(sink, first);
Signer::new(sink, signers.pop().unwrap())
} else {
Signer::new(sink, signer_keys.pop().unwrap())
};
if let Some(time) = time { if let Some(time) = time {
signer = signer.creation_time(time); signer = signer.creation_time(time);
} }
for s in signers { for s in signers {
signer = signer.add_signer(s); signer = signer.add_signer(s);
} }
for s in signer_keys {
signer = signer.add_signer(s);
}
for r in recipients.iter() { for r in recipients.iter() {
signer = signer.add_intended_recipient(r); signer = signer.add_intended_recipient(r);
} }