webauthn-authenticator-rs 0.4.9

Webauthn Authenticator Client Library
Documentation
//! ISO/IEC 7816-4 APDUs, used by CTAP v2 NFC tokens, and all CTAP v1/U2F
//! tokens.

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// [`ISO7816RequestAPDU.to_bytes()`]: `data` was too long for the given
    /// length form.
    DataTooLong,
    /// [`ISO7816RequestAPDU.to_bytes()`]: `ne` was too long for the given
    /// length form.
    NeTooLong,
    /// [`push_length_value()`]: The given value cannot be represented in short
    /// form.
    IntegerOverflow,
    /// [`ISO7816ResponseAPDU.from_bytes()`]: response was less than 2 bytes.
    ResponseTooShort,
}

/// The L<sub>c</sub> and L<sub>e</sub> form to use for [ISO7816RequestAPDU],
/// per ISO/IEC 7816-4:2005 §5.1.
///
/// All smartcards **must** support short form, and communication in extended
/// form may only be used if
/// [declared in the ATR][crate::nfc::Atr::extended_lc]. Smartcards which do not
/// support extended form must use [ISO7816LengthForm::ShortOnly].
///
/// FIDO tokens on non-NFC transports are **not required** to support short
/// form, and **must** support extended form on **all** transports – so must use
/// [ISO7816LengthForm::ExtendedOnly] for CTAP v1 / U2F commands.
///
/// FIDO tokens on NFC transports are required to support **both** short and
/// extended form.
///
/// Truth table:
///
/// Spec           | Short Form    | Extended Form
/// -------------- | ------------- | ------------------
/// ISO 7816       | required      | optional
/// FIDO / BTLE    | not supported | required (CTAP v1)
/// FIDO / NFC     | required      | required
/// FIDO / USB-HID | not supported | required (CTAP v1)
pub enum ISO7816LengthForm {
    /// Only use short form (1 byte length field). This limits
    /// [`ISO7816RequestAPDU::data`] to 255 bytes, and
    /// [`ISO7816RequestAPDU::ne`] to 256 bytes.
    ///
    /// This mode is always supported by smart cards, but may not be supported
    /// by non-NFC FIDO tokens (in CTAPv1 / U2F mode).
    ShortOnly,
    /// Automatically use extended form (3 bytes length field), if the request
    /// requires it, otherwise use short form (1 byte length field). This
    /// reduces the size of short messages.
    ///
    /// This mode is recommended only for NFC FIDO tokens. This may not be
    /// supported by non-NFC FIDO tokens (in CTAPv1 / U2F mode).
    Extended,
    /// Always use extended form (3 bytes length field), even if the request is
    /// short enough to not require it. This increases the size of short
    /// messages.
    ///
    /// This is needed to communicate with non-NFC FIDO tokens in CTAPv1 mode.
    /// This may not be supported by non-FIDO smartcards.
    ExtendedOnly,
}

/// ISO/IEC 7816-4 command APDU.
#[derive(Debug, Clone)]
pub struct ISO7816RequestAPDU {
    /// Class byte (`CLA`, ISO/IEC 7816-4:2005 §5.1.1).
    pub cla: u8,
    /// Instruction byte (`INS`, ISO/IEC 7816-4:2005 §5.1.2).
    pub ins: u8,
    /// Parameter byte 1 (`P1`).
    pub p1: u8,
    /// Parameter byte 2 (`P2`).
    pub p2: u8,

    /// Optional command data, up to 255 bytes in short form, or up to 65535
    /// bytes in extended form.
    ///
    /// Extended form can only be used with smartcards which
    /// [declare support for it in the ATR][crate::nfc::Atr::extended_lc].
    pub data: Vec<u8>,

    /// The maximum expected response length from the card (N<sub>e</sub>), in
    /// bytes, up to 256 bytes in short form, or 65535 bytes in extended form.
    ///
    /// Extended form can only be used with smartcards which
    /// [declare support for it in the ATR][crate::nfc::Atr::extended_lc].
    pub ne: usize,
}

/// Pushes a length `value` of `form` bytes to a mutable buffer, optionally
/// using ISO/IEC 7816-4 extended form:
///
/// * `form = 0`: only `value == 0`
/// * `form = 1`: for short form; `value > 0 && value <= 256`
/// * `form = 2`: for extended form with no 0-prefix (N<sub>e</sub> when
///   N<sub>c</sub> > 0)
/// * `form = 3`: for extended form with 0 prefix
///
/// # Errors
///
/// Returns [`Error::IntegerOverflow`] when `form` is inappropriate for the
/// `value`, or unknown `form`.
fn push_length_value(buf: &mut Vec<u8>, value: usize, form: u8) -> Result<(), Error> {
    if form >= 4 {
        return Err(Error::IntegerOverflow);
    } else if form == 0 {
        if value > 0 {
            return Err(Error::IntegerOverflow);
        }
        // do nothing
    } else if value == 0 || value > 65536 {
        return Err(Error::IntegerOverflow);
    } else if form >= 2 {
        if form == 3 {
            // uint16be prefixed with 0
            buf.push(0);
        }
        buf.extend_from_slice(&(value as u16).to_be_bytes());
    } else if value > 256 {
        return Err(Error::IntegerOverflow);
    } else {
        // 256 = 0x00, 1 = 0x01, 255 = 0xFF
        buf.push((value & 0xff) as u8);
    }
    Ok(())
}

