From 079cc32427e8d5806a5cc7bd87cacc84ee6f2b72 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Tue, 31 May 2022 00:31:46 +0200 Subject: [PATCH] Add callback Fn for touch confirmation prompt for signing operations. --- card-functionality/src/tests.rs | 3 +- openpgp-card-examples/src/bin/detach-sign.rs | 2 +- openpgp-card-sequoia/examples/test.rs | 2 +- openpgp-card-sequoia/src/card.rs | 16 ++++++-- openpgp-card-sequoia/src/lib.rs | 2 +- openpgp-card-sequoia/src/signer.rs | 39 ++++++++++++++++++-- openpgp-card-sequoia/src/util.rs | 18 +++++---- tools/src/bin/opgpcard/main.rs | 6 ++- 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index 09ea7a5..9a5e1fe 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -95,7 +95,7 @@ pub fn test_sign( let cert = Cert::from_str(param[0])?; let msg = "Hello world, I am signed."; - let sig = openpgp_card_sequoia::util::sign(&mut pgpt, &cert, &mut msg.as_bytes())?; + let sig = openpgp_card_sequoia::util::sign(&mut pgpt, &cert, &mut msg.as_bytes(), &|| {})?; // validate sig assert!(util::verify_sig(&cert, msg.as_bytes(), sig.as_bytes())?); @@ -266,6 +266,7 @@ pub fn test_keygen( Some(key_aut), Some(b"123456"), &|| {}, + &|| {}, )?; let armored = String::from_utf8(cert.armored().to_vec()?)?; diff --git a/openpgp-card-examples/src/bin/detach-sign.rs b/openpgp-card-examples/src/bin/detach-sign.rs index 5aea145..5091273 100644 --- a/openpgp-card-examples/src/bin/detach-sign.rs +++ b/openpgp-card-examples/src/bin/detach-sign.rs @@ -34,7 +34,7 @@ fn main() -> Result<(), Box> { let mut sign = open.signing_card().unwrap(); let cert = Cert::from_file(cert_file)?; - let s = sign.signer(&cert)?; + let s = sign.signer(&cert, &|| println!("Touch confirmation needed for signing"))?; let stdout = std::io::stdout(); diff --git a/openpgp-card-sequoia/examples/test.rs b/openpgp-card-sequoia/examples/test.rs index 729a0e1..5fcf50d 100644 --- a/openpgp-card-sequoia/examples/test.rs +++ b/openpgp-card-sequoia/examples/test.rs @@ -196,7 +196,7 @@ fn main() -> Result<(), Box> { let text = "Hello world, I am signed."; - let signer = sign.signer(&cert)?; + let signer = sign.signer(&cert, &|| {})?; let sig = sq_util::sign_helper(signer, &mut text.as_bytes())?; println!("Signature from card:\n{}", sig) diff --git a/openpgp-card-sequoia/src/card.rs b/openpgp-card-sequoia/src/card.rs index e121be7..1aedfaa 100644 --- a/openpgp-card-sequoia/src/card.rs +++ b/openpgp-card-sequoia/src/card.rs @@ -352,18 +352,26 @@ pub struct Sign<'app, 'open> { } impl<'app, 'open> Sign<'app, 'open> { - pub fn signer(&mut self, cert: &Cert) -> std::result::Result, Error> { + pub fn signer( + &mut self, + cert: &Cert, + touch_prompt: &'open (dyn Fn() + Send + Sync), + ) -> std::result::Result, Error> { // FIXME: depending on the setting in "PW1 Status byte", only one // signature can be made after verification for signing - CardSigner::new(&mut self.oc.opt, cert) + CardSigner::new(&mut self.oc.opt, cert, touch_prompt) } - pub fn signer_from_pubkey(&mut self, pubkey: PublicKey) -> CardSigner<'_, 'app> { + pub fn signer_from_pubkey( + &mut self, + pubkey: PublicKey, + touch_prompt: &'open (dyn Fn() + Send + Sync), + ) -> CardSigner<'_, 'app> { // FIXME: depending on the setting in "PW1 Status byte", only one // signature can be made after verification for signing - CardSigner::with_pubkey(&mut self.oc.opt, pubkey) + CardSigner::with_pubkey(&mut self.oc.opt, pubkey, touch_prompt) } /// Generate Attestation (Yubico) diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index dfbb45a..f4146b8 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -116,7 +116,7 @@ //! # let (cert, _) = //! # CertBuilder::general_purpose(None, Some("alice@example.org")) //! # .generate()?; -//! let signer = user.signer(&cert); +//! let signer = user.signer(&cert, &|| println!("Touch confirmation needed for signing")); //! //! // Perform signing operation(s) //! // .. diff --git a/openpgp-card-sequoia/src/signer.rs b/openpgp-card-sequoia/src/signer.rs index dea1d63..e31a2be 100644 --- a/openpgp-card-sequoia/src/signer.rs +++ b/openpgp-card-sequoia/src/signer.rs @@ -22,6 +22,9 @@ pub struct CardSigner<'a, 'app> { /// The matching public key for the card's signing key public: PublicKey, + + /// Callback function to signal if touch confirmation is needed + touch_prompt: &'a (dyn Fn() + Send + Sync), } impl<'a, 'app> CardSigner<'a, 'app> { @@ -29,9 +32,10 @@ impl<'a, 'app> CardSigner<'a, 'app> { /// /// An Error is returned if no match between the card's signing /// key and a (sub)key of `cert` can be made. - pub fn new( + pub(crate) fn new( ca: &'a mut OpenPgpTransaction<'app>, cert: &openpgp::Cert, + touch_prompt: &'a (dyn Fn() + Send + Sync), ) -> Result, Error> { // Get the fingerprint for the signing key from the card. let ard = ca.application_related_data()?; @@ -44,7 +48,7 @@ impl<'a, 'app> CardSigner<'a, 'app> { if let Some(eka) = sq_util::get_subkey_by_fingerprint(cert, &fp)? { let key = eka.key().clone(); - Ok(Self::with_pubkey(ca, key)) + Ok(Self::with_pubkey(ca, key, touch_prompt)) } else { Err(Error::InternalError(format!( "Failed to find (sub)key {} in cert", @@ -61,8 +65,13 @@ impl<'a, 'app> CardSigner<'a, 'app> { pub(crate) fn with_pubkey( ca: &'a mut OpenPgpTransaction<'app>, public: PublicKey, + touch_prompt: &'a (dyn Fn() + Send + Sync), ) -> CardSigner<'a, 'app> { - CardSigner { ca, public } + CardSigner { + ca, + public, + touch_prompt, + } } } @@ -76,6 +85,18 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> { hash_algo: openpgp::types::HashAlgorithm, digest: &[u8], ) -> openpgp::Result { + // FIXME: use cached ARD value from caller? + let ard = self.ca.application_related_data()?; + + // Touch is required if: + // - the card supports the feature + // - and the policy is set to a value other than 'Off' + let touch_required = if let Some(uif) = ard.uif_pso_cds()? { + uif.touch_policy().touch_required() + } else { + false + }; + // Delegate a signing operation to the OpenPGP card. // // This fn prepares the data structures that openpgp-card needs to @@ -111,6 +132,10 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> { } }; + if touch_required { + (self.touch_prompt)(); + } + let sig = self.ca.signature_for_hash(hash)?; let mpi = mpi::MPI::new(&sig[..]); @@ -119,6 +144,10 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> { (PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => { let hash = Hash::EdDSA(digest); + if touch_required { + (self.touch_prompt)(); + } + let sig = self.ca.signature_for_hash(hash)?; let r = mpi::MPI::new(&sig[..32]); @@ -134,6 +163,10 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> { _ => Hash::ECDSA(digest), }; + if touch_required { + (self.touch_prompt)(); + } + let sig = self.ca.signature_for_hash(hash)?; let len_2 = sig.len() / 2; diff --git a/openpgp-card-sequoia/src/util.rs b/openpgp-card-sequoia/src/util.rs index c71e60b..32024e0 100644 --- a/openpgp-card-sequoia/src/util.rs +++ b/openpgp-card-sequoia/src/util.rs @@ -49,7 +49,8 @@ pub fn make_cert<'app>( key_dec: Option, key_aut: Option, pw1: Option<&[u8]>, - prompt: &dyn Fn(), + pinpad_prompt: &dyn Fn(), + touch_prompt: &(dyn Fn() + Send + Sync), ) -> Result { let mut pp = vec![]; @@ -80,11 +81,11 @@ pub fn make_cert<'app>( if let Some(pw1) = pw1 { open.verify_user_for_signing(pw1)?; } else { - open.verify_user_for_signing_pinpad(prompt)?; + open.verify_user_for_signing_pinpad(pinpad_prompt)?; } if let Some(mut sign) = open.signing_card() { // Card-backed signer for bindings - let mut card_signer = sign.signer_from_pubkey(key_sig.clone()); + let mut card_signer = sign.signer_from_pubkey(key_sig.clone(), touch_prompt); let signing_bsig: Packet = sub_dec .bind(&mut card_signer, &cert, signing_builder)? @@ -111,11 +112,11 @@ pub fn make_cert<'app>( if let Some(pw1) = pw1 { open.verify_user_for_signing(pw1)?; } else { - open.verify_user_for_signing_pinpad(prompt)?; + open.verify_user_for_signing_pinpad(pinpad_prompt)?; } if let Some(mut sign) = open.signing_card() { // Card-backed signer for bindings - let mut card_signer = sign.signer_from_pubkey(key_sig.clone()); + let mut card_signer = sign.signer_from_pubkey(key_sig.clone(), touch_prompt); // Temporary version of the cert let cert = Cert::try_from(pp.clone())?; @@ -153,12 +154,12 @@ pub fn make_cert<'app>( if let Some(pw1) = pw1 { open.verify_user_for_signing(pw1)?; } else { - open.verify_user_for_signing_pinpad(prompt)?; + open.verify_user_for_signing_pinpad(pinpad_prompt)?; } if let Some(mut sign) = open.signing_card() { // Card-backed signer for bindings - let mut card_signer = sign.signer_from_pubkey(key_sig); + let mut card_signer = sign.signer_from_pubkey(key_sig, touch_prompt); // Temporary version of the cert let cert = Cert::try_from(pp.clone())?; @@ -378,10 +379,11 @@ pub fn sign( card_tx: &'_ mut OpenPgpTransaction<'_>, cert: &Cert, input: &mut dyn io::Read, + touch_prompt: &(dyn Fn() + Send + Sync), ) -> Result { let mut armorer = armor::Writer::new(vec![], armor::Kind::Signature)?; { - let s = signer::CardSigner::new(card_tx, cert)?; + let s = signer::CardSigner::new(card_tx, cert, touch_prompt)?; let message = Message::new(&mut armorer); let mut message = Signer::new(message, s).detached().build()?; diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 148c93c..2afcecc 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -920,7 +920,7 @@ fn sign_detached( let user_pin = util::get_pin(&mut open, pin_file, ENTER_USER_PIN); let mut sign = util::verify_to_sign(&mut open, user_pin.as_deref())?; - let s = sign.signer(&cert)?; + let s = sign.signer(&cert, &|| println!("Touch confirmation needed for signing"))?; let message = Armorer::new(Message::new(std::io::stdout())).build()?; let mut signer = Signer::new(message, s).detached().build()?; @@ -1019,7 +1019,9 @@ fn get_cert( ); } - make_cert(open, key_sig, key_dec, key_aut, user_pin, prompt) + make_cert(open, key_sig, key_dec, key_aut, user_pin, prompt, &|| { + println!("Touch confirmation needed for signing") + }) } fn generate_keys(