diff --git a/src/dispatch.rs b/src/dispatch.rs index 42da85e..cc57c7c 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -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()) } } diff --git a/tests/dispatch.rs b/tests/dispatch.rs index b2550c0..550fb6a 100644 --- a/tests/dispatch.rs +++ b/tests/dispatch.rs @@ -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]