Add sq key generate --profile in preparation for RFC9580.

- See #463.
This commit is contained in:
Justus Winter 2024-12-04 15:32:16 +01:00
parent ab01bd9557
commit 6cf2acc893
No known key found for this signature in database
GPG Key ID: 686F55B4AB2B3386
6 changed files with 95 additions and 0 deletions

View File

@ -13,6 +13,7 @@ use crate::cli::types::EncryptPurpose;
use crate::cli::types::Expiration;
use crate::cli::types::ExpirationArg;
use crate::cli::types::FileOrStdout;
use crate::cli::types::Profile;
use crate::cli::examples::*;
use crate::cli::key::CipherSuite;
@ -146,6 +147,30 @@ Canonical user IDs are of the form `Name (Comment) \
#[clap(skip)]
pub cipher_suite_source: Option<clap::parser::ValueSource>,
#[clap(
long = "profile",
value_name = "PROFILE",
default_value_t = Default::default(),
help = "Select the OpenPGP standard for the key",
long_help = config::augment_help(
"key.generate.profile",
"Select the OpenPGP standard for the key
As OpenPGP evolves, new versions will become available. This option \
selects the version of OpenPGP to use for the newly generated key.
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>,
#[clap(
long = "new-password-file",
value_name = "PASSWORD_FILE",

View File

@ -39,6 +39,8 @@ pub use userid_designator::UserIDDesignators;
pub mod expiration;
pub use expiration::Expiration;
pub use expiration::ExpirationArg;
pub mod profile;
pub use profile::Profile;
pub mod special_names;
pub use special_names::SpecialName;
pub mod time;

9
src/cli/types/profile.rs Normal file
View File

@ -0,0 +1,9 @@
//! OpenPGP profiles.
/// Profiles select versions of the OpenPGP standard.
#[derive(clap::ValueEnum, Default, Debug, Clone)]
pub enum Profile {
/// RFC4880, published in 2007, defines "v4" OpenPGP.
#[default]
RFC4880,
}

View File

@ -31,6 +31,7 @@ pub fn dispatch(sq: Sq, command: cli::key::Command, matches: &ArgMatches)
List(c) => list(sq, c)?,
Generate(mut c) => {
c.cipher_suite_source = matches.value_source("cipher_suite");
c.profile_source = matches.value_source("profile");
generate(sq, c)?
},
Import(c) => import(sq, c)?,

View File

@ -96,6 +96,10 @@ pub fn generate(
sq.config.cipher_suite(&command.cipher_suite,
command.cipher_suite_source));
// Profile. XXX: Currently, this is not actionable.
let _profile = sq.config.key_generate_profile(
&command.profile, command.profile_source);
// Primary key capabilities.
builder = builder.set_primary_key_flags(
key_flags_template.clone().set_certification());

View File

@ -63,8 +63,13 @@ pub struct Config {
policy_path: Option<PathBuf>,
policy_inline: Option<Vec<u8>>,
/// The default cipher suite for newly generated keys.
cipher_suite: Option<sequoia_openpgp::cert::CipherSuite>,
/// The default profile for newly generated keys.
key_generate_profile: Option<cli::types::Profile>,
/// The set of keyservers to use.
key_servers: Option<Vec<Url>>,
@ -94,6 +99,7 @@ impl Default for Config {
policy_path: None,
policy_inline: None,
cipher_suite: None,
key_generate_profile: None,
key_servers: None,
network_search_iterations: 3,
network_search_wkd: true,
@ -253,6 +259,25 @@ impl Config {
}
}
/// Returns the profile for generating new keys.
///
/// 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 key_generate_profile(&self, cli: &cli::types::Profile,
source: Option<ValueSource>)
-> cli::types::Profile
{
match source.expect("set by the cli parser") {
ValueSource::DefaultValue =>
self.key_generate_profile.as_ref().unwrap_or(cli),
_ => cli,
}.clone()
}
/// Returns the key servers to query or publish.
///
/// Handles the precedence of the various sources:
@ -329,6 +354,7 @@ impl ConfigFile {
[key.generate]
#cipher-suite = <DEFAULT-CIPHER-SUITE>
#profile = <DEFAULT-KEY-GENERATE-PROFILE>
[network]
#keyservers = <DEFAULT-KEY-SERVERS>
@ -357,6 +383,7 @@ impl ConfigFile {
"<SQ-CONFIG-PATH-HINT>",
"<DEFAULT-PKI-VOUCH-EXPIRATION>",
"<DEFAULT-CIPHER-SUITE>",
"<DEFAULT-KEY-GENERATE-PROFILE>",
"<DEFAULT-KEY-SERVERS>",
"<DEFAULT-SERVERS-PATH>",
"<DEFAULT-POLICY-FILE>",
@ -391,6 +418,8 @@ impl ConfigFile {
&cli::THIRD_PARTY_CERTIFICATION_VALIDITY_IN_YEARS.to_string(),
&format!("{:?}", cli::key::CipherSuite::default().
to_possible_value().unwrap().get_name()),
&format!("{:?}", cli::types::Profile::default()
.to_possible_value().unwrap().get_name()),
&format!("{:?}", cli::network::keyserver::DEFAULT_KEYSERVERS),
&format!("{:?}", {
sequoia_keystore::sequoia_ipc::Context::configure().build()
@ -1016,6 +1045,7 @@ fn apply_key(config: &mut Option<&mut Config>, cli: &mut Option<&mut Augmentatio
/// Schema for the `key.generate` section.
const KEY_GENERATE_SCHEMA: Schema = &[
("cipher-suite", apply_key_generate_cipher_suite),
("profile", apply_key_generate_profile),
];
/// Validates the `key.generate` section.
@ -1054,6 +1084,30 @@ fn apply_key_generate_cipher_suite(config: &mut Option<&mut Config>,
Ok(())
}
/// Validates the `key.generate.profile` value.
fn apply_key_generate_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.key_generate_profile = Some(v.clone());
}
if let Some(cli) = cli {
cli.insert(
"key.generate.profile",
v.to_possible_value().expect("just validated").get_name().into());
}
Ok(())
}
/// Schema for the `network` section.
const NETWORK_SCHEMA: Schema = &[
("keyservers", apply_network_keyservers),