use openpgp_card::ocard::Transaction;
use openpgp_card::state::Open;
use openpgp_card_rpgp::CardSlot;
use pgp::types::{PublicKeyTrait, PublicParams};
use sop::plumbing::PasswordsAreHumanReadable;
pub fn card_by_pp(
pp: &PublicParams,
kt: openpgp_card::ocard::KeyType,
) -> Result<Option<openpgp_card::Card<Open>>, crate::Error> {
let Ok(backends) = card_backend_pcsc::PcscBackend::cards(None) else {
return Ok(None);
};
for b in backends.filter_map(|c| c.ok()) {
if let Ok(mut card) = openpgp_card::Card::<Open>::new(b) {
let mut found = false;
{
if let Ok(mut tx) = card.transaction() {
let cs = CardSlot::init_from_card(&mut tx, kt, &|| {})?;
if cs.public_key().public_params() == pp {
found = true;
}
}
}
if found {
return Ok(Some(card));
}
}
}
Ok(None)
}
pub(crate) fn do_on_card<W, T>(
pp: &PublicParams,
slot: openpgp_card::ocard::KeyType,
sop_pw: &[sop::Password],
touch_prompt: &(dyn Fn() + Send + Sync),
work: W,
) -> Result<T, crate::Error>
where
W: FnOnce(&mut CardSlot) -> Result<T, crate::Error>,
{
let Some(mut card) = card_by_pp(pp, slot).expect("FIXME") else {
eprintln!("No card found.");
return Err(crate::Error::Message("No card found.".to_string()));
};
let mut tx = card.transaction().expect("FIXME");
if verify_pin_from_card_state(tx.card(), slot == openpgp_card::ocard::KeyType::Signing).is_ok()
{
} else if !sop_pw.is_empty() {
assert_eq!(sop_pw.len(), 1, "Expecting exactly one User PIN");
let res = verify_pin(
tx.card(),
sop_pw[0].normalized(),
slot == openpgp_card::ocard::KeyType::Signing,
);
if let Err(e) = res {
eprintln!("Invalid User Pin!");
return Err(e.into());
}
} else {
return Err(crate::Error::Message(
"No PIN found for this card.".to_string(),
));
}
let mut cs = CardSlot::init_from_card(&mut tx, slot, touch_prompt)?;
work(&mut cs)
}
pub fn verify_pin(tx: &mut Transaction, pin: &[u8], sign: bool) -> Result<(), rpgpie::Error> {
let verify = match sign {
true => Transaction::verify_pw1_sign,
false => Transaction::verify_pw1_user,
};
verify(tx, pin.to_vec().into())
.map_err(|_| rpgpie::Error::Message("User PIN verification failed.".to_string()))
}
#[cfg(feature = "openpgp-card-state")]
pub fn verify_pin_from_card_state(tx: &mut Transaction, sign: bool) -> Result<(), rpgpie::Error> {
let ard = tx.application_related_data().expect("FIXME");
let ident = ard.application_id().expect("FIXME").ident();
if let Ok(Some(pin)) = openpgp_card_state::get_pin(&ident) {
if verify_pin(tx, pin.as_bytes(), sign).is_err() {
let res = openpgp_card_state::drop_pin(&ident);
if res.is_ok() {
eprintln!(
"ERROR: The stored User PIN for OpenPGP card '{}' seems wrong or blocked! Dropped it from storage.",
&ident);
} else {
eprintln!(
"ERROR: The stored User PIN for OpenPGP card '{}' seems wrong or blocked! In addition, dropping it from storage failed.",
&ident);
}
return Err(rpgpie::Error::Message(
"User PIN verification failed.".to_string(),
));
}
} else {
return Err(rpgpie::Error::Message(
"No User PIN configured.".to_string(),
));
}
Ok(())
}
#[cfg(not(feature = "openpgp-card-state"))]
pub fn verify_pin_from_card_state(_tx: &mut Transaction, _sign: bool) -> Result<(), rpgpie::Error> {
Err(rpgpie::Error::Message(String::default()))
}