sq_util::get_subkey() is now the central helper fn for picking a (sub)key from a Cert.

This commit is contained in:
Heiko Schaefer 2021-09-11 16:27:31 +02:00
parent d020d5a8c5
commit 0d249a47fd
4 changed files with 64 additions and 84 deletions

View file

@ -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> {

View file

@ -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!();

View file

@ -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();
{ {

View file

@ -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`.