Use key designators for sq key subkey {delete,password}.
- Port `sq key subkey delete` and `sq key subkey password` to the key designator framework.
This commit is contained in:
parent
f139b50f24
commit
4d5b807f61
@ -1,8 +1,5 @@
|
||||
use clap::Args;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
use examples::Actions;
|
||||
@ -12,11 +9,13 @@ use examples::Setup;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::ClapData;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
use crate::cli::types::key_designator;
|
||||
|
||||
pub struct AdditionalDocs {}
|
||||
pub struct CertAdditionalDocs {}
|
||||
|
||||
impl cert_designator::AdditionalDocs for AdditionalDocs {
|
||||
impl cert_designator::AdditionalDocs for CertAdditionalDocs {
|
||||
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
|
||||
match arg {
|
||||
"file" =>
|
||||
@ -31,6 +30,16 @@ impl cert_designator::AdditionalDocs for AdditionalDocs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyAdditionalDocs {}
|
||||
|
||||
impl key_designator::AdditionalDocs for KeyAdditionalDocs {
|
||||
fn help(_arg: &'static str, _help: &'static str)
|
||||
-> clap::builder::StyledStr
|
||||
{
|
||||
"Delete the specified key's secret key material".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(
|
||||
name = "delete",
|
||||
@ -41,6 +50,9 @@ Delete a certificate's secret key material.
|
||||
Unlike `sq key delete`, which deletes all the secret key material, this \
|
||||
command only deletes the specified secret key material.
|
||||
|
||||
If the secret key material is managed by multiple devices, it is \
|
||||
deleted from all of them.
|
||||
|
||||
Although the secret key material is deleted, the public keys are \
|
||||
retained. If you don't want the keys to be used anymore you should \
|
||||
revoke the keys using `sq key subkey revoke`.
|
||||
@ -53,22 +65,12 @@ pub struct Command {
|
||||
cert_designator::CertUserIDEmailFileArgs,
|
||||
cert_designator::NoPrefix,
|
||||
cert_designator::OneValueAndFileRequiresOutput,
|
||||
AdditionalDocs>,
|
||||
CertAdditionalDocs>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
value_name = "FINGERPRINT|KEYID",
|
||||
required = true,
|
||||
help = "The keys to delete",
|
||||
long_help = "\
|
||||
The keys to delete.
|
||||
|
||||
The specified keys may be either the primary key or subkeys.
|
||||
|
||||
If the secret key material is managed by multiple devices, it is \
|
||||
deleted from all of them.",
|
||||
)]
|
||||
pub key: Vec<KeyHandle>,
|
||||
#[command(flatten)]
|
||||
pub keys: KeyDesignators<
|
||||
key_designator::DefaultOptions,
|
||||
KeyAdditionalDocs>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
|
@ -2,9 +2,6 @@ use std::path::PathBuf;
|
||||
|
||||
use clap::Args;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
use examples::Actions;
|
||||
@ -14,11 +11,13 @@ use examples::Setup;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::ClapData;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
use crate::cli::types::key_designator;
|
||||
|
||||
pub struct AdditionalDocs {}
|
||||
pub struct CertAdditionalDocs {}
|
||||
|
||||
impl cert_designator::AdditionalDocs for AdditionalDocs {
|
||||
impl cert_designator::AdditionalDocs for CertAdditionalDocs {
|
||||
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
|
||||
match arg {
|
||||
"file" =>
|
||||
@ -36,6 +35,17 @@ impl cert_designator::AdditionalDocs for AdditionalDocs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyAdditionalDocs {}
|
||||
|
||||
impl key_designator::AdditionalDocs for KeyAdditionalDocs {
|
||||
fn help(_arg: &'static str, _help: &'static str)
|
||||
-> clap::builder::StyledStr
|
||||
{
|
||||
"Change the password protecting the specified key's secret key \
|
||||
material".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(
|
||||
name = "password",
|
||||
@ -62,18 +72,12 @@ pub struct Command {
|
||||
cert_designator::CertUserIDEmailFileArgs,
|
||||
cert_designator::NoPrefix,
|
||||
cert_designator::OneValueAndFileRequiresOutput,
|
||||
AdditionalDocs>,
|
||||
CertAdditionalDocs>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "Change the password of the specified key",
|
||||
long_help = "\
|
||||
Change the password of the specified key.
|
||||
|
||||
The key may be either the primary key or a subkey.",
|
||||
required = true,
|
||||
)]
|
||||
pub key: Vec<KeyHandle>,
|
||||
#[command(flatten)]
|
||||
pub keys: KeyDesignators<
|
||||
key_designator::DefaultOptions,
|
||||
KeyAdditionalDocs>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
|
@ -1,15 +1,14 @@
|
||||
//! Changes key expiration.
|
||||
|
||||
use crate::Result;
|
||||
use crate::Sq;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli;
|
||||
use crate::common::key::delete;
|
||||
use crate::Result;
|
||||
|
||||
pub fn dispatch(sq: Sq, command: cli::key::delete::Command)
|
||||
-> Result<()>
|
||||
{
|
||||
let handle =
|
||||
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
|
||||
|
||||
delete::delete(sq, handle, Vec::new(), command.output, command.binary)
|
||||
delete::delete(sq, command.cert, KeyDesignators::none(),
|
||||
command.output, command.binary)
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
//! Changes a key's password.
|
||||
|
||||
use crate::Result;
|
||||
use crate::Sq;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli;
|
||||
use crate::common::key::password;
|
||||
use crate::Result;
|
||||
|
||||
pub fn dispatch(sq: Sq, command: cli::key::password::Command)
|
||||
-> Result<()>
|
||||
{
|
||||
let handle =
|
||||
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
|
||||
|
||||
password::password(sq, handle, vec![],
|
||||
password::password(sq, command.cert, KeyDesignators::none(),
|
||||
command.clear_password,
|
||||
command.new_password_file.as_deref(),
|
||||
command.output, command.binary)
|
||||
|
@ -5,10 +5,8 @@ use crate::common::key::delete;
|
||||
pub fn dispatch(sq: Sq, command: crate::cli::key::subkey::delete::Command)
|
||||
-> Result<()>
|
||||
{
|
||||
let handle =
|
||||
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
|
||||
assert!(! command.keys.is_empty());
|
||||
|
||||
assert!(! command.key.is_empty());
|
||||
|
||||
delete(sq, handle, command.key, command.output, command.binary)
|
||||
delete(sq, command.cert, Some(command.keys),
|
||||
command.output, command.binary)
|
||||
}
|
||||
|
@ -5,12 +5,9 @@ use crate::common::key::password;
|
||||
pub fn dispatch(sq: Sq, command: crate::cli::key::subkey::password::Command)
|
||||
-> Result<()>
|
||||
{
|
||||
let handle =
|
||||
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
|
||||
assert!(! command.keys.is_empty());
|
||||
|
||||
assert!(! command.key.is_empty());
|
||||
|
||||
password(sq, handle, command.key,
|
||||
password(sq, command.cert, Some(command.keys),
|
||||
command.clear_password, command.new_password_file.as_deref(),
|
||||
command.output, command.binary)
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
use anyhow::Context;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::Cert;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use openpgp::cert::amalgamation::key::PrimaryKey;
|
||||
use openpgp::parse::Parse;
|
||||
use openpgp::packet::key;
|
||||
use openpgp::packet::Key;
|
||||
|
||||
use sequoia_keystore as keystore;
|
||||
|
||||
use crate::cli::types::FileStdinOrKeyHandle;
|
||||
use crate::Sq;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::FileStdinOrKeyHandle;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
|
||||
|
||||
mod expire;
|
||||
@ -34,25 +37,35 @@ pub use password::password;
|
||||
/// secret key material.
|
||||
///
|
||||
/// The returned keys are not unlocked.
|
||||
pub fn get_keys<'a>(sq: &'a Sq,
|
||||
cert_handle: FileStdinOrKeyHandle,
|
||||
keys: Vec<KeyHandle>)
|
||||
-> Result<(Cert, Vec<(Key<key::PublicParts, key::UnspecifiedRole>,
|
||||
bool,
|
||||
Option<Vec<keystore::Key>>)>)>
|
||||
pub fn get_keys<CA, CP, CO, CD, KO, KD>(
|
||||
sq: &Sq,
|
||||
cert: &CertDesignators<CA, CP, CO, CD>,
|
||||
keys: Option<&KeyDesignators<KO, KD>>)
|
||||
-> Result<(Cert,
|
||||
FileStdinOrKeyHandle,
|
||||
Vec<(Key<key::PublicParts, key::UnspecifiedRole>,
|
||||
bool,
|
||||
Option<Vec<keystore::Key>>)>)>
|
||||
where CP: cert_designator::ArgumentPrefix,
|
||||
{
|
||||
let mut ks = None;
|
||||
|
||||
let cert = match cert_handle {
|
||||
FileStdinOrKeyHandle::FileOrStdin(ref file) => {
|
||||
let input = file.open()?;
|
||||
let cert = Cert::from_buffered_reader(input)?;
|
||||
assert_eq!(cert.len(), 1);
|
||||
if let Some(keys) = keys {
|
||||
assert!(keys.len() > 0);
|
||||
}
|
||||
|
||||
let (cert, cert_source)
|
||||
= sq.resolve_cert(&cert, sequoia_wot::FULLY_TRUSTED)?;
|
||||
|
||||
let cert = match cert_source {
|
||||
FileStdinOrKeyHandle::FileOrStdin(ref file) => {
|
||||
// If it is not a TSK, there is nothing to do.
|
||||
if ! cert.is_tsk() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} does not contain any secret key material.",
|
||||
cert.fingerprint()));
|
||||
"{} (read from {}) does not contain any secret \
|
||||
key material.",
|
||||
cert.fingerprint(), file));
|
||||
}
|
||||
|
||||
cert
|
||||
@ -63,94 +76,57 @@ pub fn get_keys<'a>(sq: &'a Sq,
|
||||
}
|
||||
};
|
||||
|
||||
let vc = Cert::with_policy(&cert, sq.policy, sq.time)
|
||||
.with_context(|| {
|
||||
format!("The certificate {} is not valid under the \
|
||||
current policy.",
|
||||
cert.fingerprint())
|
||||
})?;
|
||||
|
||||
let kas = if let Some(keys) = keys {
|
||||
sq.resolve_keys(&vc, &cert_source, &keys, true)?
|
||||
} else {
|
||||
vc.keys().collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let mut ks = ks.map(|ks| ks.lock().unwrap());
|
||||
|
||||
let list: Vec<(Key<_, _>, bool, Option<_>)> = if keys.is_empty() {
|
||||
// Get all secret key material.
|
||||
let list: Vec<_>
|
||||
= cert.keys().filter_map(|ka| {
|
||||
if let Some(ks) = ks.as_mut() {
|
||||
let remote_keys = ks.find_key(ka.key_handle()).ok()?;
|
||||
if remote_keys.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((ka.key().clone(), ka.primary(), Some(remote_keys)))
|
||||
}
|
||||
} else {
|
||||
if ka.has_secret() {
|
||||
Some((ka.key().clone(), ka.primary(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
let mut list: Vec<(Key<_, _>, bool, Option<_>)> = Vec::new();
|
||||
|
||||
// Make the primary last so that if something goes wrong it
|
||||
// is still possible to generate a revocation certificate.
|
||||
list.into_iter().rev().collect()
|
||||
} else {
|
||||
// Get only the specified secret key material.
|
||||
let mut list = Vec::new();
|
||||
let mut no_secret_key_material_count = 0;
|
||||
for ka in kas.into_iter() {
|
||||
let (no_secret_key_material, remote_keys)
|
||||
= if let Some(ks) = ks.as_mut()
|
||||
{
|
||||
let remote_keys = ks.find_key(ka.key_handle())?;
|
||||
(remote_keys.is_empty(), Some(remote_keys))
|
||||
} else {
|
||||
(! ka.has_secret(), None)
|
||||
};
|
||||
|
||||
let mut not_found_key_count = 0;
|
||||
let mut no_secret_key_material_count = 0;
|
||||
for key in keys.into_iter() {
|
||||
let ka = if let Some(ka)
|
||||
= cert.keys().find(|ka| ka.fingerprint().aliases(&key))
|
||||
{
|
||||
ka
|
||||
} else {
|
||||
wprintln!("{} does not contain {}",
|
||||
cert.fingerprint(), key);
|
||||
not_found_key_count += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let (no_secret_key_material, remote_keys)
|
||||
= if let Some(ks) = ks.as_mut()
|
||||
{
|
||||
let remote_keys = ks.find_key(ka.key_handle())?;
|
||||
(remote_keys.is_empty(), Some(remote_keys))
|
||||
} else {
|
||||
(! ka.has_secret(), None)
|
||||
};
|
||||
|
||||
if no_secret_key_material {
|
||||
wprintln!("{} does not contain any secret key material",
|
||||
key);
|
||||
no_secret_key_material_count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
list.push((ka.key().clone(), ka.primary(), remote_keys));
|
||||
if no_secret_key_material {
|
||||
wprintln!("{} does not contain any secret key material",
|
||||
ka.fingerprint());
|
||||
no_secret_key_material_count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if not_found_key_count > 1 {
|
||||
// Plural.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} keys not found", not_found_key_count));
|
||||
} else if not_found_key_count > 0 {
|
||||
// Singular.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} key not found", not_found_key_count));
|
||||
}
|
||||
list.push((ka.key().clone(), ka.primary(), remote_keys));
|
||||
}
|
||||
|
||||
if no_secret_key_material_count > 1 {
|
||||
// Plural.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} of the specified keys don't have secret key material",
|
||||
no_secret_key_material_count));
|
||||
} else if no_secret_key_material_count > 0 {
|
||||
// Singular.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} of the specified keys doesn't have secret key material",
|
||||
no_secret_key_material_count));
|
||||
}
|
||||
|
||||
list
|
||||
};
|
||||
if no_secret_key_material_count > 1 {
|
||||
// Plural.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} of the specified keys don't have secret key material",
|
||||
no_secret_key_material_count));
|
||||
} else if no_secret_key_material_count > 0 {
|
||||
// Singular.
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} of the specified keys doesn't have secret key material",
|
||||
no_secret_key_material_count));
|
||||
}
|
||||
|
||||
assert!(! list.is_empty());
|
||||
|
||||
Ok((cert, list))
|
||||
Ok((cert, cert_source, list))
|
||||
}
|
||||
|
@ -2,28 +2,32 @@
|
||||
use anyhow::Context;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use openpgp::Packet;
|
||||
use openpgp::serialize::Serialize;
|
||||
|
||||
use crate::Sq;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::FileStdinOrKeyHandle;
|
||||
use crate::Sq;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
|
||||
use super::get_keys;
|
||||
|
||||
pub fn delete(sq: Sq,
|
||||
cert_handle: FileStdinOrKeyHandle,
|
||||
keys: Vec<KeyHandle>,
|
||||
output: Option<FileOrStdout>,
|
||||
binary: bool)
|
||||
pub fn delete<CA, CP, CO, CD, KO, KD>(
|
||||
sq: Sq,
|
||||
cert: CertDesignators<CA, CP, CO, CD>,
|
||||
keys: Option<KeyDesignators<KO, KD>>,
|
||||
output: Option<FileOrStdout>,
|
||||
binary: bool)
|
||||
-> Result<()>
|
||||
where CP: cert_designator::ArgumentPrefix,
|
||||
{
|
||||
let ks = matches!(cert_handle, FileStdinOrKeyHandle::KeyHandle(_));
|
||||
|
||||
let (cert, to_delete) = get_keys(&sq, cert_handle, keys)?;
|
||||
let (cert, cert_source, to_delete)
|
||||
= get_keys(&sq, &cert, keys.as_ref())?;
|
||||
|
||||
let ks = matches!(cert_source, FileStdinOrKeyHandle::KeyHandle(_));
|
||||
if ks {
|
||||
// Delete the secret key material from the key store.
|
||||
for (key, _primary, remote_keys) in to_delete.into_iter() {
|
||||
|
@ -4,7 +4,6 @@ use anyhow::Context;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::crypto::Password;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::serialize::Serialize;
|
||||
use openpgp::Packet;
|
||||
use openpgp::Result;
|
||||
@ -14,18 +13,23 @@ use keystore::Protection;
|
||||
|
||||
use crate::common;
|
||||
use crate::Sq;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::FileStdinOrKeyHandle;
|
||||
use crate::cli::types::KeyDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
use crate::common::password;
|
||||
|
||||
pub fn password(sq: Sq,
|
||||
cert_handle: FileStdinOrKeyHandle,
|
||||
keys: Vec<KeyHandle>,
|
||||
clear_password: bool,
|
||||
new_password_file: Option<&Path>,
|
||||
output: Option<FileOrStdout>,
|
||||
binary: bool)
|
||||
pub fn password<CA, CP, CO, CD, KO, KD>(
|
||||
sq: Sq,
|
||||
cert: CertDesignators<CA, CP, CO, CD>,
|
||||
keys: Option<KeyDesignators<KO, KD>>,
|
||||
clear_password: bool,
|
||||
new_password_file: Option<&Path>,
|
||||
output: Option<FileOrStdout>,
|
||||
binary: bool)
|
||||
-> Result<()>
|
||||
where CP: cert_designator::ArgumentPrefix,
|
||||
{
|
||||
let mut new_password_ = None;
|
||||
// Some(password) => new password
|
||||
@ -44,11 +48,11 @@ pub fn password(sq: Sq,
|
||||
Ok(new_password_.clone().unwrap())
|
||||
};
|
||||
|
||||
let ks = matches!(cert_handle, FileStdinOrKeyHandle::KeyHandle(_));
|
||||
|
||||
let (cert, mut list) = super::get_keys(&sq, cert_handle, keys)?;
|
||||
let (cert, cert_source, mut list)
|
||||
= super::get_keys(&sq, &cert, keys.as_ref())?;
|
||||
let uid = sq.best_userid(&cert, true);
|
||||
|
||||
let ks = matches!(cert_source, FileStdinOrKeyHandle::KeyHandle(_));
|
||||
if ks {
|
||||
// Change the password of the secret key material on the key
|
||||
// store.
|
||||
|
Loading…
Reference in New Issue
Block a user