use super::transport::PcscSession;
use crate::auxiliaries::parse_tlv_flat;
use crate::errors::CardOp;
use crate::piv::tlv::{encode_length, encode_tlv, parse_gen_key_response};
use anyhow::Result;
impl PcscSession {
fn general_authenticate(
&mut self,
slot: u8,
p1: u8,
inner_tag: u8,
payload: &[u8],
op: CardOp,
) -> Result<Vec<u8>> {
let label = op.describe();
let mut inner = vec![0x82, 0x00];
inner.extend(encode_tlv(inner_tag, payload));
let outer = encode_tlv(0x7C, &inner);
let mut apdu = vec![0x00, 0x87, p1, slot];
apdu.extend(encode_length(outer.len()));
apdu.extend(&outer);
apdu.push(0x00);
let resp = self.transmit_check(&apdu, op)?;
let outer_r = tlv_get(&resp, 0x7C, &label)?;
tlv_get(&outer_r, 0x82, &label)
}
pub(crate) fn general_authenticate_ecdh(
&mut self,
slot: u8,
peer_point: &[u8],
) -> Result<Vec<u8>> {
self.general_authenticate(slot, 0x11, 0x85, peer_point, CardOp::Ecdh(slot))
}
pub(crate) fn general_authenticate_sign(&mut self, slot: u8, digest: &[u8]) -> Result<Vec<u8>> {
self.general_authenticate(slot, 0x11, 0x81, digest, CardOp::Sign(slot))
}
pub(crate) fn general_authenticate_sign_raw(
&mut self,
slot: u8,
digest: &[u8],
) -> Result<[u8; 64]> {
let der = self.general_authenticate_sign(slot, digest)?;
der_ecdsa_to_raw(&der)
}
pub(crate) fn generate_key(&mut self, slot: u8) -> Result<Vec<u8>> {
let apdu = [
0x00, 0x47, 0x00, slot, 0x05, 0xAC, 0x03, 0x80, 0x01, 0x11, 0x00,
];
let resp = self.transmit_check(&apdu, CardOp::GenerateKey(slot))?;
parse_gen_key_response(&resp)
}
}
pub(crate) fn der_ecdsa_to_raw(der: &[u8]) -> Result<[u8; 64]> {
let (seq_body, _) = consume_tlv(der, 0x30, "ECDSA DER SEQUENCE")?;
let (r_bytes, rest) = consume_tlv(seq_body, 0x02, "ECDSA DER r")?;
let (s_bytes, _) = consume_tlv(rest, 0x02, "ECDSA DER s")?;
let mut out = [0u8; 64];
copy_integer_to_fixed(r_bytes, &mut out[0..32], "r")?;
copy_integer_to_fixed(s_bytes, &mut out[32..64], "s")?;
Ok(out)
}
pub(crate) fn tlv_get(buf: &[u8], tag: u8, label: &str) -> Result<Vec<u8>> {
parse_tlv_flat(buf)
.get(&tag)
.ok_or_else(|| anyhow::anyhow!("{label}: missing tag 0x{tag:02x}"))
.cloned()
}
fn consume_tlv<'a>(data: &'a [u8], expected_tag: u8, label: &str) -> Result<(&'a [u8], &'a [u8])> {
if data.len() < 2 {
anyhow::bail!("{label}: truncated TLV");
}
if data[0] != expected_tag {
anyhow::bail!(
"{label}: expected tag 0x{expected_tag:02x}, got 0x{:02x}",
data[0]
);
}
let len = data[1] as usize;
if data.len() < 2 + len {
anyhow::bail!(
"{label}: truncated TLV value (need {len}, have {})",
data.len() - 2
);
}
Ok((&data[2..2 + len], &data[2 + len..]))
}
fn copy_integer_to_fixed(src: &[u8], dst: &mut [u8], label: &str) -> Result<()> {
let src = if src.first() == Some(&0x00) {
&src[1..]
} else {
src
};
if src.len() > dst.len() {
anyhow::bail!("ECDSA integer {label} too long ({} bytes)", src.len());
}
let pad = dst.len() - src.len();
dst[..pad].fill(0);
dst[pad..].copy_from_slice(src);
Ok(())
}