-
Notifications
You must be signed in to change notification settings - Fork 172
Open
Description
Currently apdu, iso7816, yubikey.rs and emrtd reimplement the same C-APDU logic, but limited short/extended distinction.
Notably, apdu-core does not allow to create extended C-APDU explicitly, therefore it can't send extended read-binary C-APDU.
I think the best solution would be to start with an enum:
/// Command APDU
pub enum CAPDU<D> {
/// Short-form C-APDU
Short(CAPDUshort<D>),
/// Extended-form C-APDU
Extended(CAPDUextended<D>),
}where the D parameter would be der-like object with Encode/Decode traits (or ApduEncode/ApduDecode).
Therefore:
iso7816::CommandbecomesCAPDU<heapless::Vec<u8>>iso7816::CommandView<'a>becomesCAPDU<&[u8]>.
[Spoiler] Example code of `CAPDUshort` / `CAPDUextended`
use std::num::NonZero;
/// Command header
#[derive(Debug, Copy, Clone)]
pub struct Header {
pub cla: u8,
pub ins: u8,
pub p1: u8,
pub p2: u8,
}
/// Command APDU, short form
#[derive(Clone, Debug)]
pub struct CAPDUshort<D> {
// CLA, INS, P1, P2
pub header: Header,
/// Either:
/// - None: No Lc is encoded. No data.
/// - Some(data): Short-form Lc will be encoded before data.
pub data: Option<D>,
/// Expected length
///
/// special value: max length of 256 is coded as [0x00]
pub le: Option<ExpectedLenShort>,
}
/// Command APDU, extended form
#[derive(Clone, Debug)]
pub struct CAPDUextended<D> {
// CLA, INS, P1, P2
pub header: Header,
/// Either:
/// - None: No Lc is encoded. No data.
/// - Some(data): Short-form Lc will be encoded before data.
pub data: Option<D>,
/// Expected length
///
/// special value: max length of 65536 is coded as [0x00, 0x00]
pub le: Option<ExpectedLenExtended>,
}
/// Le
///
/// special value: max length of 256 is coded as [0x00]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ExpectedLenShort {
/// 1..=255
Short(NonZero<u8>),
/// [0x00] means max length of 256
ShortMax,
}
/// Le
///
/// special value: max length of 65536 is coded as [0x00, 0x00]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ExpectedLenExtended {
/// 1..=65535
Extended(NonZero<u16>),
/// [0x00, 0x00] means max length of 65536
ExtendedMax,
}
impl ApduEncode for ExpectedLenShort {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
Ok(der::Length::new(1))
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
let value = match self {
ExpectedLenShort::Short(v) => v.get(),
ExpectedLenShort::ShortMax => 0x00,
};
encoder.write_byte(value)
}
}
impl ApduEncode for ExpectedLenExtended {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
Ok(der::Length::new(2))
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
let value = match self {
ExpectedLenExtended::Extended(v) => u16::to_le_bytes(v.get()),
ExpectedLenExtended::ExtendedMax => [0x00, 0x00],
};
encoder.write(&value)
}
}
impl<D: ApduEncode> ApduEncode for CAPDU<D> {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
match self {
CAPDU::Short(capdu) => capdu.apdu_encoded_len(),
CAPDU::Extended(capdu) => capdu.apdu_encoded_len(),
}
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
match self {
CAPDU::Short(capdu) => capdu.apdu_encode(encoder),
CAPDU::Extended(capdu) => capdu.apdu_encode(encoder),
}
}
}
impl<D: ApduEncode> ApduEncode for CAPDUshort<D> {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
// Header
let mut len = der::Length::new(4);
if let Some(data) = &self.data {
// Lc (1 byte)
len = (len + 1u8)?;
// data
len = (len + data.apdu_encoded_len()?)?;
}
if let Some(le) = self.le {
// Le (1 byte)
len = (len + le.apdu_encoded_len()?)?;
}
Ok(len)
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
self.header.apdu_encode(encoder)?;
if let Some(data) = &self.data {
let data_len = u32::from(data.apdu_encoded_len()?);
let lc: u8 = data_len.try_into()?;
// Write Lc
encoder.write_byte(lc)?;
data.apdu_encode(encoder)?;
}
if let Some(le) = self.le {
le.apdu_encode(encoder)?;
}
Ok(())
}
}
impl<D: ApduEncode> ApduEncode for CAPDUextended<D> {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
// Header
let mut len = der::Length::new(4);
if let Some(data) = &self.data {
// Lc (3 bytes)
len = (len + 3u8)?;
// data
len = (len + data.apdu_encoded_len()?)?;
}
if let Some(le) = self.le {
if self.data.is_some() {
// special case: '00' byte before Le
len = (len + 1u8)?;
}
// Le (2 bytes)
len = (len + le.apdu_encoded_len()?)?;
}
Ok(len)
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
self.header.apdu_encode(encoder)?;
if let Some(data) = &self.data {
let data_len = u32::from(data.apdu_encoded_len()?);
let lc: u16 = data_len.try_into()?;
// Write extended Lc
encoder.write_byte(0u8)?;
encoder.write_byte((lc >> 8) as u8)?;
encoder.write_byte((lc & 0xFF) as u8)?;
data.apdu_encode(encoder)?;
}
if let Some(le) = self.le {
if self.data.is_some() {
encoder.write_byte(0)?;
}
le.apdu_encode(encoder)?;
}
Ok(())
}
}
impl ApduEncode for Header {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
Ok(der::Length::new(4))
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
encoder.write(&[self.cla, self.ins, self.p1, self.p2])?;
Ok(())
}
}
/// APDU encoding trait, similar to [`der::Encode`]. Distinct in order to prevent TLV header mismatch.
pub trait ApduEncode {
/// Compute the length of this APDU part in bytes.
fn apdu_encoded_len(&self) -> der::Result<der::Length>;
/// Encode this APDU part using the provided [`Writer`].
fn apdu_encode(&self, writer: &mut impl der::Writer) -> der::Result<()>;
/// Encode this APDU part to the provided byte slice, returning a sub-slice
/// containing the encoded message.
fn encode_to_slice<'a>(&self, buf: &'a mut [u8]) -> der::Result<&'a [u8]> {
let mut writer = der::SliceWriter::new(buf);
self.apdu_encode(&mut writer)?;
writer.finish()
}
}[Spoiler] Proxy `der::Encode` to `ApduEncode`
/// Useful with DER-TLV structure that is [`der::EncodeValue`], for example:
/// - `CAPDU<WithoutTagLength<SecureMessagingCommand>>`
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct WithoutTagLength<V>(pub V);
impl<V: der::EncodeValue> ApduEncode for WithoutTagLength<V> {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
self.0.value_len()
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
self.0.encode_value(encoder)
}
}
/// Always returns error [`der::ErrorKind::Overlength`].
///
/// Useful with read-binary, for example:
/// - `CAPDU<NeverData>`
#[derive(Debug)]
pub struct NeverData;
impl ApduEncode for NeverData {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
Ok(0u8.into())
}
fn apdu_encode(&self, _encoder: &mut impl der::Writer) -> der::Result<()> {
Err(der::ErrorKind::Overlength.into())
}
}
/// Useful with update-binary C-APDU, for example:
/// - `CAPDU<RawBytesRef>`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RawBytesRef<'a> {
pub bytes: &'a [u8],
}
impl<'a> ApduEncode for RawBytesRef<'a> {
fn apdu_encoded_len(&self) -> der::Result<der::Length> {
self.bytes.len().try_into()
}
fn apdu_encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
encoder.write(&self.bytes)
}
}
impl<'a> From<&'a [u8]> for RawBytesRef<'a> {
fn from(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels