use heapless::Vec;
use crate::backend::{Scp02Backend, Scp03Backend};
use crate::command::select::select_by_aid;
use crate::error::ScllError;
use crate::limits::RAPDU_MAX;
use crate::scp::ScpSession;
use crate::tlv::{self, Tlv};
use crate::transport::{Transport, TransportError};
pub(crate) const SW_OK: u16 = 0x9000;
pub(crate) const SW_REF_NOT_FOUND: u16 = 0x6A88;
pub(crate) const SW_FILE_NOT_FOUND: u16 = 0x6A82;
pub(crate) const SW_WRONG_DATA: u16 = 0x6A80;
pub(crate) const SW_CONDITIONS: u16 = 0x6985;
pub(crate) const SW_MORE_DATA: u16 = 0x6310;
pub(crate) fn map_transport(err: &TransportError) -> ScllError {
match err {
TransportError::CardRemoved => ScllError::CardRemoved,
TransportError::ReaderGone => ScllError::ReaderGone,
TransportError::Timeout => ScllError::Timeout,
TransportError::ProtocolError | TransportError::Other(_) => ScllError::TransportUnavailable,
}
}
pub(crate) fn split_sw(rapdu: &[u8]) -> Result<(Vec<u8, RAPDU_MAX>, u16), ScllError> {
let n = rapdu.len();
if n < 2 {
return Err(ScllError::Card { sw: 0x0000 });
}
let sw = u16::from_be_bytes([rapdu[n - 2], rapdu[n - 1]]);
let mut data = Vec::new();
data.extend_from_slice(&rapdu[..n - 2])
.map_err(|()| ScllError::Card { sw })?;
Ok((data, sw))
}
pub(crate) fn transmit_plain(
t: &mut dyn Transport,
capdu: &[u8],
) -> Result<(Vec<u8, RAPDU_MAX>, u16), ScllError> {
let rapdu = t.transmit(capdu).map_err(|e| map_transport(&e))?;
split_sw(&rapdu)
}
pub(crate) fn select(t: &mut dyn Transport, aid: &[u8]) -> Result<Vec<u8, RAPDU_MAX>, ScllError> {
let capdu = select_by_aid(aid)?;
let (data, sw) = transmit_plain(t, &capdu)?;
match sw {
SW_OK => Ok(data),
SW_FILE_NOT_FOUND => Err(ScllError::CardNotUsable),
other => Err(ScllError::from_general_sw(other)),
}
}
const TAG_FCI: u32 = 0x6F;
const TAG_DF_NAME: u32 = 0x84;
pub(crate) fn fci_df_name(fci: &[u8]) -> Result<Option<&[u8]>, ScllError> {
if fci.is_empty() {
return Ok(None);
}
let top = tlv::parse(fci)?;
if let Some(inner) = find(&top, TAG_FCI) {
let parsed = tlv::parse(inner)?;
return Ok(find(&parsed, TAG_DF_NAME));
}
Ok(find(&top, TAG_DF_NAME))
}
fn find<'a>(tlvs: &[Tlv<'a>], tag: u32) -> Option<&'a [u8]> {
tlvs.iter().find(|t| t.tag == tag).map(|t| t.value)
}
pub(crate) fn transmit_in_session<B>(
t: &mut dyn Transport,
backend: &B,
session: &mut ScpSession,
capdu_plaintext: &[u8],
) -> Result<(Vec<u8, RAPDU_MAX>, u16), ScllError>
where
B: Scp02Backend + Scp03Backend,
{
let wrapped = match session {
ScpSession::Scp03(s) => s.wrap_command(backend, capdu_plaintext)?,
ScpSession::Scp02(s) => s.wrap_command(backend, capdu_plaintext)?,
};
let rapdu = t.transmit(&wrapped).map_err(|e| map_transport(&e))?;
let unwrapped = match session {
ScpSession::Scp03(s) => s.unwrap_response(backend, &rapdu)?,
ScpSession::Scp02(s) => s.unwrap_response(backend, &rapdu)?,
};
split_sw(&unwrapped)
}
#[cfg(test)]
mod tests {
use super::fci_df_name;
const FULL_AID: &[u8] = &[0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C];
const PARTIAL_AID: &[u8] = &[0xA0, 0x00, 0x00, 0x00, 0x62];
fn fci_wrapped(aid: &[u8]) -> std::vec::Vec<u8> {
let la = u8::try_from(aid.len()).expect("AID fits u8");
let mut v = std::vec::Vec::new();
v.extend_from_slice(&[0x6F, la + 2, 0x84, la]);
v.extend_from_slice(aid);
v
}
#[test]
fn wrapped_6f_84_yields_full_aid_even_for_partial_select() {
let fci = fci_wrapped(FULL_AID);
assert_eq!(fci_df_name(&fci).unwrap(), Some(FULL_AID));
assert_ne!(fci_df_name(&fci).unwrap(), Some(PARTIAL_AID));
}
#[test]
fn bare_84_without_outer_6f_resolves() {
let la = u8::try_from(FULL_AID.len()).expect("AID fits u8");
let mut bare = std::vec::Vec::new();
bare.extend_from_slice(&[0x84, la]);
bare.extend_from_slice(FULL_AID);
assert_eq!(fci_df_name(&bare).unwrap(), Some(FULL_AID));
}
#[test]
fn tag_84_among_siblings_inside_6f() {
let la = u8::try_from(FULL_AID.len()).expect("AID fits u8");
let inner_len = 2 + FULL_AID.len() + 4; let mut fci = std::vec::Vec::new();
fci.extend_from_slice(&[0x6F, u8::try_from(inner_len).unwrap(), 0x84, la]);
fci.extend_from_slice(FULL_AID);
fci.extend_from_slice(&[0x9F, 0x70, 0x01, 0x07]);
assert_eq!(fci_df_name(&fci).unwrap(), Some(FULL_AID));
}
#[test]
fn fci_without_tag_84_is_none() {
let fci = [0x6F, 0x03, 0x85, 0x01, 0xFF];
assert_eq!(fci_df_name(&fci).unwrap(), None);
}
#[test]
fn empty_fci_is_none() {
assert_eq!(fci_df_name(&[]).unwrap(), None);
}
#[test]
fn malformed_fci_is_an_error() {
let bad = [0x6F, 0x05, 0x84, 0x10, 0x00];
assert!(fci_df_name(&bad).is_err());
}
}