Add sq encrypt --profile in preparation for RFC9580.

- Fixes #463.
This commit is contained in:
Justus Winter 2024-12-04 16:18:43 +01:00
parent 6cf2acc893
commit 3ab852aba2
No known key found for this signature in database
GPG Key ID: 686F55B4AB2B3386
4 changed files with 92 additions and 2 deletions

View File

@ -7,8 +7,10 @@ use super::types::EncryptPurpose;
use super::types::FileOrStdin;
use super::types::FileOrStdout;
use crate::cli::config;
use crate::cli::types::CertDesignators;
use crate::cli::types::cert_designator::*;
use crate::cli::types::Profile;
use crate::cli::examples;
use examples::*;
@ -157,6 +159,36 @@ pub struct Command {
to using the one that expired last",
)]
pub use_expired_subkey: bool,
#[clap(
long = "profile",
value_name = "PROFILE",
default_value_t = Default::default(),
help = "Select the OpenPGP standard for the encryption container",
long_help = config::augment_help(
"key.generate.profile",
"Select the default OpenPGP standard for the encryption container
When encrypting for certificates, the encryption container is selected \
based on the stated preferences of the recipients. However, if there \
is no guidance, for example because the message is encrypted only with \
passwords, sq falls back to this profile.
As OpenPGP evolves, new versions will become available. This option \
selects the version of OpenPGP to use for encrypting messages if the \
version can not be inferred otherwise.
Currently, sq supports only one version: RFC4880. Consequently, this \
is the default. However, there is already a newer version of the \
standard: RFC9580. And, the default will change in a future version of \
sq."),
value_enum,
)]
pub profile: Profile,
/// Workaround for https://github.com/clap-rs/clap/issues/3846
#[clap(skip)]
pub profile_source: Option<clap::parser::ValueSource>,
}
/// Documentation for signer arguments.

View File

@ -37,8 +37,10 @@ pub fn dispatch(sq: Sq, command: SqCommand, matches: &ArgMatches) -> Result<()>
{
let matches = matches.subcommand().unwrap().1;
match command.subcommand {
SqSubcommands::Encrypt(command) =>
encrypt::dispatch(sq, command),
SqSubcommands::Encrypt(mut command) => {
command.profile_source = matches.value_source("profile");
encrypt::dispatch(sq, command)
},
SqSubcommands::Decrypt(command) =>
decrypt::dispatch(sq, command),
SqSubcommands::Sign(command) =>

View File

@ -67,6 +67,10 @@ pub fn dispatch(sq: Sq, command: cli::encrypt::Command) -> Result<()> {
but none are given"));
}
// Profile. XXX: Currently, this is not actionable.
let _profile = sq.config.encrypt_profile(
&command.profile, command.profile_source);
encrypt(
&sq,
sq.policy,

View File

@ -51,6 +51,9 @@ pub struct Config {
/// The set of encryption certs selected using `--for-self`.
encrypt_for_self: BTreeSet<Fingerprint>,
/// The default profile for encryption containers.
encrypt_profile: Option<cli::types::Profile>,
/// The set of signing keys selected using `--signer-self`.
sign_signer_self: BTreeSet<Fingerprint>,
@ -93,6 +96,7 @@ impl Default for Config {
quiet: false,
hints: None,
encrypt_for_self: Default::default(),
encrypt_profile: None,
sign_signer_self: Default::default(),
pki_vouch_certifier_self: None,
pki_vouch_expiration: None,
@ -177,6 +181,25 @@ impl Config {
&self.encrypt_for_self
}
/// Returns the default profile for encryption containers.
///
/// Handles the precedence of the various sources:
///
/// - If the flag is given, use the given value.
/// - If the command line flag is not given, then
/// - use the value from the configuration file (if any),
/// - or use the default value.
pub fn encrypt_profile(&self, cli: &cli::types::Profile,
source: Option<ValueSource>)
-> cli::types::Profile
{
match source.expect("set by the cli parser") {
ValueSource::DefaultValue =>
self.encrypt_profile.as_ref().unwrap_or(cli),
_ => cli,
}.clone()
}
/// Returns the keys that should be added to the list of
/// signers if `--signer-self` is given.
pub fn sign_signer_self(&self) -> &BTreeSet<Fingerprint> {
@ -344,6 +367,7 @@ impl ConfigFile {
[encrypt]
#for-self = [\"fingerprint of your key\"]
#profile = <DEFAULT-ENCRYPT-PROFILE>
[sign]
#signer-self = [\"fingerprint of your key\"]
@ -381,6 +405,7 @@ impl ConfigFile {
const TEMPLATE_PATTERNS: &'static [&'static str] = &[
"<SQ-VERSION>",
"<SQ-CONFIG-PATH-HINT>",
"<DEFAULT-ENCRYPT-PROFILE>",
"<DEFAULT-PKI-VOUCH-EXPIRATION>",
"<DEFAULT-CIPHER-SUITE>",
"<DEFAULT-KEY-GENERATE-PROFILE>",
@ -415,6 +440,8 @@ impl ConfigFile {
} else {
"".into()
},
&format!("{:?}", cli::types::Profile::default()
.to_possible_value().unwrap().get_name()),
&cli::THIRD_PARTY_CERTIFICATION_VALIDITY_IN_YEARS.to_string(),
&format!("{:?}", cli::key::CipherSuite::default().
to_possible_value().unwrap().get_name()),
@ -855,6 +882,7 @@ fn apply_ui_verbosity(config: &mut Option<&mut Config>,
/// Schema for the `encrypt` section.
const ENCRYPT_SCHEMA: Schema = &[
("for-self", apply_encrypt_for_self),
("profile", apply_encrypt_profile),
];
/// Validates the `encrypt` section.
@ -899,6 +927,30 @@ fn apply_encrypt_for_self(config: &mut Option<&mut Config>,
Ok(())
}
/// Validates the `key.generate.profile` value.
fn apply_encrypt_profile(config: &mut Option<&mut Config>,
cli: &mut Option<&mut Augmentations>,
path: &str, item: &Item)
-> Result<()>
{
let s = item.as_str()
.ok_or_else(|| Error::bad_item_type(path, item, "string"))?;
let v = cli::types::Profile::from_str(s, false)
.map_err(|e| anyhow::anyhow!("{}", e))?;
if let Some(config) = config {
config.encrypt_profile = Some(v.clone());
}
if let Some(cli) = cli {
cli.insert(
"encrypt.profile",
v.to_possible_value().expect("just validated").get_name().into());
}
Ok(())
}
/// Schema for the `sign` section.
const SIGN_SCHEMA: Schema = &[
("signer-self", apply_sign_signer_self),