Construct and return immutable instances of APDU instead of mutating one

This commit is contained in:
Kamran Khan
2020-12-02 23:29:11 -08:00
parent 0420ad8de6
commit 1d8c103d9b

View File

@@ -123,48 +123,56 @@ impl TryFrom<&[u8]> for APDU {
// +-----+-----+----+----+ // +-----+-----+----+----+
let (header, payload) = frame.split_at(APDU_HEADER_LEN); let (header, payload) = frame.split_at(APDU_HEADER_LEN);
let mut apdu = APDU {
header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
lc: 0x00,
data: Vec::new(),
le: 0x00,
case_type: ApduType::default(),
};
if payload.is_empty() { if payload.is_empty() {
// Lc is zero-bytes in length // Lc is zero-bytes in length
apdu.case_type = ApduType::Instruction; return Ok(APDU {
header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
lc: 0x00,
data: Vec::new(),
le: 0x00,
case_type: ApduType::Instruction,
});
} else { } else {
// Lc is not zero-bytes in length, let's figure out how long it is // Lc is not zero-bytes in length, let's figure out how long it is
let byte_0 = payload[0]; let byte_0 = payload[0];
if payload.len() == 1 { if payload.len() == 1 {
// There is only one byte in the payload, that byte cannot be Lc because that would // 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) // entail at *least* one another byte in the payload (for the command data)
apdu.case_type = ApduType::Short(Case::Le1); return Ok(APDU {
apdu.le = if byte_0 == 0x00 { header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
// Ne = 256 lc: 0x00,
0x100 data: Vec::new(),
} else { le: if byte_0 == 0x00 {
byte_0.into() // Ne = 256
} 0x100
} else {
byte_0.into()
},
case_type: ApduType::Short(Case::Le1),
});
} }
if payload.len() == (1 + byte_0) as usize && byte_0 != 0 { 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 // 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 // payload there's no Le at the end
apdu.case_type = ApduType::Short(Case::Lc1Data); return Ok(APDU {
apdu.lc = byte_0.into(); header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
apdu.data = payload[1..].to_vec(); lc: byte_0.into(),
data: payload[1..].to_vec(),
case_type: ApduType::Short(Case::Lc1Data),
le: 0,
});
} }
if payload.len() == (1 + byte_0 + 1) as usize && byte_0 != 0 { 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 // 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 // payload with ONE additional byte that byte must be Le
apdu.case_type = ApduType::Short(Case::Lc1DataLe1); let last_byte: u32 = (*payload.last().unwrap()).into();
apdu.lc = byte_0.into(); return Ok(APDU {
apdu.data = payload[1..(payload.len() - 1)].to_vec(); header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
apdu.le = (*payload.last().unwrap()).into(); lc: byte_0.into(),
if apdu.le == 0x00 { data: payload[1..(payload.len() - 1)].to_vec(),
apdu.le = 0x100; le: if last_byte == 0x00 { 0x100 } else { last_byte },
} case_type: ApduType::Short(Case::Lc1DataLe1),
});
} }
if payload.len() > 2 { if payload.len() > 2 {
// Lc is possibly three-bytes long // Lc is possibly three-bytes long
@@ -178,30 +186,40 @@ impl TryFrom<&[u8]> for APDU {
// If first byte is zero AND the next two bytes can be parsed as a big-endian // 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 // length that covers the rest of the block (plus few additional bytes for Le), we
// have an extended-length APDU // have an extended-length APDU
apdu.case_type = ApduType::Extended(match extended_apdu_le_len { let last_byte: u32 = (*payload.last().unwrap()).into();
0 => Case::Lc3Data, return Ok(APDU {
1 => Case::Lc3DataLe1, header: array_ref!(header, 0, APDU_HEADER_LEN).into(),
2 => Case::Lc3DataLe2, lc: extended_apdu_lc as u16,
3 => Case::Lc3DataLe3, data: payload[3..(payload.len() - extended_apdu_le_len)].to_vec(),
_ => Case::Unknown, le: match extended_apdu_le_len {
0 => 0,
1 => {
if last_byte == 0x00 {
0x100
} else {
last_byte
}
}
2 => BigEndian::read_u16(
&payload[payload.len() - extended_apdu_le_len..],
) as u32,
3 => BigEndian::read_u32(
&payload[payload.len() - extended_apdu_le_len..],
),
_ => 0,
},
case_type: ApduType::Extended(match extended_apdu_le_len {
0 => Case::Lc3Data,
1 => Case::Lc3DataLe1,
2 => Case::Lc3DataLe2,
3 => Case::Le3,
_ => Case::Unknown,
}),
}); });
apdu.data = payload[3..(payload.len() - extended_apdu_le_len)].to_vec();
apdu.lc = extended_apdu_lc as u16;
apdu.le = match extended_apdu_le_len {
0 => 0,
1 => (*payload.last().unwrap()).into(),
2 => BigEndian::read_u16(&payload[payload.len() - extended_apdu_le_len..])
as u32,
3 => BigEndian::read_u32(&payload[payload.len() - extended_apdu_le_len..]),
_ => 0,
}
} }
} }
} }
if apdu.case_type == ApduType::default() { return Err(ApduStatusCode::SW_COND_USE_NOT_SATISFIED);
return Err(ApduStatusCode::SW_COND_USE_NOT_SATISFIED);
}
Ok(apdu)
} }
} }