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 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()?)?;

View file

@ -34,7 +34,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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();

View file

@ -196,7 +196,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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)

View file

@ -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<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
// 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)

View file

@ -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)
//! // ..

View file

@ -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<CardSigner<'a, 'app>, 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<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.
//
// 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;

View file

@ -49,7 +49,8 @@ pub fn make_cert<'app>(
key_dec: Option<PublicKey>,
key_aut: Option<PublicKey>,
pw1: Option<&[u8]>,
prompt: &dyn Fn(),
pinpad_prompt: &dyn Fn(),
touch_prompt: &(dyn Fn() + Send + Sync),
) -> Result<Cert> {
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<String> {
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()?;

View file

@ -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(