sq_util::get_subkey() is now the central helper fn for picking a (sub)key from a Cert.
This commit is contained in:
parent
d020d5a8c5
commit
0d249a47fd
4 changed files with 64 additions and 84 deletions
|
@ -1,12 +1,10 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::Result;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation;
|
|
||||||
use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole};
|
|
||||||
use sequoia_openpgp::parse::stream::{
|
use sequoia_openpgp::parse::stream::{
|
||||||
DetachedVerifierBuilder, MessageLayer, MessageStructure,
|
DetachedVerifierBuilder, MessageLayer, MessageStructure,
|
||||||
VerificationHelper,
|
VerificationHelper,
|
||||||
|
@ -20,6 +18,7 @@ use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::card_do::KeyGenerationTime;
|
use openpgp_card::card_do::KeyGenerationTime;
|
||||||
use openpgp_card::{CardApp, KeyType};
|
use openpgp_card::{CardApp, KeyType};
|
||||||
|
use openpgp_card_sequoia::sq_util::get_subkey;
|
||||||
use openpgp_card_sequoia::util::vka_as_uploadable_key;
|
use openpgp_card_sequoia::util::vka_as_uploadable_key;
|
||||||
|
|
||||||
pub const SP: &StandardPolicy = &StandardPolicy::new();
|
pub const SP: &StandardPolicy = &StandardPolicy::new();
|
||||||
|
@ -35,7 +34,8 @@ pub(crate) fn upload_subkeys(
|
||||||
KeyType::Decryption,
|
KeyType::Decryption,
|
||||||
KeyType::Authentication,
|
KeyType::Authentication,
|
||||||
] {
|
] {
|
||||||
let vka = get_subkey(cert, *kt)?;
|
let sp = StandardPolicy::new();
|
||||||
|
let vka = get_subkey(cert, &sp, *kt)?;
|
||||||
|
|
||||||
// store fingerprint as return-value
|
// store fingerprint as return-value
|
||||||
let fp = vka.fingerprint().to_hex();
|
let fp = vka.fingerprint().to_hex();
|
||||||
|
@ -56,32 +56,6 @@ pub(crate) fn upload_subkeys(
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_subkey(
|
|
||||||
cert: &Cert,
|
|
||||||
key_type: KeyType,
|
|
||||||
) -> Result<ValidKeyAmalgamation<'_, SecretParts, UnspecifiedRole, bool>> {
|
|
||||||
// Find all suitable (sub)keys for key_type.
|
|
||||||
let mut valid_ka = cert
|
|
||||||
.keys()
|
|
||||||
.with_policy(SP, None)
|
|
||||||
.secret()
|
|
||||||
.alive()
|
|
||||||
.revoked(false);
|
|
||||||
valid_ka = match key_type {
|
|
||||||
KeyType::Decryption => valid_ka.for_storage_encryption(),
|
|
||||||
KeyType::Signing => valid_ka.for_signing(),
|
|
||||||
KeyType::Authentication => valid_ka.for_authentication(),
|
|
||||||
_ => return Err(anyhow!("Unexpected KeyType")),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: for now, we just pick the first (sub)key from the list
|
|
||||||
if let Some(vka) = valid_ka.next() {
|
|
||||||
Ok(vka)
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("No suitable (sub)key found"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform signature verification for one Cert and a simple signature
|
/// Perform signature verification for one Cert and a simple signature
|
||||||
/// over a message.
|
/// over a message.
|
||||||
struct VHelper<'a> {
|
struct VHelper<'a> {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use openpgp_card_pcsc::PcscClient;
|
||||||
|
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
use openpgp_card_sequoia::sq_util::{decryption_helper, sign_helper};
|
use openpgp_card_sequoia::sq_util::{decryption_helper, sign_helper};
|
||||||
|
use openpgp_card_sequoia::util::upload_key;
|
||||||
|
|
||||||
// Filename of test key and test message to use
|
// Filename of test key and test message to use
|
||||||
|
|
||||||
|
@ -115,30 +116,31 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
println!("set url {:x?}", res);
|
println!("set url {:x?}", res);
|
||||||
|
|
||||||
let cert = Cert::from_file(TEST_KEY_PATH)?;
|
let cert = Cert::from_file(TEST_KEY_PATH)?;
|
||||||
|
let p = StandardPolicy::new();
|
||||||
|
|
||||||
println!("Upload decryption key");
|
println!("Upload decryption key");
|
||||||
openpgp_card_sequoia::util::upload_from_cert_yolo(
|
let vka = openpgp_card_sequoia::sq_util::get_subkey(
|
||||||
&mut admin,
|
|
||||||
&cert,
|
&cert,
|
||||||
|
&p,
|
||||||
KeyType::Decryption,
|
KeyType::Decryption,
|
||||||
None,
|
|
||||||
)?;
|
)?;
|
||||||
|
upload_key(&mut admin, vka, KeyType::Decryption, None)?;
|
||||||
|
|
||||||
println!("Upload signing key");
|
println!("Upload signing key");
|
||||||
openpgp_card_sequoia::util::upload_from_cert_yolo(
|
let vka = openpgp_card_sequoia::sq_util::get_subkey(
|
||||||
&mut admin,
|
|
||||||
&cert,
|
&cert,
|
||||||
|
&p,
|
||||||
KeyType::Signing,
|
KeyType::Signing,
|
||||||
None,
|
|
||||||
)?;
|
)?;
|
||||||
|
upload_key(&mut admin, vka, KeyType::Signing, None)?;
|
||||||
|
|
||||||
// FIXME: Test keys have no authentication subkey
|
println!("Upload auth key");
|
||||||
// openpgp_card_sequoia::util::upload_from_cert_yolo(
|
let vka = openpgp_card_sequoia::sq_util::get_subkey(
|
||||||
// &mut admin,
|
&cert,
|
||||||
// &cert,
|
&p,
|
||||||
// KeyType::Authentication,
|
KeyType::Authentication,
|
||||||
// None,
|
)?;
|
||||||
// )?;
|
upload_key(&mut admin, vka, KeyType::Authentication, None)?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,59 @@
|
||||||
|
|
||||||
//! Simple wrappers for performing very specific tasks with Sequoia PGP
|
//! Simple wrappers for performing very specific tasks with Sequoia PGP
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use openpgp::armor;
|
use openpgp::armor;
|
||||||
|
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||||
use openpgp::crypto;
|
use openpgp::crypto;
|
||||||
|
use openpgp::packet::key::SecretParts;
|
||||||
use openpgp::parse::{
|
use openpgp::parse::{
|
||||||
stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper},
|
stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper},
|
||||||
Parse,
|
Parse,
|
||||||
};
|
};
|
||||||
use openpgp::policy::Policy;
|
use openpgp::policy::Policy;
|
||||||
use openpgp::serialize::stream::{Message, Signer};
|
use openpgp::serialize::stream::{Message, Signer};
|
||||||
|
use openpgp::Cert;
|
||||||
use sequoia_openpgp as openpgp;
|
use sequoia_openpgp as openpgp;
|
||||||
|
|
||||||
|
use openpgp_card::KeyType;
|
||||||
|
|
||||||
|
/// Retrieve a (sub)key from a Cert, for a given KeyType.
|
||||||
|
///
|
||||||
|
/// If no, or multiple suitable (sub)keys are found, an error is thrown.
|
||||||
|
pub fn get_subkey<'a>(
|
||||||
|
cert: &'a Cert,
|
||||||
|
policy: &'a dyn Policy,
|
||||||
|
key_type: KeyType,
|
||||||
|
) -> Result<ValidErasedKeyAmalgamation<'a, SecretParts>> {
|
||||||
|
// Find all suitable (sub)keys for key_type.
|
||||||
|
let valid_ka = cert
|
||||||
|
.keys()
|
||||||
|
.with_policy(policy, None)
|
||||||
|
.secret()
|
||||||
|
.alive()
|
||||||
|
.revoked(false);
|
||||||
|
let valid_ka = match key_type {
|
||||||
|
KeyType::Decryption => valid_ka.for_storage_encryption(),
|
||||||
|
KeyType::Signing => valid_ka.for_signing(),
|
||||||
|
KeyType::Authentication => valid_ka.for_authentication(),
|
||||||
|
_ => return Err(anyhow!("Unexpected KeyType")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut vkas: Vec<_> = valid_ka.collect();
|
||||||
|
|
||||||
|
if vkas.len() == 1 {
|
||||||
|
Ok(vkas.pop().unwrap())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"Unexpected number of suitable (sub)key found: {}",
|
||||||
|
vkas.len()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce an armored signature from `input` and a Signer `s`.
|
||||||
pub fn sign_helper<S>(s: S, input: &mut dyn io::Read) -> Result<String>
|
pub fn sign_helper<S>(s: S, input: &mut dyn io::Read) -> Result<String>
|
||||||
where
|
where
|
||||||
S: crypto::Signer + Send + Sync,
|
S: crypto::Signer + Send + Sync,
|
||||||
|
@ -36,13 +76,15 @@ where
|
||||||
String::from_utf8(buffer).context("Failed to convert signature to utf8")
|
String::from_utf8(buffer).context("Failed to convert signature to utf8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produce decrypted plaintext from a VerificationHelper+DecryptionHelper
|
||||||
|
/// `d` and the ciphertext `msg`.
|
||||||
pub fn decryption_helper<D>(
|
pub fn decryption_helper<D>(
|
||||||
d: D,
|
d: D,
|
||||||
msg: Vec<u8>,
|
msg: Vec<u8>,
|
||||||
p: &dyn Policy,
|
p: &dyn Policy,
|
||||||
) -> Result<Vec<u8>>
|
) -> Result<Vec<u8>>
|
||||||
where
|
where
|
||||||
D: crypto::Decryptor + VerificationHelper + DecryptionHelper,
|
D: VerificationHelper + DecryptionHelper,
|
||||||
{
|
{
|
||||||
let mut decrypted = Vec::new();
|
let mut decrypted = Vec::new();
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::convert::TryInto;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use openpgp::armor;
|
use openpgp::armor;
|
||||||
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||||
|
@ -257,44 +257,6 @@ pub fn public_to_fingerprint(
|
||||||
fp.as_bytes().try_into()
|
fp.as_bytes().try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FIXME: this is used in main.rs for testing, but should probably not exist.
|
|
||||||
///
|
|
||||||
/// Convenience fn to select and upload a (sub)key from a Cert, as a given
|
|
||||||
/// KeyType. If multiple suitable (sub)keys are found, the first one is
|
|
||||||
/// used.
|
|
||||||
///
|
|
||||||
/// (sub)keys for upload should probably always be explicitly picked by
|
|
||||||
/// client software.
|
|
||||||
pub fn upload_from_cert_yolo(
|
|
||||||
oca: &mut Admin,
|
|
||||||
cert: &Cert,
|
|
||||||
key_type: KeyType,
|
|
||||||
password: Option<String>,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let policy = StandardPolicy::new();
|
|
||||||
|
|
||||||
// Find all suitable (sub)keys for key_type.
|
|
||||||
let mut valid_ka = cert
|
|
||||||
.keys()
|
|
||||||
.with_policy(&policy, None)
|
|
||||||
.secret()
|
|
||||||
.alive()
|
|
||||||
.revoked(false);
|
|
||||||
valid_ka = match key_type {
|
|
||||||
KeyType::Decryption => valid_ka.for_storage_encryption(),
|
|
||||||
KeyType::Signing => valid_ka.for_signing(),
|
|
||||||
KeyType::Authentication => valid_ka.for_authentication(),
|
|
||||||
_ => return Err(anyhow!("Unexpected KeyType").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: for now, we just pick the first (sub)key from the list
|
|
||||||
if let Some(vka) = valid_ka.next() {
|
|
||||||
upload_key(oca, vka, key_type, password).map_err(|e| e.into())
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("No suitable (sub)key found").into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType.
|
/// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType.
|
||||||
///
|
///
|
||||||
/// The caller needs to make sure that `vka` is suitable for `key_type`.
|
/// The caller needs to make sure that `vka` is suitable for `key_type`.
|
||||||
|
|
Loading…
Reference in a new issue