diff --git a/Cargo.toml b/Cargo.toml index eb178aa..b982e90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ documentation = "https://docs.rs/iso7816" [dependencies] delog = "0.1.2" -heapless = "0.7" +# use unreleased heapless for https://github.com/japaric/heapless/pull/255 +heapless = { git = "https://github.com/japaric/heapless" } [dev-dependencies] hex-literal = "0.3.1" diff --git a/src/command.rs b/src/command.rs index f00c614..1c67739 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,13 +1,13 @@ use core::convert::TryFrom; -use crate::Data; +use crate::somebytes::{Bytes, TryExtendFromSlice}; pub mod class; pub mod instruction; pub use instruction::Instruction; #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Command +pub struct Command> { class: class::Class, instruction: Instruction, @@ -15,21 +15,22 @@ pub struct Command pub p1: u8, pub p2: u8, - /// The main reason this is modeled as Bytes and not - /// a fixed array is for serde purposes. - data: Data, + data: Bytes, le: usize, pub extended: bool, } -impl Command +impl<'a, B: AsRef<[u8]> + TryFrom<&'a [u8]>> Command { - pub fn try_from(apdu: &[u8]) -> Result { + pub fn try_from(apdu: &'a [u8]) -> Result { use core::convert::TryInto; apdu.try_into() } +} +impl> Command +{ pub fn class(&self) -> class::Class { self.class } @@ -38,22 +39,24 @@ impl Command self.instruction } - pub fn data(&self) -> &Data { + pub fn data(&self) -> &B { &self.data } - pub fn data_mut(&mut self) -> &mut Data { + pub fn data_mut(&mut self) -> &mut B { &mut self.data } pub fn expected(&self) -> usize { self.le } +} +impl + TryExtendFromSlice> Command { /// This can be use for APDU chaining to convert /// multiple APDU's into one. /// * Global Platform GPC_SPE_055 3.10 - pub fn extend_from_command(&mut self, command: &Command) -> core::result::Result<(), ()> { + pub fn extend_from_command>(&mut self, command: &Command) -> core::result::Result<(), B::Error> { // Always take the header from the last command; self.class = command.class(); @@ -64,7 +67,7 @@ impl Command self.extended = true; // add the data to the end. - self.data.extend_from_slice(&command.data()) + self.data.extend_from_slice(command.data().as_ref()) } } @@ -83,10 +86,9 @@ impl From for FromSliceError { } } -impl TryFrom<&[u8]> for Command -{ +impl<'a, B: AsRef<[u8]> + TryFrom<&'a [u8]>> TryFrom<&'a [u8]> for Command { type Error = FromSliceError; - fn try_from(apdu: &[u8]) -> core::result::Result { + fn try_from(apdu: &'a [u8]) -> core::result::Result { if apdu.len() < 4 { return Err(FromSliceError::TooShort); } @@ -106,8 +108,7 @@ impl TryFrom<&[u8]> for Command // maximum expected response length le: parsed.le, // payload - data: Data::from_slice(data_slice) - .map_err(|_| Self::Error::TooLong)?, + data: B::try_from(data_slice).map_err(|_| Self::Error::TooLong)?.into(), extended: parsed.extended, }) } @@ -249,6 +250,6 @@ mod test { 0x06, 0x03, 0x55, 0x1d, ]; - let command = Command::<256>::try_from(apdu).unwrap(); + let command = Command::>::try_from(apdu).unwrap(); } } diff --git a/src/lib.rs b/src/lib.rs index 8139d0d..deba0be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,12 @@ pub enum Interface { Contactless, } -pub type Data = heapless::Vec; pub type Result = core::result::Result; pub mod aid; pub mod command; pub mod response; +pub mod somebytes; pub use aid::{Aid, App}; pub use command::{Command, Instruction}; diff --git a/src/response.rs b/src/response.rs index c7da6ef..3e2a982 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,15 +1,15 @@ mod status; pub use status::Status; -use crate::Data; +use crate::somebytes::Bytes; #[derive(Clone, Debug, Eq, PartialEq)] -pub enum Response { - Data(Data), +pub enum Response> { + Data(Bytes), Status(Status), } -impl Default for Response { +impl> Default for Response { fn default() -> Self { Self::Status(Default::default()) } diff --git a/src/response/status.rs b/src/response/status.rs index 153ae4f..69d7a1e 100644 --- a/src/response/status.rs +++ b/src/response/status.rs @@ -1,5 +1,4 @@ use core::convert::TryFrom; -use crate::Data; impl Default for Status { fn default() -> Self { @@ -171,12 +170,10 @@ impl Into<[u8; 2]> for Status { } } -impl Into> for Status -{ +impl Into> for Status { #[inline] - fn into(self) -> Data { + fn into(self) -> heapless::Vec { let arr: [u8; 2] = self.into(); - Data::from_slice(&arr).unwrap() + heapless::Vec::from_slice(&arr).unwrap() } } - diff --git a/src/somebytes.rs b/src/somebytes.rs new file mode 100644 index 0000000..66bfb1b --- /dev/null +++ b/src/somebytes.rs @@ -0,0 +1,79 @@ +// This could be a separate crate. + +use core::{cmp, fmt, ops}; + +pub trait TryExtendFromSlice { + type Error; + fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), Self::Error>; +} + +impl TryExtendFromSlice for heapless::Vec { + type Error = (); + fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), Self::Error> { + heapless::Vec::extend_from_slice(self, slice) + } +} + +/// A wrapper type for a byte sequence. +/// +/// This wrapper implements common traits based on the content of the byte sequence. +#[derive(Clone)] +pub struct Bytes>(T); + +impl> Bytes { + pub fn into_bytes(self) -> T { + self.0 + } +} + +impl> AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl> ops::Deref for Bytes { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl> ops::DerefMut for Bytes { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl> From for Bytes { + fn from(bytes: T) -> Self { + Self(bytes) + } +} + +impl> fmt::Debug for Bytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.as_ref().fmt(f) + } +} + +impl> Eq for Bytes {} + +impl> PartialEq for Bytes { + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq(other.0.as_ref()) + } +} + +impl> Ord for Bytes { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.0.as_ref().cmp(other.0.as_ref()) + } +} + +impl> PartialOrd for Bytes { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.as_ref().partial_cmp(other.0.as_ref()) + } +}