diff --git a/card-functionality/src/util.rs b/card-functionality/src/util.rs index fb76442..5a2d201 100644 --- a/card-functionality/src/util.rs +++ b/card-functionality/src/util.rs @@ -18,7 +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::sq_util; use openpgp_card_sequoia::util::vka_as_uploadable_key; pub const SP: &StandardPolicy = &StandardPolicy::new(); @@ -35,7 +35,7 @@ pub(crate) fn upload_subkeys( KeyType::Decryption, KeyType::Authentication, ] { - if let Some(vka) = get_subkey(cert, policy, *kt)? { + if let Some(vka) = sq_util::get_subkey_by_type(cert, policy, *kt)? { // store fingerprint as return-value let fp = vka.fingerprint().to_hex(); // store key creation time as return-value diff --git a/openpgp-card-sequoia/src/decryptor.rs b/openpgp-card-sequoia/src/decryptor.rs index b7188dd..41c5c1c 100644 --- a/openpgp-card-sequoia/src/decryptor.rs +++ b/openpgp-card-sequoia/src/decryptor.rs @@ -18,6 +18,7 @@ use sequoia_openpgp as openpgp; use openpgp_card::crypto_data::Cryptogram; use openpgp_card::{CardApp, Error}; +use crate::sq_util; use crate::PublicKey; pub struct CardDecryptor<'a> { @@ -47,23 +48,23 @@ impl<'a> CardDecryptor<'a> { // Transform into Sequoia Fingerprint let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes()); - // Find the matching encryption-capable (sub)key in `cert` - let keys: Vec<_> = cert - .keys() - .with_policy(policy, None) - .for_storage_encryption() - .for_transport_encryption() - .filter(|ka| ka.fingerprint() == fp) - .map(|ka| ka.key()) - .collect(); - - // Exactly one matching (sub)key should be found. If not, fail! - if keys.len() == 1 { - let public = keys[0].clone(); - Ok(Self { ca, public }) + if let Some(vk) = + sq_util::get_subkey_by_fingerprint(cert, policy, &fp)? + { + if vk.for_storage_encryption() || vk.for_transport_encryption() + { + let public = vk.key().clone(); + Ok(Self { ca, public }) + } else { + Err(Error::InternalError(anyhow!( + "(Sub)key {} in the cert isn't encryption capable", + fp + ))) + } } else { Err(Error::InternalError(anyhow!( - "Failed to find a matching (sub)key in cert" + "Failed to find (sub)key {} in cert", + fp ))) } } else { diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 23234d8..2fe7194 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -14,7 +14,7 @@ use openpgp_card::KeyType; 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::sq_util; // Filename of test key and test message to use @@ -118,29 +118,23 @@ fn main() -> Result<(), Box> { let cert = Cert::from_file(TEST_KEY_PATH)?; let p = StandardPolicy::new(); - if let Some(vka) = openpgp_card_sequoia::sq_util::get_subkey( - &cert, - &p, - KeyType::Signing, - )? { + if let Some(vka) = + sq_util::get_subkey_by_type(&cert, &p, KeyType::Signing)? + { println!("Upload signing key"); admin.upload_key(vka, KeyType::Signing, None)?; } - if let Some(vka) = openpgp_card_sequoia::sq_util::get_subkey( - &cert, - &p, - KeyType::Decryption, - )? { + if let Some(vka) = + sq_util::get_subkey_by_type(&cert, &p, KeyType::Decryption)? + { println!("Upload decryption key"); admin.upload_key(vka, KeyType::Decryption, None)?; } - if let Some(vka) = openpgp_card_sequoia::sq_util::get_subkey( - &cert, - &p, - KeyType::Authentication, - )? { + if let Some(vka) = + sq_util::get_subkey_by_type(&cert, &p, KeyType::Authentication)? + { println!("Upload auth key"); admin.upload_key(vka, KeyType::Authentication, None)?; } @@ -179,7 +173,7 @@ fn main() -> Result<(), Box> { let sp = StandardPolicy::new(); let d = user.decryptor(&cert, &sp)?; - let res = decryption_helper(d, msg.into_bytes(), &sp)?; + let res = sq_util::decryption_helper(d, msg.into_bytes(), &sp)?; let plain = String::from_utf8_lossy(&res); println!("Decrypted plaintext: {}", plain); @@ -204,7 +198,7 @@ fn main() -> Result<(), Box> { let text = "Hello world, I am signed."; let signer = sign.signer(&cert, &StandardPolicy::new())?; - let sig = sign_helper(signer, &mut text.as_bytes())?; + let sig = sq_util::sign_helper(signer, &mut text.as_bytes())?; println!("Signature from card:\n{}", sig) diff --git a/openpgp-card-sequoia/src/signer.rs b/openpgp-card-sequoia/src/signer.rs index 19ad19b..bdaf558 100644 --- a/openpgp-card-sequoia/src/signer.rs +++ b/openpgp-card-sequoia/src/signer.rs @@ -4,6 +4,7 @@ use std::convert::TryInto; use anyhow::anyhow; + use openpgp::crypto; use openpgp::crypto::mpi; use openpgp::policy::Policy; @@ -13,6 +14,7 @@ use sequoia_openpgp as openpgp; use openpgp_card::crypto_data::Hash; use openpgp_card::{CardApp, Error}; +use crate::sq_util; use crate::PublicKey; pub struct CardSigner<'a> { @@ -42,25 +44,22 @@ impl<'a> CardSigner<'a> { // Transform into Sequoia Fingerprint let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes()); - // Find the matching signing-capable (sub)key in `cert` - let keys: Vec<_> = cert - .keys() - .with_policy(policy, None) - .alive() - .revoked(false) - .for_signing() - .filter(|ka| ka.fingerprint() == fp) - .map(|ka| ka.key()) - .collect(); - - // Exactly one matching (sub)key should be found. If not, fail! - if keys.len() == 1 { - let public = keys[0].clone(); - - Ok(Self::with_pubkey(ca, public)) + if let Some(vk) = + sq_util::get_subkey_by_fingerprint(cert, policy, &fp)? + { + if vk.for_signing() { + let key = vk.key().clone(); + Ok(Self::with_pubkey(ca, key)) + } else { + Err(Error::InternalError(anyhow!( + "(Sub)key {} in the cert isn't signing capable", + fp + ))) + } } else { Err(Error::InternalError(anyhow!( - "Failed to find a matching (sub)key in cert" + "Failed to find (sub)key {} in cert", + fp ))) } } else { diff --git a/openpgp-card-sequoia/src/sq_util.rs b/openpgp-card-sequoia/src/sq_util.rs index e1be025..fd6bc44 100644 --- a/openpgp-card-sequoia/src/sq_util.rs +++ b/openpgp-card-sequoia/src/sq_util.rs @@ -9,25 +9,28 @@ use anyhow::{anyhow, Context, Result}; use std::io; use openpgp::armor; -use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation; +use openpgp::cert::amalgamation::{ + key::ValidErasedKeyAmalgamation, ValidAmalgamation, ValidateAmalgamation, +}; use openpgp::crypto; -use openpgp::packet::key::SecretParts; +use openpgp::packet::key::{PublicParts, SecretParts}; use openpgp::parse::{ stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper}, Parse, }; use openpgp::policy::Policy; use openpgp::serialize::stream::{Message, Signer}; +use openpgp::types::RevocationStatus; use openpgp::{Cert, Fingerprint}; use sequoia_openpgp as openpgp; -use openpgp_card::KeyType; +use openpgp_card::{Error, KeyType}; /// Retrieve a (sub)key from a Cert, for a given KeyType. /// /// Returns Ok(None), if no such (sub)key exists. /// If multiple suitable (sub)keys are found, an error is returned. -pub fn get_subkey<'a>( +pub fn get_subkey_by_type<'a>( cert: &'a Cert, policy: &'a dyn Policy, key_type: KeyType, @@ -60,8 +63,8 @@ pub fn get_subkey<'a>( } } -/// Retrieve a (sub)key from a Cert, with a specified fingerprint. -pub fn get_subkey_by_fingerprint<'a>( +/// Retrieve a private (sub)key from a Cert, by fingerprint. +pub fn get_priv_subkey_by_fingerprint<'a>( cert: &'a Cert, policy: &'a dyn Policy, fingerprint: &str, @@ -90,6 +93,51 @@ pub fn get_subkey_by_fingerprint<'a>( } } +/// Retrieve a public (sub)key from a Cert, by fingerprint. +pub fn get_subkey_by_fingerprint<'a>( + cert: &'a Cert, + policy: &'a dyn Policy, + fp: &Fingerprint, +) -> Result>, Error> { + // Find the (sub)key in `cert` that matches the fingerprint from + // the Card's signing-key slot. + let keys: Vec<_> = + cert.keys().filter(|ka| &ka.fingerprint() == fp).collect(); + + // Exactly one matching (sub)key should be found. If not, fail! + if keys.len() == 1 { + // Check if the (sub)key is valid/alive, return error + // otherwise + let validkey = keys[0].clone().with_policy(policy, None)?; + validkey.alive()?; + + if let RevocationStatus::Revoked(_) = validkey.revocation_status() { + return Err(Error::InternalError(anyhow!( + "(Sub)key {} in the cert is revoked", + fp + ))); + } + + Ok(Some(validkey)) + } else { + if keys.len() == 0 { + Ok(None) + } else if keys.len() == 2 { + Err(Error::InternalError(anyhow!( + "Found two results for {}, probably the cert has the \ + primary as a subkey?", + fp + ))) + } else { + Err(Error::InternalError(anyhow!( + "Found {} results for (sub)key {}, this is unexpected", + keys.len(), + fp + ))) + } + } +} + /// Produce an armored signature from `input` and a Signer `s`. pub fn sign_helper(s: S, input: &mut dyn io::Read) -> Result where diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index c2747b7..08fa487 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -353,20 +353,11 @@ fn factory_reset(ident: &str) -> Result<()> { fn key_import_yolo(mut admin: Admin, key: &Cert) -> Result<()> { let p = StandardPolicy::new(); - let sig = - openpgp_card_sequoia::sq_util::get_subkey(key, &p, KeyType::Signing)?; + let sig = sq_util::get_subkey_by_type(key, &p, KeyType::Signing)?; - let dec = openpgp_card_sequoia::sq_util::get_subkey( - key, - &p, - KeyType::Decryption, - )?; + let dec = sq_util::get_subkey_by_type(key, &p, KeyType::Decryption)?; - let auth = openpgp_card_sequoia::sq_util::get_subkey( - key, - &p, - KeyType::Authentication, - )?; + let auth = sq_util::get_subkey_by_type(key, &p, KeyType::Authentication)?; if let Some(sig) = sig { println!("Uploading {} as signing key", sig.fingerprint()); @@ -395,7 +386,7 @@ fn key_import_explicit( if let Some(sig_fp) = sig_fp { if let Some(sig) = - sq_util::get_subkey_by_fingerprint(key, &p, &sig_fp)? + sq_util::get_priv_subkey_by_fingerprint(key, &p, &sig_fp)? { println!("Uploading {} as signing key", sig.fingerprint()); admin.upload_key(sig, KeyType::Signing, None)?; @@ -406,7 +397,7 @@ fn key_import_explicit( if let Some(dec_fp) = dec_fp { if let Some(dec) = - sq_util::get_subkey_by_fingerprint(key, &p, &dec_fp)? + sq_util::get_priv_subkey_by_fingerprint(key, &p, &dec_fp)? { println!("Uploading {} as decryption key", dec.fingerprint()); admin.upload_key(dec, KeyType::Decryption, None)?; @@ -417,7 +408,7 @@ fn key_import_explicit( if let Some(auth_fp) = auth_fp { if let Some(auth) = - sq_util::get_subkey_by_fingerprint(key, &p, &auth_fp)? + sq_util::get_priv_subkey_by_fingerprint(key, &p, &auth_fp)? { println!("Uploading {} as authentication key", auth.fingerprint()); admin.upload_key(auth, KeyType::Authentication, None)?;