use super::transport::{connect_reader_mode, RESET_RETRIES, SELECT_PIV};
use anyhow::{bail, Result};
fn query_card_apdu(reader: &str, query: &[u8]) -> Result<Vec<u8>> {
let ctx = pcsc::Context::establish(pcsc::Scope::User)?;
let mut buf = vec![0u8; 258];
let mut attempts = 0;
let card = loop {
let card = connect_reader_mode(&ctx, reader, pcsc::ShareMode::Shared)?;
match card.transmit(SELECT_PIV, &mut buf) {
Ok(_) => break card,
Err(pcsc::Error::ResetCard | pcsc::Error::ProtoMismatch)
if attempts < RESET_RETRIES =>
{
attempts += 1;
}
Err(e) => return Err(e.into()),
}
};
let resp = card.transmit(query, &mut buf)?.to_vec();
let _ = card.disconnect(pcsc::Disposition::LeaveCard);
Ok(resp)
}
pub(crate) fn serial_from_reader(reader: &str) -> Result<u32> {
let resp = query_card_apdu(reader, &[0x00, 0xF8, 0x00, 0x00, 0x00])?;
if resp.len() < 4 {
bail!("GET_SERIAL response too short");
}
Ok(u32::from_be_bytes(resp[0..4].try_into().unwrap()))
}
pub(crate) fn version_from_reader(reader: &str) -> Option<String> {
let resp = query_card_apdu(reader, &[0x00, 0xFD, 0x00, 0x00, 0x00]).ok()?;
if resp.len() < 3 {
return None;
}
Some(format!("{}.{}.{}", resp[0], resp[1], resp[2]))
}