Add callback Fn for touch confirmation prompt for signing operations.
This commit is contained in:
parent
ccba7c7e9f
commit
079cc32427
8 changed files with 67 additions and 21 deletions
|
@ -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()?)?;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
//! // ..
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue