Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

ISO7816-4: apdu der-like Encode/Decode crate

Aperta
#2,211 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Ferma
Stack tecnologico
rust
Ambito
cryptography

Direzione di ricerca

Inizia confrontando le implementazioni APDU referenziate nei link apdu, iso7816, yubikey.rs ed emrtd, quindi esamina la limitazione di apdu-core nella creazione di C-APDU estese. Il lavoro è completato quando la codifica e la decodifica di CAPDU brevi ed estese sono definite e integrate con le interfacce proposte in stile ApduEncode/ApduDecode.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Currently apdu, iso7816, yubikey.rs and emrtd reimplement the same C-APDU logic, but with 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::Command becomes CAPDU<heapless::Vec<u8>>
  • iso7816::CommandView<'a> becomes CAPDU<&[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 }
    }
}
Lingua principale
Rust
Stelle
341
Fork
193
Merge medio
1g 12h
PR unite (30g)
19

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di RustCrypto/formats

Tutte le issue di RustCrypto/formats

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.