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 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()?)?;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
//! // ..
|
//! // ..
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()?;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue