Add callback Fn for touch confirmation prompt for signing operations.

This commit is contained in:
Heiko Schaefer 2022-05-31 00:31:46 +02:00
parent ccba7c7e9f
commit 079cc32427
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
8 changed files with 67 additions and 21 deletions

View file

@ -95,7 +95,7 @@ pub fn test_sign(
let cert = Cert::from_str(param[0])?; let cert = Cert::from_str(param[0])?;
let msg = "Hello world, I am signed."; 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 // validate sig
assert!(util::verify_sig(&cert, msg.as_bytes(), sig.as_bytes())?); assert!(util::verify_sig(&cert, msg.as_bytes(), sig.as_bytes())?);
@ -266,6 +266,7 @@ pub fn test_keygen(
Some(key_aut), Some(key_aut),
Some(b"123456"), Some(b"123456"),
&|| {}, &|| {},
&|| {},
)?; )?;
let armored = String::from_utf8(cert.armored().to_vec()?)?; let armored = String::from_utf8(cert.armored().to_vec()?)?;

View file

@ -34,7 +34,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut sign = open.signing_card().unwrap(); let mut sign = open.signing_card().unwrap();
let cert = Cert::from_file(cert_file)?; 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(); let stdout = std::io::stdout();

View file

@ -196,7 +196,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let text = "Hello world, I am signed."; 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())?; let sig = sq_util::sign_helper(signer, &mut text.as_bytes())?;
println!("Signature from card:\n{}", sig) println!("Signature from card:\n{}", sig)

View file

@ -352,18 +352,26 @@ pub struct Sign<'app, 'open> {
} }
impl<'app, 'open> Sign<'app, 'open> { impl<'app, 'open> Sign<'app, 'open> {
pub fn signer(&mut self, cert: &Cert) -> std::result::Result<CardSigner<'_, 'app>, Error> { pub fn signer(
&mut self,
cert: &Cert,
touch_prompt: &'open (dyn Fn() + Send + Sync),
) -> std::result::Result<CardSigner<'_, 'app>, Error> {
// FIXME: depending on the setting in "PW1 Status byte", only one // FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing // 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 // FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing // 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) /// Generate Attestation (Yubico)

View file

@ -116,7 +116,7 @@
//! # let (cert, _) = //! # let (cert, _) =
//! # CertBuilder::general_purpose(None, Some("alice@example.org")) //! # CertBuilder::general_purpose(None, Some("alice@example.org"))
//! # .generate()?; //! # .generate()?;
//! let signer = user.signer(&cert); //! let signer = user.signer(&cert, &|| println!("Touch confirmation needed for signing"));
//! //!
//! // Perform signing operation(s) //! // Perform signing operation(s)
//! // .. //! // ..

View file

@ -22,6 +22,9 @@ pub struct CardSigner<'a, 'app> {
/// The matching public key for the card's signing key /// The matching public key for the card's signing key
public: PublicKey, public: PublicKey,
/// Callback function to signal if touch confirmation is needed
touch_prompt: &'a (dyn Fn() + Send + Sync),
} }
impl<'a, 'app> CardSigner<'a, 'app> { 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 /// An Error is returned if no match between the card's signing
/// key and a (sub)key of `cert` can be made. /// key and a (sub)key of `cert` can be made.
pub fn new( pub(crate) fn new(
ca: &'a mut OpenPgpTransaction<'app>, ca: &'a mut OpenPgpTransaction<'app>,
cert: &openpgp::Cert, cert: &openpgp::Cert,
touch_prompt: &'a (dyn Fn() + Send + Sync),
) -> Result<CardSigner<'a, 'app>, Error> { ) -> Result<CardSigner<'a, 'app>, Error> {
// Get the fingerprint for the signing key from the card. // Get the fingerprint for the signing key from the card.
let ard = ca.application_related_data()?; 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)? { if let Some(eka) = sq_util::get_subkey_by_fingerprint(cert, &fp)? {
let key = eka.key().clone(); let key = eka.key().clone();
Ok(Self::with_pubkey(ca, key)) Ok(Self::with_pubkey(ca, key, touch_prompt))
} else { } else {
Err(Error::InternalError(format!( Err(Error::InternalError(format!(
"Failed to find (sub)key {} in cert", "Failed to find (sub)key {} in cert",
@ -61,8 +65,13 @@ impl<'a, 'app> CardSigner<'a, 'app> {
pub(crate) fn with_pubkey( pub(crate) fn with_pubkey(
ca: &'a mut OpenPgpTransaction<'app>, ca: &'a mut OpenPgpTransaction<'app>,
public: PublicKey, public: PublicKey,
touch_prompt: &'a (dyn Fn() + Send + Sync),
) -> CardSigner<'a, 'app> { ) -> 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, hash_algo: openpgp::types::HashAlgorithm,
digest: &[u8], digest: &[u8],
) -> openpgp::Result<mpi::Signature> { ) -> openpgp::Result<mpi::Signature> {
// 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. // Delegate a signing operation to the OpenPGP card.
// //
// This fn prepares the data structures that openpgp-card needs to // 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 sig = self.ca.signature_for_hash(hash)?;
let mpi = mpi::MPI::new(&sig[..]); let mpi = mpi::MPI::new(&sig[..]);
@ -119,6 +144,10 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> {
(PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => { (PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => {
let hash = Hash::EdDSA(digest); let hash = Hash::EdDSA(digest);
if touch_required {
(self.touch_prompt)();
}
let sig = self.ca.signature_for_hash(hash)?; let sig = self.ca.signature_for_hash(hash)?;
let r = mpi::MPI::new(&sig[..32]); let r = mpi::MPI::new(&sig[..32]);
@ -134,6 +163,10 @@ impl<'a, 'app> crypto::Signer for CardSigner<'a, 'app> {
_ => Hash::ECDSA(digest), _ => Hash::ECDSA(digest),
}; };
if touch_required {
(self.touch_prompt)();
}
let sig = self.ca.signature_for_hash(hash)?; let sig = self.ca.signature_for_hash(hash)?;
let len_2 = sig.len() / 2; let len_2 = sig.len() / 2;

View file

@ -49,7 +49,8 @@ pub fn make_cert<'app>(
key_dec: Option<PublicKey>, key_dec: Option<PublicKey>,
key_aut: Option<PublicKey>, key_aut: Option<PublicKey>,
pw1: Option<&[u8]>, pw1: Option<&[u8]>,
prompt: &dyn Fn(), pinpad_prompt: &dyn Fn(),
touch_prompt: &(dyn Fn() + Send + Sync),
) -> Result<Cert> { ) -> Result<Cert> {
let mut pp = vec![]; let mut pp = vec![];
@ -80,11 +81,11 @@ pub fn make_cert<'app>(
if let Some(pw1) = pw1 { if let Some(pw1) = pw1 {
open.verify_user_for_signing(pw1)?; open.verify_user_for_signing(pw1)?;
} else { } else {
open.verify_user_for_signing_pinpad(prompt)?; open.verify_user_for_signing_pinpad(pinpad_prompt)?;
} }
if let Some(mut sign) = open.signing_card() { if let Some(mut sign) = open.signing_card() {
// Card-backed signer for bindings // 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 let signing_bsig: Packet = sub_dec
.bind(&mut card_signer, &cert, signing_builder)? .bind(&mut card_signer, &cert, signing_builder)?
@ -111,11 +112,11 @@ pub fn make_cert<'app>(
if let Some(pw1) = pw1 { if let Some(pw1) = pw1 {
open.verify_user_for_signing(pw1)?; open.verify_user_for_signing(pw1)?;
} else { } else {
open.verify_user_for_signing_pinpad(prompt)?; open.verify_user_for_signing_pinpad(pinpad_prompt)?;
} }
if let Some(mut sign) = open.signing_card() { if let Some(mut sign) = open.signing_card() {
// Card-backed signer for bindings // 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 // Temporary version of the cert
let cert = Cert::try_from(pp.clone())?; let cert = Cert::try_from(pp.clone())?;
@ -153,12 +154,12 @@ pub fn make_cert<'app>(
if let Some(pw1) = pw1 { if let Some(pw1) = pw1 {
open.verify_user_for_signing(pw1)?; open.verify_user_for_signing(pw1)?;
} else { } else {
open.verify_user_for_signing_pinpad(prompt)?; open.verify_user_for_signing_pinpad(pinpad_prompt)?;
} }
if let Some(mut sign) = open.signing_card() { if let Some(mut sign) = open.signing_card() {
// Card-backed signer for bindings // 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 // Temporary version of the cert
let cert = Cert::try_from(pp.clone())?; let cert = Cert::try_from(pp.clone())?;
@ -378,10 +379,11 @@ pub fn sign(
card_tx: &'_ mut OpenPgpTransaction<'_>, card_tx: &'_ mut OpenPgpTransaction<'_>,
cert: &Cert, cert: &Cert,
input: &mut dyn io::Read, input: &mut dyn io::Read,
touch_prompt: &(dyn Fn() + Send + Sync),
) -> Result<String> { ) -> Result<String> {
let mut armorer = armor::Writer::new(vec![], armor::Kind::Signature)?; 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 message = Message::new(&mut armorer);
let mut message = Signer::new(message, s).detached().build()?; let mut message = Signer::new(message, s).detached().build()?;

View file

@ -920,7 +920,7 @@ fn sign_detached(
let user_pin = util::get_pin(&mut open, pin_file, ENTER_USER_PIN); 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 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 message = Armorer::new(Message::new(std::io::stdout())).build()?;
let mut signer = Signer::new(message, s).detached().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( fn generate_keys(