diff --git a/Cargo.toml b/Cargo.toml index 4923746..5407efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ documentation = "https://docs.rs/iso7816" [dependencies] delog = "0.1.2" heapless = "0.7" +heapless-bytes = { version = "0.3.0", optional = true } [features] std = [] diff --git a/fuzz/fuzz_targets/fuzz_target_1.rs b/fuzz/fuzz_targets/fuzz_target_1.rs index d9850ca..f12348a 100644 --- a/fuzz/fuzz_targets/fuzz_target_1.rs +++ b/fuzz/fuzz_targets/fuzz_target_1.rs @@ -5,7 +5,37 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; use iso7816::command::{class, Command, CommandBuilder, CommandView}; +use std::convert::Infallible; use std::iter::repeat; +use std::ops::Deref; + +struct WriteMock { + buffer: [u8; 4096], + written: usize, + capacity: usize, +} + +impl Deref for WriteMock { + type Target = [u8]; + fn deref(&self) -> &[u8] { + &self.buffer[..self.written] + } +} + +impl iso7816::command::Writer for WriteMock { + type Error = Infallible; + fn write(&mut self, data: &[u8]) -> Result { + let available = self.capacity - self.written; + let written = available.min(data.len()); + self.buffer[self.written..][..written].copy_from_slice(data); + self.written += written; + Ok(written) + } + + fn remaining_len(&self) -> usize { + self.capacity - self.written + } +} #[derive(Debug, Arbitrary)] struct Input<'a> { @@ -47,12 +77,20 @@ fuzz_target!(|data: Input| { command.clone().serialize_to_vec(); } - let buffer = &mut [0; 4096][..buf_len.min(4096).max(128)]; + let mut buffer = WriteMock { + buffer: [0; 4096], + written: 0, + capacity: buf_len.min(4096).max(128), + }; - match command.clone().serialize_into(buffer, supports_extended) { - Ok(len) => { - // dbg!(&buffer[..len][..len]); - let view = CommandView::try_from(&buffer[..len]).unwrap(); + match command + .clone() + .serialize_into(&mut buffer, supports_extended) + .unwrap() + { + Ok(()) => { + // dbg!(&*buffer, buffer.len()); + let view = CommandView::try_from(&*buffer).unwrap(); if !supports_extended { assert!(view.data().len() <= 256); assert!(!view.extended()); @@ -63,8 +101,10 @@ fuzz_target!(|data: Input| { assert_eq!(view, command); } } - Err((len, mut rem)) => { - // dbg!(&buffer[..len]); + + Err(mut rem) => { + let len = buffer.len(); + // dbg!(&*buffer, buffer.len()); let mut parsed = Command::<4096>::try_from(&buffer[..len]).unwrap(); if !supports_extended { assert!(parsed.data().len() <= 255); @@ -72,11 +112,15 @@ fuzz_target!(|data: Input| { } // Loop with arbitrary buflens forever for buflen in repeat(buf_lens.iter().chain([&128])).flatten() { - let buffer = &mut [0; 4096][..(*buflen).min(4096).max(128)]; - match rem.serialize_into(buffer, supports_extended) { - Ok(len) => { - // dbg!(&buffer[..len]); - let view = CommandView::try_from(&buffer[..len]).unwrap(); + let mut buffer = WriteMock { + buffer: [0; 4096], + written: 0, + capacity: (*buflen).min(4096).max(128), + }; + match rem.serialize_into(&mut buffer, supports_extended).unwrap() { + Ok(()) => { + // dbg!(&*buffer, buffer.len()); + let view = CommandView::try_from(&*buffer).unwrap(); if !supports_extended { assert!(view.data().len() <= 255); assert!(!view.extended()); @@ -92,11 +136,10 @@ fuzz_target!(|data: Input| { } return; } - Err((len, new_rem)) => { - // dbg!(&buffer[..len]); + Err(new_rem) => { rem = new_rem; - let view = CommandView::try_from(&buffer[..len]).unwrap(); + let view = CommandView::try_from(&*buffer).unwrap(); if !supports_extended { assert!(view.data().len() <= 255); assert!(!view.extended()); diff --git a/src/command.rs b/src/command.rs index d623a9c..0c5d53a 100644 --- a/src/command.rs +++ b/src/command.rs @@ -4,6 +4,9 @@ pub mod class; pub mod instruction; pub use instruction::Instruction; +mod writer; +pub use writer::Writer; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Command { class: class::Class, @@ -247,27 +250,22 @@ impl<'a> CommandBuilder<'a> { #[cfg(any(feature = "std", test))] pub fn serialize_to_vec(self) -> Vec { let required_len = self.required_len(); - let mut buffer = vec![0; required_len]; - assert_eq!( - self.serialize_into(&mut buffer, true).unwrap(), - required_len, - "internal error, serialization should fill the buffer" - ); + let mut buffer = Vec::with_capacity(required_len); + self.serialize_into(&mut buffer, true).unwrap().unwrap(); buffer } - /// Serialize the command into the given buffer and return the length of data returned to the buffer. - /// - /// If the command does not fit in the buffer, fill the buffer with data and return another command - /// to be send containing the remaining data through command chaining - pub fn serialize_into( + /// - `Ok(Ok(()))` means that the command was successfully written + /// - `Ok(Err(command))` means that the writer ran out of space and that the command needed + /// - `Err(err)` means that there was an error from the writer + pub fn serialize_into( self, - buf: &mut [u8], + writer: &mut W, supports_extended_length: bool, - ) -> Result { + ) -> Result, W::Error> { const HEADER_LEN: usize = 4; - if buf.len() < HEADER_LEN { - return Err((0, self)); + if writer.remaining_len() < HEADER_LEN { + return Ok(Err(self)); } let BuildingHeaderData { @@ -280,15 +278,14 @@ impl<'a> CommandBuilder<'a> { if !supports_extended_length { max_data_len = 255; } - let rem = &buf[HEADER_LEN..]; - let available_data_len = rem - .len() + + let available_data_len = (writer.remaining_len() - HEADER_LEN) .saturating_sub(data_len.len() + expected_data_len.len()) .min(max_data_len); if available_data_len < self.data.len() { if available_data_len == 0 { // Let's not support this case - return Err((0, self)); + return Ok(Err(self)); } let (send_now, send_later) = self.data.split_at(available_data_len); @@ -310,22 +307,22 @@ impl<'a> CommandBuilder<'a> { le, }; // We know that the comman has enough space to be properly serialized - let sent = send_now - .serialize_into(buf, supports_extended_length) + send_now + .serialize_into(writer, supports_extended_length)? .unwrap(); - return Err((sent, send_later)); + return Ok(Err(send_later)); } - buf[0] = self.class.into_inner(); - buf[1] = self.instruction.into(); - buf[2] = self.p1; - buf[3] = self.p2; + writer.write_all(&[ + self.class.into_inner(), + self.instruction.into(), + self.p1, + self.p2, + ])?; - let rem = &mut buf[HEADER_LEN..]; - rem[..data_len.len()].copy_from_slice(&data_len); - rem[data_len.len()..][..self.data.len()].copy_from_slice(self.data); - rem[data_len.len() + self.data.len()..][..expected_data_len.len()] - .copy_from_slice(&expected_data_len); - Ok(HEADER_LEN + data_len.len() + self.data.len() + expected_data_len.len()) + writer.write_all(&data_len)?; + writer.write_all(self.data)?; + writer.write_all(&expected_data_len)?; + Ok(Ok(())) } } @@ -718,36 +715,52 @@ mod test { fn building_chained() { let cla = 0x00.try_into().unwrap(); let ins = 0x01.into(); - let mut buffer = [0; 4096]; + let mut buffer = heapless::Vec::::new(); let command = CommandBuilder::new(cla, ins, 2, 3, &[], 0xFFFF); - let len = command.clone().serialize_into(&mut buffer, true).unwrap(); - assert_eq!(&buffer[..len], &command.clone().serialize_to_vec()); + command + .clone() + .serialize_into(&mut buffer, true) + .unwrap() + .unwrap(); + assert_eq!(&*buffer, &command.clone().serialize_to_vec()); + buffer.clear(); // without extended length - let len = command.clone().serialize_into(&mut buffer, false).unwrap(); + command + .clone() + .serialize_into(&mut buffer, false) + .unwrap() + .unwrap(); assert_eq!( - &buffer[..len], + &*buffer, &CommandBuilder::new(cla, ins, 2, 3, &[], 0x0100).serialize_to_vec() ); + buffer.clear(); // without extended length let command = CommandBuilder::new(cla, ins, 2, 3, &[], 0); - let len = command.clone().serialize_into(&mut buffer, false).unwrap(); + command + .clone() + .serialize_into(&mut buffer, false) + .unwrap() + .unwrap(); assert_eq!( - &buffer[..len], + &*buffer, &CommandBuilder::new(cla, ins, 2, 3, &[], 0).serialize_to_vec() ); + buffer.clear(); - buffer = [0; 4096]; + let mut buffer = heapless::Vec::::new(); let command = CommandBuilder::new(cla, ins, 2, 3, &[5; 200], 0); - let (len, rem) = command - .serialize_into(&mut buffer[..105], false) + let rem = command + .serialize_into(&mut buffer, false) + .unwrap() .unwrap_err(); - assert_eq!(len, 105); + assert_eq!(buffer.len(), 105); assert_eq!(rem, CommandBuilder::new(cla, ins, 2, 3, &[5; 100], 0)); assert_eq!( - &buffer[..len], + &*buffer, &CommandBuilder::new(cla.as_chained(), ins, 2, 3, &[5; 100], 0).serialize_to_vec() ); } diff --git a/src/command/writer.rs b/src/command/writer.rs new file mode 100644 index 0000000..5b828ac --- /dev/null +++ b/src/command/writer.rs @@ -0,0 +1,76 @@ +use core::convert::Infallible; +use core::fmt::{Debug, Display}; +use core::mem::replace; + +pub trait Writer { + type Error: Debug + Display; + + fn write(&mut self, data: &[u8]) -> Result; + + fn remaining_len(&self) -> usize; + + /// data must be smaller than [`remaining_len`](Writer::remaining_len) + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + debug_assert!(data.len() <= self.remaining_len()); + let mut offset = 0; + while offset < data.len() { + offset += self.write(data)?; + } + Ok(()) + } +} + +impl<'a> Writer for &'a mut [u8] { + type Error = Infallible; + fn write(&mut self, data: &[u8]) -> Result { + let amt = data.len().min(self.len()); + let (a, b) = replace(self, &mut []).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + fn remaining_len(&self) -> usize { + self.len() + } +} + +impl Writer for heapless::Vec { + type Error = Infallible; + fn write(&mut self, data: &[u8]) -> Result { + let written_len = data.len().min(self.capacity() - self.len()); + self.extend_from_slice(&data[..written_len]).unwrap(); + Ok(written_len) + } + + fn remaining_len(&self) -> usize { + self.capacity() - self.len() + } +} + +#[cfg(feature = "heapless_bytes")] +impl Writer for heapless_bytes::Bytes { + type Error = Infallible; + fn write(&mut self, data: &[u8]) -> Result { + let written_len = data.len().min(self.capacity() - self.len()); + self.extend_from_slice(&data[..written_len]).unwrap(); + Ok(written_len) + } + + fn remaining_len(&self) -> usize { + self.capacity() - self.len() + } +} + +#[cfg(any(feature = "std", test))] +impl Writer for Vec { + type Error = Infallible; + fn write(&mut self, data: &[u8]) -> Result { + self.extend_from_slice(data); + Ok(data.len()) + } + + fn remaining_len(&self) -> usize { + usize::MAX + } +}