use card_backend_pcsc::PcscBackend;
use openpgp_card::Card;
use openpgp_card::ocard::{OpenPGP, data::UserInteractionFlag};
use secrecy::{SecretString, SecretVec};
use super::types::{CardError, CardInfo, KeySlot, TouchMode};
use crate::error::{Error, Result};
pub fn is_card_connected() -> bool {
match PcscBackend::cards(None) {
Ok(mut cards) => cards.next().is_some(),
Err(_) => false,
}
}
fn get_card_backend() -> Result<PcscBackend> {
let mut cards = PcscBackend::cards(None)
.map_err(|e| Error::Card(CardError::CommunicationError(e.to_string())))?;
cards.next()
.ok_or(Error::Card(CardError::NotConnected))?
.map_err(|e| Error::Card(CardError::CommunicationError(e.to_string())))
}
fn pin_to_secret(pin: &[u8]) -> Result<SecretString> {
let pin_str = std::str::from_utf8(pin)
.map_err(|_| Error::Card(CardError::InvalidData("PIN must be valid UTF-8".to_string())))?;
Ok(SecretString::new(pin_str.to_string()))
}
pub fn get_card_details() -> Result<CardInfo> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut info = CardInfo::default();
if let Ok(aid) = tx.application_identifier() {
info.serial_number = format!("{:08X}", aid.serial());
info.manufacturer = Some(format!("{:04X}", aid.manufacturer()));
}
if let Ok(fps) = tx.fingerprints() {
if let Some(fp) = fps.signature() {
info.signature_fingerprint = Some(hex::encode(fp.as_bytes()));
}
if let Some(fp) = fps.decryption() {
info.encryption_fingerprint = Some(hex::encode(fp.as_bytes()));
}
if let Some(fp) = fps.authentication() {
info.authentication_fingerprint = Some(hex::encode(fp.as_bytes()));
}
}
if let Ok(status) = tx.pw_status_bytes() {
info.pin_retry_counter = status.err_count_pw1();
info.reset_code_retry_counter = status.err_count_rc();
info.admin_pin_retry_counter = status.err_count_pw3();
}
if let Ok(name) = tx.cardholder_name() {
if !name.is_empty() {
info.cardholder_name = Some(name);
}
}
if let Ok(url) = tx.url() {
if !url.is_empty() {
info.public_key_url = Some(url);
}
}
Ok(info)
}
pub fn get_card_version() -> Result<String> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let aid = tx.application_identifier()
.map_err(|e| Error::Card(CardError::from(e)))?;
let version = aid.version();
let major = version >> 8;
let minor = version & 0xFF;
Ok(format!("{}.{}", major, minor))
}
pub fn get_card_serial() -> Result<String> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let aid = tx.application_identifier()
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(format!("{:08X}", aid.serial()))
}
pub fn verify_user_pin(pin: &[u8]) -> Result<bool> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let secret_pin = pin_to_secret(pin)?;
tx.verify_user_pin(secret_pin)
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(true)
}
pub fn verify_admin_pin(pin: &[u8]) -> Result<bool> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let secret_pin = pin_to_secret(pin)?;
tx.verify_admin_pin(secret_pin)
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(true)
}
pub fn get_pin_retry_counters() -> Result<(u8, u8, u8)> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let status = tx.pw_status_bytes()
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok((
status.err_count_pw1(),
status.err_count_rc(),
status.err_count_pw3(),
))
}
pub fn reset_card() -> Result<()> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
tx.factory_reset()
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(())
}
pub fn change_user_pin(old_pin: &[u8], new_pin: &[u8]) -> Result<()> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let old_secret = pin_to_secret(old_pin)?;
let new_secret = pin_to_secret(new_pin)?;
tx.change_user_pin(old_secret, new_secret)
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(())
}
pub fn change_admin_pin(old_pin: &[u8], new_pin: &[u8]) -> Result<()> {
let backend = get_card_backend()?;
let mut card = Card::new(backend)
.map_err(|e| Error::Card(CardError::from(e)))?;
let mut tx = card.transaction()
.map_err(|e| Error::Card(CardError::from(e)))?;
let old_secret = pin_to_secret(old_pin)?;
let new_secret = pin_to_secret(new_pin)?;
tx.change_admin_pin(old_secret, new_secret)
.map_err(|e| Error::Card(CardError::from(e)))?;
Ok(())
}
pub fn set_touch_mode(slot: KeySlot, mode: TouchMode, admin_pin: &[u8]) -> Result<()> {
let backend = get_card_backend()?;
let mut opgp = OpenPGP::new(backend)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
let mut tx = opgp.transaction()
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
let secret_pin = SecretVec::new(admin_pin.to_vec());
tx.verify_pw3(secret_pin)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
let policy_byte: u8 = match mode {
TouchMode::Off => 0x00,
TouchMode::On => 0x01,
TouchMode::Fixed => 0x02,
TouchMode::Cached => 0x03,
TouchMode::CachedFixed => 0x04,
};
let uif_bytes = vec![policy_byte, 0x20]; let uif = UserInteractionFlag::try_from(uif_bytes)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(format!("Failed to create UIF: {}", e))))?;
match slot {
KeySlot::Signature => {
tx.set_uif_pso_cds(&uif)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
}
KeySlot::Encryption => {
tx.set_uif_pso_dec(&uif)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
}
KeySlot::Authentication => {
tx.set_uif_pso_aut(&uif)
.map_err(|e: openpgp_card::Error| Error::Card(CardError::CardError(e.to_string())))?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
}