impl ISO7816RequestAPDU {
    /// Serializes a request APDU into bytes to send to the card.
    pub fn to_bytes(&self, form: &ISO7816LengthForm) -> Result<Vec<u8>, Error> {
        let extended_form = match form {
            ISO7816LengthForm::Extended => self.ne > 256 || self.data.len() > 255,
            ISO7816LengthForm::ShortOnly => false,
            ISO7816LengthForm::ExtendedOnly => true,
        };

        if extended_form && self.data.len() > 65535 {
            return Err(Error::DataTooLong);
        } else if !extended_form {
            if self.data.len() > 255 {
                return Err(Error::DataTooLong);
            }
            if self.ne > 256 {
                return Err(Error::NeTooLong);
            }
        }

        // §5.1: "short and extended length fields shall not be combined: either
        // both of them are short, or both of them are extended".
        let lc_len: u8 = if self.data.is_empty() {
            0
        } else if extended_form {
            3
        } else {
            1
        };

        let le_len: u8 = if self.ne == 0 {
            0
        } else if extended_form {
            if lc_len == 0 {
                // §5.1: extended Le prefixed with 0 if Lc field is absent
                3
            } else {
                2
            }
        } else {
            1
        };

        let mut buf = Vec::with_capacity(4 + self.data.len() + lc_len as usize + le_len as usize);
        buf.push(self.cla);
        buf.push(self.ins);
        buf.push(self.p1);
        buf.push(self.p2);

        push_length_value(&mut buf, self.data.len(), lc_len)?;
        if !self.data.is_empty() {
            buf.extend_from_slice(&self.data);
        }
        push_length_value(&mut buf, self.ne, le_len)?;

        Ok(buf)
    }
}

/// ISO/IEC 7816-4 response APDU.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ISO7816ResponseAPDU {
    /// Response data field.
    pub data: Vec<u8>,
    /// Status byte 1 (`SW1`, ISO/IEC 7816-4:2005 §5.1.3).
    pub sw1: u8,
    /// Status byte 2 (`SW2`, ISO/IEC 7816-4:2005 §5.1.3).
    pub sw2: u8,
}

impl TryFrom<&[u8]> for ISO7816ResponseAPDU {
    type Error = self::Error;

    /// Attempts to deserialize a ISO/IEC 7816-4 response APDU.
    fn try_from(raw: &[u8]) -> Result<Self, Error> {
        if raw.len() < 2 {
            Err(Error::ResponseTooShort)
        } else {
            Ok(Self {
                data: raw[..raw.len() - 2].to_vec(),
                sw1: raw[raw.len() - 2],
                sw2: raw[raw.len() - 1],
            })
        }
    }
}

impl ISO7816ResponseAPDU {
    /// True if the response from the card was a simple "OK" (`90 00`).
    pub fn is_ok(&self) -> bool {
        self.sw1 == 0x90 && self.sw2 == 0x00
    }

    /// Non-zero if the card responded that further data bytes are available
    /// (`61 XX`), which can be retrieved with [`get_response()`].
    pub fn bytes_available(&self) -> usize {
        if self.sw1 == 0x61 {
            if self.sw2 == 0x00 {
                256
            } else {
                self.sw2.into()
            }
        } else {
            0
        }
    }

    /// **CTAP proprietary**: `true` if the card expects a `NFCCTAP_GETRESPONSE`
    /// command to get the actual response (`91 00`).
    pub fn ctap_needs_get_response(&self) -> bool {
        self.sw1 == 0x91 && self.sw2 == 0x00
    }

    /// `true` if the card returned a success-like response ([`Self::is_ok()`],
    /// [`Self::bytes_available()`] > 0, or
    /// [`Self::ctap_needs_get_response()`]).
    pub fn is_success(&self) -> bool {
        self.is_ok() || self.bytes_available() > 0 || self.ctap_needs_get_response()
    }
}

/// Selects an application by DF (directory file) name, of up to 16 bytes.
///
/// Reference: ISO/IEC 7816-4:2005 §5.3.1, §7.1.1.
pub fn select_by_df_name(df: &[u8]) -> ISO7816RequestAPDU {
    ISO7816RequestAPDU {
        cla: 0x00,
        ins: 0xA4, // SELECT
        p1: 0x04,  // By DF name
        p2: 0x00,  // First or only occurrence
        data: df.to_vec(),
        ne: 256,
    }
}

