diff --git a/der/src/asn1.rs b/der/src/asn1.rs index a128f1bbb..9717b395f 100644 --- a/der/src/asn1.rs +++ b/der/src/asn1.rs @@ -74,7 +74,7 @@ pub use self::{ }; #[cfg(any(feature = "alloc", feature = "heapless"))] -pub use set_of::SetOfIter; +pub use set_of::{SetOfIter, SetOfRef}; #[cfg(feature = "oid")] pub use const_oid::ObjectIdentifier; diff --git a/der/src/asn1/set_of.rs b/der/src/asn1/set_of.rs index 2233a062d..81f50a985 100644 --- a/der/src/asn1/set_of.rs +++ b/der/src/asn1/set_of.rs @@ -11,8 +11,8 @@ #![cfg(any(feature = "alloc", feature = "heapless"))] use crate::{ - Decode, DecodeValue, DerOrd, Encode, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, - Reader, Tag, ValueOrd, Writer, ord::iter_cmp, + AnyRef, Decode, DecodeValue, DerOrd, Encode, EncodeValue, Error, ErrorKind, FixedTag, Header, + Length, Reader, SliceReader, Tag, ValueOrd, Writer, ord::iter_cmp, ord::iter_cmp_owned, }; use core::cmp::Ordering; @@ -35,6 +35,27 @@ where inner: heapless::Vec, } +// Inner reference of a SetOfRef +// +// An internal reference can either be bytes when constructed during decoding +// or a slice of items of the generic type T. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +enum InnerRef<'a, T> { + BytesRef(&'a [u8], usize), + ObjectsRef(&'a [T]), +} +/// ASN.1 `SET OF` with a reference to an array. +/// +/// This type implements a viewer in a `SET OF` type +/// and does not depend on `alloc` support. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct SetOfRef<'a, T> +where + T: DerOrd, +{ + inner: InnerRef<'a, T>, +} + #[cfg(feature = "heapless")] impl SetOf where @@ -125,6 +146,90 @@ where } } +impl<'a, T> SetOfRef<'a, T> +where + T: Decode<'a> + 'a, + T: Clone + DerOrd, +{ + /// Creates a [`SetOfRef`] by parsing the *contents* of a DER-encoded `SET OF` — + /// that is, the raw bytes after the tag and length bytes have been stripped. + fn from_bytes(v: &'a [u8]) -> Result { + // Make sure we can decode valid objects from the bytes + let mut reader = SliceReader::new(v)?; + + let mut iter_len = 0; + while !reader.is_finished() { + AnyRef::decode(&mut reader).map_err(|_| Error::from_kind(ErrorKind::Failed))?; + iter_len += 1; + } + + // Generate the set and check for ordering + let new_set = Self { + inner: InnerRef::BytesRef(v, iter_len), + }; + + new_set + .iter() + .is_sorted_by(|a, b| matches!(a.der_cmp(b), Ok(Ordering::Less))) + .then_some(new_set) + .ok_or_else(|| Error::from_kind(ErrorKind::SetOrdering)) + } + + /// Get the nth element from this [`SetOfRef`]. + #[must_use] + pub fn get(&self, index: usize) -> Option + where + T: Decode<'a> + 'a, + T: Clone, + { + self.iter().nth(index) + } + + /// Iterate over the elements of this [`SetOfRef`]. + /// + /// # Panics + /// + /// Panics if the inner byte slice contains invalid data that cannot be + /// parsed by [`SliceReader`]. + #[must_use] + pub fn iter(&self) -> SetOfRefIter<'a, T> + where + T: Decode<'a> + 'a, + { + match self.inner { + InnerRef::BytesRef(inner, length) => SetOfRefIter { + inner: InnerIterRef::<'a, T>::BytesRef( + SliceReader::new(inner).expect("Invalid data"), + ), + length, + }, + InnerRef::ObjectsRef(inner) => SetOfRefIter { + inner: InnerIterRef::<'a, T>::ObjectsRef(inner), + length: inner.len(), + }, + } + } + + /// Is this [`SetOfRef`] empty? + #[must_use] + pub fn is_empty(&self) -> bool { + match self.inner { + InnerRef::BytesRef(inner, _) => inner.is_empty(), + InnerRef::ObjectsRef(inner) => inner.is_empty(), + } + } + + /// Number of elements in this [`SetOfRef`]. + #[must_use] + pub fn len(&self) -> usize + where + T: Decode<'a> + 'a, + T: Clone, + { + self.iter().len() + } +} + #[cfg(feature = "heapless")] impl Default for SetOf where @@ -158,6 +263,19 @@ where } } +impl<'a, T> DecodeValue<'a> for SetOfRef<'a, T> +where + T: Clone, + T: Decode<'a> + DerOrd, +{ + type Error = Error; + + fn decode_value>(reader: &mut R, header: Header) -> Result { + let inner_slice: &'a [u8] = reader.read_slice(header.length())?; + SetOfRef::<'a, T>::from_bytes(inner_slice) + } +} + #[cfg(feature = "heapless")] impl EncodeValue for SetOf where @@ -177,6 +295,25 @@ where } } +impl<'a, T> EncodeValue for SetOfRef<'a, T> +where + T: Decode<'a> + Encode + DerOrd, + T: Clone, +{ + fn value_len(&self) -> Result { + self.iter() + .try_fold(Length::ZERO, |len, elem| len + elem.encoded_len()?) + } + + fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> { + for elem in self.iter() { + elem.encode(writer)?; + } + + Ok(()) + } +} + #[cfg(feature = "heapless")] impl FixedTag for SetOf where @@ -185,6 +322,13 @@ where const TAG: Tag = Tag::Set; } +impl<'a, T> FixedTag for SetOfRef<'a, T> +where + T: DerOrd, +{ + const TAG: Tag = Tag::Set; +} + #[cfg(feature = "heapless")] impl TryFrom<[T; N]> for SetOf where @@ -205,6 +349,35 @@ where } } +impl<'a, T> TryFrom<&'a [T]> for SetOfRef<'a, T> +where + T: DerOrd, +{ + type Error = Error; + + fn try_from(arr: &'a [T]) -> Result, Error> { + arr.windows(2) + .try_for_each(|w| match w[0].der_cmp(&w[1]) { + Ok(Ordering::Less) => Ok(()), + Ok(Ordering::Equal) => Err(Error::from_kind(ErrorKind::SetDuplicate)), + Ok(Ordering::Greater) => Err(Error::from_kind(ErrorKind::SetOrdering)), + Err(e) => Err(e), + }) + .map(|_| Self { + inner: InnerRef::ObjectsRef(arr), + }) + } +} + +impl<'a, T> From<&SetOfRef<'a, T>> for SetOfRef<'a, T> +where + T: Clone + DerOrd, +{ + fn from(value: &SetOfRef<'a, T>) -> SetOfRef<'a, T> { + value.clone() + } +} + #[cfg(feature = "heapless")] impl ValueOrd for SetOf where @@ -215,6 +388,16 @@ where } } +impl<'a, T> ValueOrd for SetOfRef<'a, T> +where + T: Decode<'a> + DerOrd + 'a, + T: Clone, +{ + fn value_cmp(&self, other: &Self) -> Result { + iter_cmp_owned(self.iter(), other.iter()) + } +} + /// Iterator over the elements of an [`SetOf`]. #[derive(Clone, Debug)] pub struct SetOfIter<'a, T> { @@ -236,6 +419,67 @@ impl<'a, T: 'a> Iterator for SetOfIter<'a, T> { impl<'a, T: 'a> ExactSizeIterator for SetOfIter<'a, T> {} +// Inner reference of a SetOfRefIter +// +// An internal reference can either be a slice reader when constructed during decoding +// or a slice of items of the generic type T. +#[derive(Clone, Debug)] +enum InnerIterRef<'a, T> { + BytesRef(SliceReader<'a>), + ObjectsRef(&'a [T]), +} + +/// Iterator over the elements of an [`SetOfRef`]. +#[derive(Clone, Debug)] +pub struct SetOfRefIter<'a, T> +where + T: Decode<'a>, +{ + /// Inner iterator. + inner: InnerIterRef<'a, T>, + length: usize, +} + +impl<'a, T> Iterator for SetOfRefIter<'a, T> +where + T: Decode<'a> + 'a, + T: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option { + match &mut self.inner { + InnerIterRef::BytesRef(inner_reader) => { + if inner_reader.is_finished() { + return None; + } + + let next_val = T::decode(inner_reader).ok()?; + self.length -= 1; + Some(next_val) + } + InnerIterRef::ObjectsRef(inner_slice) => { + let next_val = inner_slice.first()?; + self.inner = InnerIterRef::ObjectsRef(&inner_slice[1..]); + self.length -= 1; + + Some(next_val.clone()) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } +} + +impl<'a, T> ExactSizeIterator for SetOfRefIter<'a, T> +where + T: Decode<'a> + 'a, + T: Clone, +{ +} + /// ASN.1 `SET OF` backed by a [`Vec`]. /// /// This type implements an append-only `SET OF` type which is heap-backed @@ -544,14 +788,55 @@ fn der_sort(slice: &mut [T]) -> Result<(), Error> { Ok(()) } +#[cfg(feature = "alloc")] +mod allocating { + use super::*; + use crate::referenced::*; + + impl<'a, T> RefToOwned<'a> for SetOfRef<'a, T> + where + T: Decode<'a> + EncodeValue + 'a, + T: DerOrd + FixedTag, + T: Clone, + { + type Owned = SetOfVec; + fn ref_to_owned(&self) -> Self::Owned { + SetOfVec::from_iter(self.iter()).expect("SetOfVec: Could not sort inner slice") + } + } + + impl OwnedToRef for SetOfVec + where + T: Encode, + T: DerOrd, + { + type Borrowed<'a> + = SetOfRef<'a, T> + where + T: 'a; + + fn owned_to_ref(&self) -> Self::Borrowed<'_> { + SetOfRef::::try_from(self.inner.as_slice()).expect("Unsorted slice") + } + } +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { - #[cfg(feature = "alloc")] - use super::SetOfVec; + use crate::ErrorKind; + #[cfg(feature = "alloc")] + use { + super::SetOfVec, + crate::{Decode, Encode, EncodeValue, SliceWriter}, + alloc::vec, + }; + #[cfg(feature = "heapless")] - use {super::SetOf, crate::DerOrd}; + use super::SetOf; + #[cfg(any(feature = "alloc", feature = "heapless"))] + use {super::SetOfRef, crate::DerOrd}; #[cfg(feature = "heapless")] #[test] @@ -597,6 +882,64 @@ mod tests { assert_eq!(set1.der_cmp(&set2), Ok(Ordering::Greater)); } + #[test] + fn setofref_tryfrom_array() { + let arr = [0u16, 1, 2, 3, 65535]; + let set = SetOfRef::try_from(arr.as_ref()).unwrap(); + assert!(set.iter().eq([0, 1, 2, 3, 65535])); + } + + #[cfg(feature = "alloc")] + #[test] + fn setofref_tryfrom_der() { + let arr = SetOfVec::try_from([0u16, 1, 2, 3, 65535]) + .unwrap() + .to_der() + .unwrap(); + // print!("{:?}", arr); + let set = SetOfRef::::from_der(arr.as_ref()).unwrap(); + assert!(set.iter().eq([0, 1, 2, 3, 65535])); + } + + #[cfg(feature = "alloc")] + #[test] + fn setofref_tryfrom_bytes() { + let arr = SetOfVec::try_from([0u16, 1, 2, 3, 65535]).unwrap(); + + let mut encoded = vec![0u8; arr.value_len().unwrap().try_into().unwrap()]; + let mut writer = SliceWriter::new(&mut encoded); + arr.encode_value(&mut writer).unwrap(); + + let decoded = SetOfRef::::from_bytes(writer.finish().unwrap()).unwrap(); + + assert!(decoded.iter().eq([0, 1, 2, 3, 65535])); + } + + #[test] + fn setofref_tryfrom_array_reject_unsorted() { + let arr = [3u16, 2, 1, 65535, 0]; + let err = SetOfRef::try_from(arr.as_ref()).err().unwrap(); + assert_eq!(err.kind(), ErrorKind::SetOrdering); + } + + #[test] + fn setofref_tryfrom_array_reject_duplicates() { + let arr = [1u16, 1]; + let err = SetOfRef::try_from(arr.as_ref()).err().unwrap(); + assert_eq!(err.kind(), ErrorKind::SetDuplicate); + } + + #[test] + fn setofref_valueord_value_cmp() { + use core::cmp::Ordering; + + let arr1 = [0u16, 1, 2, 3, 5]; + let arr2 = [0u16, 1, 2, 3, 4]; + let set1 = SetOfRef::try_from(arr1.as_ref()).unwrap(); + let set2 = SetOfRef::try_from(arr2.as_ref()).unwrap(); + assert_eq!(set1.der_cmp(&set2), Ok(Ordering::Greater)); + } + #[cfg(feature = "alloc")] #[test] fn setofvec_insert() { diff --git a/der/src/ord.rs b/der/src/ord.rs index ca430d771..6d33a42e7 100644 --- a/der/src/ord.rs +++ b/der/src/ord.rs @@ -85,6 +85,25 @@ where Ok(length_ord) } +#[cfg(any(feature = "alloc", feature = "heapless"))] +/// Compare the order of two iterators using [`DerCmp`] on the values. +pub(crate) fn iter_cmp_owned<'a, I, T>(a: I, b: I) -> Result +where + I: Iterator + ExactSizeIterator, + T: 'a + DerOrd, +{ + let length_ord = a.len().cmp(&b.len()); + + for (value1, value2) in a.zip(b) { + match value1.der_cmp(&value2)? { + Ordering::Equal => (), + other => return Ok(other), + } + } + + Ok(length_ord) +} + /// Provide a no-op implementation for `PhantomData` impl ValueOrd for PhantomData { fn value_cmp(&self, _other: &Self) -> Result {