From 0d249a47fd94fcc6f22f677aa24b898c657c932e Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 11 Sep 2021 16:27:31 +0200 Subject: [PATCH] sq_util::get_subkey() is now the central helper fn for picking a (sub)key from a Cert. --- card-functionality/src/util.rs | 34 +++------------------ openpgp-card-sequoia/src/main.rs | 28 ++++++++++-------- openpgp-card-sequoia/src/sq_util.rs | 46 +++++++++++++++++++++++++++-- openpgp-card-sequoia/src/util.rs | 40 +------------------------ 4 files changed, 64 insertions(+), 84 deletions(-) diff --git a/card-functionality/src/util.rs b/card-functionality/src/util.rs index 60071dd..c3a201f 100644 --- a/card-functionality/src/util.rs +++ b/card-functionality/src/util.rs @@ -1,12 +1,10 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -use anyhow::{anyhow, Result}; +use anyhow::Result; use std::io::Write; use std::time::SystemTime; -use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation; -use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole}; use sequoia_openpgp::parse::stream::{ DetachedVerifierBuilder, MessageLayer, MessageStructure, VerificationHelper, @@ -20,6 +18,7 @@ use sequoia_openpgp::Cert; use openpgp_card::card_do::KeyGenerationTime; use openpgp_card::{CardApp, KeyType}; +use openpgp_card_sequoia::sq_util::get_subkey; use openpgp_card_sequoia::util::vka_as_uploadable_key; pub const SP: &StandardPolicy = &StandardPolicy::new(); @@ -35,7 +34,8 @@ pub(crate) fn upload_subkeys( KeyType::Decryption, KeyType::Authentication, ] { - let vka = get_subkey(cert, *kt)?; + let sp = StandardPolicy::new(); + let vka = get_subkey(cert, &sp, *kt)?; // store fingerprint as return-value let fp = vka.fingerprint().to_hex(); @@ -56,32 +56,6 @@ pub(crate) fn upload_subkeys( Ok(out) } -fn get_subkey( - cert: &Cert, - key_type: KeyType, -) -> Result> { - // 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 /// over a message. struct VHelper<'a> { diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 3152a17..60152be 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -15,6 +15,7 @@ use openpgp_card_pcsc::PcscClient; use openpgp_card_sequoia::card::Open; 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 @@ -115,30 +116,31 @@ fn main() -> Result<(), Box> { println!("set url {:x?}", res); let cert = Cert::from_file(TEST_KEY_PATH)?; + let p = StandardPolicy::new(); println!("Upload decryption key"); - openpgp_card_sequoia::util::upload_from_cert_yolo( - &mut admin, + let vka = openpgp_card_sequoia::sq_util::get_subkey( &cert, + &p, KeyType::Decryption, - None, )?; + upload_key(&mut admin, vka, KeyType::Decryption, None)?; println!("Upload signing key"); - openpgp_card_sequoia::util::upload_from_cert_yolo( - &mut admin, + let vka = openpgp_card_sequoia::sq_util::get_subkey( &cert, + &p, KeyType::Signing, - None, )?; + upload_key(&mut admin, vka, KeyType::Signing, None)?; - // FIXME: Test keys have no authentication subkey - // openpgp_card_sequoia::util::upload_from_cert_yolo( - // &mut admin, - // &cert, - // KeyType::Authentication, - // None, - // )?; + println!("Upload auth key"); + let vka = openpgp_card_sequoia::sq_util::get_subkey( + &cert, + &p, + KeyType::Authentication, + )?; + upload_key(&mut admin, vka, KeyType::Authentication, None)?; println!(); diff --git a/openpgp-card-sequoia/src/sq_util.rs b/openpgp-card-sequoia/src/sq_util.rs index 0b451a0..f018400 100644 --- a/openpgp-card-sequoia/src/sq_util.rs +++ b/openpgp-card-sequoia/src/sq_util.rs @@ -3,19 +3,59 @@ //! Simple wrappers for performing very specific tasks with Sequoia PGP -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use std::io; use openpgp::armor; +use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation; use openpgp::crypto; +use openpgp::packet::key::SecretParts; use openpgp::parse::{ stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper}, Parse, }; use openpgp::policy::Policy; use openpgp::serialize::stream::{Message, Signer}; +use openpgp::Cert; 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> { + // 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, input: &mut dyn io::Read) -> Result where S: crypto::Signer + Send + Sync, @@ -36,13 +76,15 @@ where 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: D, msg: Vec, p: &dyn Policy, ) -> Result> where - D: crypto::Decryptor + VerificationHelper + DecryptionHelper, + D: VerificationHelper + DecryptionHelper, { let mut decrypted = Vec::new(); { diff --git a/openpgp-card-sequoia/src/util.rs b/openpgp-card-sequoia/src/util.rs index 35b1bec..009a6ad 100644 --- a/openpgp-card-sequoia/src/util.rs +++ b/openpgp-card-sequoia/src/util.rs @@ -8,7 +8,7 @@ use std::convert::TryInto; use std::io; use std::time::SystemTime; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use openpgp::armor; use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation; @@ -257,44 +257,6 @@ pub fn public_to_fingerprint( 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, -) -> Result<(), Box> { - 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. /// /// The caller needs to make sure that `vka` is suitable for `key_type`.