Repository: google/OpenSK
File: src/ctap/apdu.rs (Rust)

Code before:
        if payload.is_empty() {
            // Lc is zero-bytes in length
            apdu.case_type = ApduType::Instruction;
        } else {
            // Lc is not zero-bytes in length, let's figure out how long it is
            let byte_0 = payload[0];
            if payload.len() == 1 {
                // There is only one byte in the payload, that byte cannot be Lc because that would
                // entail at *least* one another byte in the payload (for the command data)
                apdu.case_type = ApduType::Short(Case::Le1);
                apdu.le = if byte_0 == 0x00 {
                    // Ne = 256
                    0x100
                } else {
                    byte_0.into()
                }
            }
            if payload.len() == (1 + byte_0) as usize && byte_0 != 0 {
                // Lc is one-byte long and since the size specified by Lc covers the rest of the
                // payload there's no Le at the end
                apdu.case_type = ApduType::Short(Case::Lc1Data);
                apdu.lc = byte_0.into();
                apdu.data = payload[1..].to_vec();
            }
            if payload.len() == (1 + byte_0 + 1) as usize && byte_0 != 0 {
                // Lc is one-byte long and since the size specified by Lc covers the rest of the
                // payload with ONE additional byte that byte must be Le
                apdu.case_type = ApduType::Short(Case::Lc1DataLe1);
                apdu.lc = byte_0.into();
                apdu.data = payload[1..(payload.len() - 1)].to_vec();
                apdu.le = (*payload.last().unwrap()).into();
                if apdu.le == 0x00 {
                    apdu.le = 0x100;
                }
            }
            if payload.len() > 2 {
                // Lc is possibly three-bytes long
                let extended_apdu_lc: usize = BigEndian::read_u16(&payload[1..]) as usize;
                let extended_apdu_le_len: usize = if payload.len() > extended_apdu_lc {
                    payload.len() - extended_apdu_lc - 3
                } else {
                    0
                };
                if byte_0 == 0 && extended_apdu_le_len <= 3 {
                    // If first byte is zero AND the next two bytes can be parsed as a big-endian
                    // length that covers the rest of the block (plus few additional bytes for Le), we
                    // have an extended-length APDU
                    apdu.case_type = ApduType::Extended(match extended_apdu_le_len {
                        0 => Case::Lc3Data,
                        1 => Case::Lc3DataLe1,
                        2 => Case::Lc3DataLe2,

Review comment (suggestion):
According to the [ISO7816-4 spec](http://www.unsads.com/specs/ISO/7816/ISO7816-4.pdf) page 7, it seems it's a bit easier actually. Either `Lc` is 1 byte between `01` and `FF` or it's 3-bytes long and the first byte must be set to `00`.
Same thing goes for `Le`:
- 1 byte between `01` and `FF`
- 2 bytes if `Lc` is present
- 3 bytes if `Lc` is absent

The behavior of considering `Lc` set to `00` as 256 seems to be legacy, before they introduced the extended encoding.

Code after:
                header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
                lc: 0x00,
                data: Vec::new(),
                le: if byte_0 == 0x00 {
                    // Ne = 256
                    0x100
                } else {
                    byte_0.into()
                },
                case_type: ApduType::Short(Case::Le1),
            });
        }
        if payload.len() == 1 + (byte_0 as usize) && byte_0 != 0 {
            // Lc is one-byte long and since the size specified by Lc covers the rest of the
            // payload there's no Le at the end
            return Ok(APDU {
                header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
                lc: byte_0.into(),
                data: payload[1..].to_vec(),
                case_type: ApduType::Short(Case::Lc1Data),
                le: 0,
            });
        }
        if payload.len() == 2 + (byte_0 as usize) && byte_0 != 0 {
            // Lc is one-byte long and since the size specified by Lc covers the rest of the
            // payload with ONE additional byte that byte must be Le
            let last_byte: u32 = (*payload.last().unwrap()).into();
            return Ok(APDU {
                header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
                lc: byte_0.into(),
                data: payload[1..(payload.len() - 1)].to_vec(),
                le: if last_byte == 0x00 { 0x100 } else { last_byte },
                case_type: ApduType::Short(Case::Lc1DataLe1),
            });
        }
        if payload.len() > 2 {
            // Lc is possibly three-bytes long
            let extended_apdu_lc: usize = BigEndian::read_u16(&payload[1..]) as usize;
            let extended_apdu_le_len: usize = if payload.len() > extended_apdu_lc {
                payload.len() - extended_apdu_lc - 3
            } else {
                0
            };
            if byte_0 == 0 && extended_apdu_le_len <= 3 {
                // If first byte is zero AND the next two bytes can be parsed as a big-endian
                // length that covers the rest of the block (plus few additional bytes for Le), we
                // have an extended-length APDU
                let last_byte: u32 = (*payload.last().unwrap()).into();
                return Ok(APDU {
                    header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
                    lc: extended_apdu_lc as u16,