Fix panic on response larger than interchanges::SIZE or response::SIZE

This commit is contained in:
Sosthène Guédon
2022-08-22 13:43:52 +02:00
committed by Nicolas Stalder
parent e30785029e
commit d1b8498ba3
2 changed files with 77 additions and 6 deletions
+14 -6
View File
@@ -22,6 +22,14 @@ use iso7816::{
command::FromSliceError,
};
/// Maximum length of a data field of a response that can fit in an interchange message after
/// concatenation of SW1SW2
const MAX_INTERCHANGE_DATA: usize = if interchanges::SIZE < ResponseSize {
interchanges::SIZE
} else {
ResponseSize
} - 2;
pub use iso7816::Interface;
pub enum RequestType {
@@ -270,7 +278,7 @@ impl ApduDispatch
// If the reader is using chaining, we will simply
// reply 61XX, and put the response in a buffer.
// It is up to the reader to then send GetResponse
// requests, to which we will return up to 256 bytes at a time.
// requests, to which we will return up to `Le` bytes at a time.
let (new_state, response) = match &mut self.buffer.raw {
RawApduBuffer::Request(_) | RawApduBuffer::None => {
info!("Unexpected GetResponse request.");
@@ -281,10 +289,10 @@ impl ApduDispatch
}
RawApduBuffer::Response(res) => {
if self.was_request_chained || res.len() > interchanges::SIZE {
if self.was_request_chained || res.len() > MAX_INTERCHANGE_DATA {
// Do not send more than the expected bytes
let boundary = core::cmp::min(self.response_len_expected, res.len());
let boundary = self.response_len_expected.min(res.len()).min(MAX_INTERCHANGE_DATA);
let to_send = &res[..boundary];
let remaining = &res[boundary..];
@@ -298,7 +306,7 @@ impl ApduDispatch
// Last chunk has success code
0x9000
};
message.extend_from_slice(&return_code.to_be_bytes()).ok();
message.extend_from_slice(&return_code.to_be_bytes()).expect("Failed add to status bytes");
if return_code == 0x9000 {
(
RawApduBuffer::None,
@@ -314,8 +322,8 @@ impl ApduDispatch
} else {
// Add success code
res.extend_from_slice(&[0x90,00]).ok();
(RawApduBuffer::None, interchanges::Data::from_slice(res.as_slice()).unwrap())
res.extend_from_slice(&[0x90,00]).expect("Failed to add the status bytes");
(RawApduBuffer::None, interchanges::Data::from_slice(&res.as_slice()).unwrap())
}
}
+63
View File
@@ -86,6 +86,24 @@ impl App< {apdu_dispatch::command::SIZE}, {apdu_dispatch::response::SIZE},> for
reply.extend_from_slice(&addr.to_be_bytes()).unwrap();
Ok(())
}
// Testing a response larger than the interchange's size
0x21 => {
reply.extend_from_slice(&[10; interchanges::SIZE+1]).unwrap();
Ok(())
}
0x22 => {
reply.extend_from_slice(&[10; interchanges::SIZE-2]).unwrap();
Ok(())
}
0x23 => {
reply.extend_from_slice(&[10; interchanges::SIZE-1]).unwrap();
Ok(())
}
0x24 => {
reply.extend_from_slice(&[10; interchanges::SIZE]).unwrap();
Ok(())
}
_ =>
Err(Status::InstructionNotSupportedOrInvalid)
}
@@ -1029,6 +1047,51 @@ fn send_select_preceded_with_zero_chained_data(){
)
}
#[test]
#[serial]
fn response_larger_than_interchange(){
// Sending a select after chaining 0 bytes should result in successful select operation
let mut response1 = vec![0x0A; interchanges::SIZE-2];
response1.extend_from_slice(&hex!("6103"));
let mut response2 = vec![0x0A; interchanges::SIZE-2];
response2.extend_from_slice(&hex!("9000"));
let mut response3 = vec![0x0A; interchanges::SIZE-2];
response3.extend_from_slice(&hex!("6101"));
let mut response4 = vec![0x0A; interchanges::SIZE-2];
response4.extend_from_slice(&hex!("6102"));
run_apdus(
&[
// Select 1
&hex!("00A40400 05 0A01000001"),
&hex!("9000"),
&hex!("00210000 00ffff"),
&response1,
// Get Response
&hex!("00C00000 00"),
&hex!("0A0A0A 9000"),
&hex!("00220000 00ffff"),
&response2,
&hex!("00230000 00ffff"),
&response3,
// Get Response
&hex!("00C00000 00"),
&hex!("0A 9000"),
&hex!("00240000 00ffff"),
&response4,
// Get Response
&hex!("00C00000 00"),
&hex!("0A0A 9000"),
]
)
}
#[test]