diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index 9a5e1fe..9ef54cf 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -72,7 +72,7 @@ pub fn test_decrypt( let p = StandardPolicy::new(); - let res = openpgp_card_sequoia::util::decrypt(&mut pgpt, &cert, msg.into_bytes(), &p)?; + let res = openpgp_card_sequoia::util::decrypt(&mut pgpt, &cert, msg.into_bytes(), &|| {}, &p)?; let plain = String::from_utf8_lossy(&res); assert_eq!(plain, "Hello world!\n"); diff --git a/openpgp-card-examples/src/bin/decrypt.rs b/openpgp-card-examples/src/bin/decrypt.rs index 430897a..1a70905 100644 --- a/openpgp-card-examples/src/bin/decrypt.rs +++ b/openpgp-card-examples/src/bin/decrypt.rs @@ -35,7 +35,9 @@ fn main() -> Result<(), Box> { let p = StandardPolicy::new(); let cert = Cert::from_file(cert_file)?; - let d = user.decryptor(&cert)?; + let d = user.decryptor(&cert, &|| { + println!("Touch confirmation needed for decryption") + })?; let stdin = std::io::stdin(); let mut stdout = std::io::stdout(); diff --git a/openpgp-card-sequoia/examples/test.rs b/openpgp-card-sequoia/examples/test.rs index 5fcf50d..75a5822 100644 --- a/openpgp-card-sequoia/examples/test.rs +++ b/openpgp-card-sequoia/examples/test.rs @@ -169,7 +169,9 @@ fn main() -> Result<(), Box> { println!("Encrypted message:\n{}", msg); let sp = StandardPolicy::new(); - let d = user.decryptor(&cert)?; + let d = user.decryptor(&cert, &|| { + println!("Touch confirmation needed for decryption") + })?; let res = sq_util::decryption_helper(d, msg.into_bytes(), &sp)?; let plain = String::from_utf8_lossy(&res); diff --git a/openpgp-card-sequoia/src/card.rs b/openpgp-card-sequoia/src/card.rs index 1aedfaa..604b648 100644 --- a/openpgp-card-sequoia/src/card.rs +++ b/openpgp-card-sequoia/src/card.rs @@ -340,8 +340,12 @@ pub struct User<'app, 'open> { } impl<'app, 'open> User<'app, 'open> { - pub fn decryptor(&mut self, cert: &Cert) -> Result, Error> { - CardDecryptor::new(&mut self.oc.opt, cert) + pub fn decryptor( + &mut self, + cert: &Cert, + touch_prompt: &'open (dyn Fn() + Send + Sync), + ) -> Result, Error> { + CardDecryptor::new(&mut self.oc.opt, cert, touch_prompt) } } diff --git a/openpgp-card-sequoia/src/decryptor.rs b/openpgp-card-sequoia/src/decryptor.rs index 9f38ece..b1099ad 100644 --- a/openpgp-card-sequoia/src/decryptor.rs +++ b/openpgp-card-sequoia/src/decryptor.rs @@ -24,6 +24,9 @@ pub struct CardDecryptor<'a, 'app> { /// The matching public key for the card's decryption key public: PublicKey, + + /// Callback function to signal if touch confirmation is needed + touch_prompt: &'a (dyn Fn() + Send + Sync), } impl<'a, 'app> CardDecryptor<'a, 'app> { @@ -34,6 +37,7 @@ impl<'a, 'app> CardDecryptor<'a, 'app> { pub fn new( ca: &'a mut OpenPgpTransaction<'app>, cert: &Cert, + touch_prompt: &'a (dyn Fn() + Send + Sync), ) -> Result, Error> { // Get the fingerprint for the decryption key from the card. let ard = ca.application_related_data()?; @@ -46,7 +50,11 @@ impl<'a, 'app> CardDecryptor<'a, 'app> { if let Some(eka) = sq_util::get_subkey_by_fingerprint(cert, &fp)? { let public = eka.key().clone(); - Ok(Self { ca, public }) + Ok(Self { + ca, + public, + touch_prompt, + }) } else { Err(Error::InternalError(format!( "Failed to find (sub)key {} in cert", @@ -71,6 +79,18 @@ impl<'a, 'app> crypto::Decryptor for CardDecryptor<'a, 'app> { ciphertext: &mpi::Ciphertext, _plaintext_len: Option, ) -> 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_dec()? { + uif.touch_policy().touch_required() + } else { + false + }; + // Delegate a decryption operation to the OpenPGP card. // // This fn prepares the data structures that openpgp-card needs to @@ -80,6 +100,11 @@ impl<'a, 'app> crypto::Decryptor for CardDecryptor<'a, 'app> { match (ciphertext, self.public.mpis()) { (mpi::Ciphertext::RSA { c: ct }, mpi::PublicKey::RSA { .. }) => { let dm = Cryptogram::RSA(ct.value()); + + if touch_required { + (self.touch_prompt)(); + } + let dec = self.ca.decipher(dm)?; let sk = openpgp::crypto::SessionKey::from(&dec[..]); @@ -100,6 +125,10 @@ impl<'a, 'app> crypto::Decryptor for CardDecryptor<'a, 'app> { Cryptogram::ECDH(e.value()) }; + if touch_required { + (self.touch_prompt)(); + } + // Decryption operation on the card let mut dec = self.ca.decipher(dm)?; diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index f4146b8..58a6bf3 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -73,7 +73,7 @@ //! # let (cert, _) = //! # CertBuilder::general_purpose(None, Some("alice@example.org")) //! # .generate()?; -//! let decryptor = user.decryptor(&cert); +//! let decryptor = user.decryptor(&cert, &|| { println!("Touch confirmation needed for decryption") }); //! //! // Perform decryption operation(s) //! // .. diff --git a/openpgp-card-sequoia/src/util.rs b/openpgp-card-sequoia/src/util.rs index 32024e0..dc42255 100644 --- a/openpgp-card-sequoia/src/util.rs +++ b/openpgp-card-sequoia/src/util.rs @@ -404,13 +404,14 @@ pub fn decrypt( card_tx: &'_ mut OpenPgpTransaction<'_>, cert: &Cert, msg: Vec, + touch_prompt: &(dyn Fn() + Send + Sync), p: &dyn Policy, ) -> Result> { let mut decrypted = Vec::new(); { let reader = io::BufReader::new(&msg[..]); - let d = decryptor::CardDecryptor::new(card_tx, cert)?; + let d = decryptor::CardDecryptor::new(card_tx, cert, touch_prompt)?; let db = DecryptorBuilder::from_reader(reader)?; let mut decryptor = db.with_policy(p, None, d)?; diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 2afcecc..88cc7dc 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -892,7 +892,9 @@ fn decrypt( let user_pin = util::get_pin(&mut open, pin_file, ENTER_USER_PIN); let mut user = util::verify_to_user(&mut open, user_pin.as_deref())?; - let d = user.decryptor(&cert)?; + let d = user.decryptor(&cert, &|| { + println!("Touch confirmation needed for decryption") + })?; let db = DecryptorBuilder::from_reader(input)?; let mut decryptor = db.with_policy(&p, None, d)?;