/// Requests a chunked response from the previous command that was too long for
/// the previous [`ISO7816RequestAPDU::ne`].
///
/// Reference: ISO/IEC 7816-4:2005 §7.6.1
pub fn get_response(cla: u8, ne: usize) -> ISO7816RequestAPDU {
    ISO7816RequestAPDU {
        cla,
        ins: 0xC0, // GET RESPONSE
        p1: 0x00,
        p2: 0x00,
        data: vec![],
        ne,
    }
}

pub const EMPTY_RESPONSE: ISO7816ResponseAPDU = ISO7816ResponseAPDU {
    data: vec![],
    sw1: 0,
    sw2: 0,
};

#[cfg(test)]
mod tests {
    use super::*;

    // Copy of crate::nfc::APPLET_DF, so that the tests don't require NFC.
    const APPLET_DF: [u8; 8] = [0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01];

    macro_rules! length_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, len, expected): (usize, u8, &[u8]) = $value;
                let mut b = Vec::with_capacity(expected.len());
                assert!(push_length_value(&mut b, input, len).is_ok());
                assert_eq!(expected, b);
            }
        )*
        }
    }

    macro_rules! length_errors {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, len): (usize, u8) = $value;
                let mut b = Vec::with_capacity(0);
                let r = push_length_value(&mut b, input, len);
                assert_eq!(Error::IntegerOverflow, r.unwrap_err());
                assert_eq!(0, b.len());
            }
        )*
        }
    }

    macro_rules! command_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, form, expected): (ISO7816RequestAPDU, ISO7816LengthForm, &[u8]) = $value;
                let b = input.to_bytes(&form).expect("serialisation error");
                assert_eq!(expected, b);
            }
        )*
        }
    }

    macro_rules! command_errors {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, form, expected): (ISO7816RequestAPDU, ISO7816LengthForm, Error) = $value;
                let err = input.to_bytes(&form).expect_err("expected error");
                assert_eq!(expected, err);
            }
        )*
        }
    }

    macro_rules! response_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, expected): (&[u8], ISO7816ResponseAPDU) = $value;
                let r = ISO7816ResponseAPDU::try_from(input).expect("deserialisation error");
                assert_eq!(expected, r);
            }
        )*
        }
    }

    macro_rules! response_errors {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let input: &[u8] = $value;
                let err = ISO7816ResponseAPDU::try_from(input).expect_err("expected error");
                assert_eq!(Error::ResponseTooShort, err);
            }
        )*
        }
    }

    length_tests! {
        length_0: (0, 0, &[]),
        length_1_short: (1, 1, &[0x01]),
        length_1_long: (1, 2, &[0x00, 0x01]),
        length_1_longer: (1, 3, &[0x00, 0x00, 0x01]),
        length_255_short: (255, 1, &[0xff]),
        length_255_long: (255, 2, &[0x00, 0xff]),
        length_255_longer: (255, 3, &[0x00, 0x00, 0xff]),
        length_256_short: (256, 1, &[0x00]),
        length_256_long: (256, 2, &[0x01, 0x00]),
        length_256_longer: (256, 3, &[0x00, 0x01, 0x00]),
        length_65535_long: (65535, 2, &[0xff, 0xff]),
        length_65535_longer: (65535, 3, &[0x00, 0xff, 0xff]),
        length_65536_long: (65536, 2, &[0x00, 0x00]),
        length_65536_longer: (65536, 3, &[0x00, 0x00, 0x00]),
    }

    length_errors! {
        length_0_short: (0, 1),
        length_0_long: (0, 2),
        length_0_longer: (0, 3),
        length_1_zero: (1, 0),
        length_1_bad_form: (1, 4),
        length_255_zero: (255, 0),
        length_257_zero: (257, 0),
        length_257_short: (257, 1),
        length_65535_short: (65535, 1),
        length_65536_short: (65536, 1),
        length_65537_short: (65537, 1),
        length_65537_long: (65537, 2),
        length_65537_longer: (65537, 3),
    }

    command_tests! {
        select_none_auto: (
            select_by_df_name(&[]),
            ISO7816LengthForm::Extended,
            &[0x00, 0xa4, 0x04, 0x00, 0x00]),
        select_none_short: (
            select_by_df_name(&[]),
            ISO7816LengthForm::ShortOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x00]),
        select_none_extended: (
            select_by_df_name(&[]),
            ISO7816LengthForm::ExtendedOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00]),

        select_u2f_applet_auto: (
            select_by_df_name(&APPLET_DF),
            ISO7816LengthForm::Extended,
            &[0x00, 0xa4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x00]),
        select_u2f_applet_short: (
            select_by_df_name(&APPLET_DF),
            ISO7816LengthForm::ShortOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x00]),
        select_u2f_applet_extended: (
            select_by_df_name(&APPLET_DF),
            ISO7816LengthForm::ExtendedOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x01, 0x00]),

        select_255_auto: (
            select_by_df_name(&[0xFF; 255]),
            ISO7816LengthForm::Extended,
            &[0x00, 0xa4, 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00],
        ),
        select_255_short: (
            select_by_df_name(&[0xFF; 255]),
            ISO7816LengthForm::ShortOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00],
        ),
        select_255_extended: (
            select_by_df_name(&[0xFF; 255]),
            ISO7816LengthForm::ExtendedOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
        ),
        select_256_auto: (
            select_by_df_name(&[0xFF; 256]),
            ISO7816LengthForm::Extended,
            &[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
        ),
        select_256_extended: (
            select_by_df_name(&[0xFF; 256]),
            ISO7816LengthForm::ExtendedOnly,
            &[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
        ),

        get_response_auto: (
            get_response(0x80, 256),
            ISO7816LengthForm::Extended,
            &[0x80, 0xc0, 0x00, 0x00, 0x00]),
        get_response_short: (
            get_response(0x80, 256),
            ISO7816LengthForm::ShortOnly,
            &[0x80, 0xc0, 0x00, 0x00, 0x00]),
        get_response_extended: (
            get_response(0x80, 65535),
            ISO7816LengthForm::ExtendedOnly,
            &[0x80, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff]),
        get_response_extended_auto: (
            get_response(0x80, 65535),
            ISO7816LengthForm::Extended,
            &[0x80, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff]),
    }

    command_errors! {
        get_response_long_short: (
            get_response(0x80, 65535),
            ISO7816LengthForm::ShortOnly,
            Error::NeTooLong,
        ),
        select_64k: (
            select_by_df_name(&[0xFF; 65536]),
            ISO7816LengthForm::Extended,
            Error::DataTooLong,
        ),
        select_64k_short: (
            select_by_df_name(&[0xFF; 65536]),
            ISO7816LengthForm::ShortOnly,
            Error::DataTooLong,
        ),
        select_64k_extended: (
            select_by_df_name(&[0xFF; 65536]),
            ISO7816LengthForm::ExtendedOnly,
            Error::DataTooLong,
        ),
        select_256_short: (
            select_by_df_name(&[0xFF; 256]),
            ISO7816LengthForm::ShortOnly,
            Error::DataTooLong,
        ),
    }

    response_tests! {
        response_ok: (
            &[0x90, 0x00],
            ISO7816ResponseAPDU { sw1: 0x90, sw2: 0x00, data: vec![] },
        ),
        response_data: (
            &[0x01, 0x02, 0x03, 0x90, 0x00],
            ISO7816ResponseAPDU { sw1: 0x90, sw2: 0x00, data: vec![0x01, 0x02, 0x03] },
        ),
    }

    #[test]
    fn response_attrs() {
        // OK
        let mut r = ISO7816ResponseAPDU {
            sw1: 0x90,
            sw2: 0x00,
            data: vec![],
        };
        assert!(r.is_ok());
        assert!(r.is_success());
        assert!(!r.ctap_needs_get_response());
        assert_eq!(0, r.bytes_available());

        // More bytes available
        r = ISO7816ResponseAPDU {
            sw1: 0x61,
            sw2: 0x01,
            data: vec![],
        };
        assert!(!r.is_ok());
        assert!(r.is_success());
        assert!(!r.ctap_needs_get_response());
        assert_eq!(1, r.bytes_available());

        r = ISO7816ResponseAPDU {
            sw1: 0x61,
            sw2: 0x00,
            data: vec![],
        };
        assert!(!r.is_ok());
        assert!(r.is_success());
        assert!(!r.ctap_needs_get_response());
        assert_eq!(256, r.bytes_available());

        // Needs NFCCTAP_GETRESPONSE
        r = ISO7816ResponseAPDU {
            sw1: 0x91,
            sw2: 0x00,
            data: vec![],
        };
        assert!(!r.is_ok());
        assert!(r.is_success());
        assert!(r.ctap_needs_get_response());
        assert_eq!(0, r.bytes_available());

        // Error
        r = ISO7816ResponseAPDU {
            sw1: 0x6A,
            sw2: 0x82,
            data: vec![],
        };
        assert!(!r.is_ok());
        assert!(!r.is_success());
        assert!(!r.ctap_needs_get_response());
        assert_eq!(0, r.bytes_available());
    }

    response_errors! {
        response_empty: &[],
        response_1_byte: &[0x90],
    }
}