Construct and return immutable instances of APDU instead of mutating one
This commit is contained in:
110
src/ctap/apdu.rs
110
src/ctap/apdu.rs
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user