summaryrefslogtreecommitdiff
path: root/src/asn1_types/sequence.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/asn1_types/sequence.rs')
-rw-r--r--src/asn1_types/sequence.rs398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/asn1_types/sequence.rs b/src/asn1_types/sequence.rs
new file mode 100644
index 0000000..3a65ec0
--- /dev/null
+++ b/src/asn1_types/sequence.rs
@@ -0,0 +1,398 @@
+use crate::*;
+use alloc::borrow::Cow;
+use alloc::vec::Vec;
+use core::convert::TryFrom;
+
+mod iterator;
+mod sequence_of;
+mod vec;
+
+pub use iterator::*;
+pub use sequence_of::*;
+pub use vec::*;
+
+/// The `SEQUENCE` object is an ordered list of heteregeneous types.
+///
+/// Sequences can usually be of 2 types:
+/// - a list of different objects (`SEQUENCE`, usually parsed as a `struct`)
+/// - a list of similar objects (`SEQUENCE OF`, usually parsed as a `Vec<T>`)
+///
+/// The current object covers the former. For the latter, see the [`SequenceOf`] documentation.
+///
+/// The `Sequence` object contains the (*unparsed*) encoded representation of its content. It provides
+/// methods to parse and iterate contained objects, or convert the sequence to other types.
+///
+/// # Building a Sequence
+///
+/// To build a DER sequence:
+/// - if the sequence is composed of objects of the same type, the [`Sequence::from_iter_to_der`] method can be used
+/// - otherwise, the [`ToDer`] trait can be used to create content incrementally
+///
+/// ```
+/// use asn1_rs::{Integer, Sequence, SerializeResult, ToDer};
+///
+/// fn build_seq<'a>() -> SerializeResult<Sequence<'a>> {
+/// let mut v = Vec::new();
+/// // add an Integer object (construct type):
+/// let i = Integer::from_u32(4);
+/// let _ = i.write_der(&mut v)?;
+/// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`:
+/// let _ = "abcd".write_der(&mut v)?;
+/// // return the sequence built from the DER content
+/// Ok(Sequence::new(v.into()))
+/// }
+///
+/// let seq = build_seq().unwrap();
+///
+/// ```
+///
+/// # Examples
+///
+/// ```
+/// use asn1_rs::{Error, Sequence};
+///
+/// // build sequence
+/// let it = [2, 3, 4].iter();
+/// let seq = Sequence::from_iter_to_der(it).unwrap();
+///
+/// // `seq` now contains the serialized DER representation of the array
+///
+/// // iterate objects
+/// let mut sum = 0;
+/// for item in seq.der_iter::<u32, Error>() {
+/// // item has type `Result<u32>`, since parsing the serialized bytes could fail
+/// sum += item.expect("parsing list item failed");
+/// }
+/// assert_eq!(sum, 9);
+///
+/// ```
+///
+/// Note: the above example encodes a `SEQUENCE OF INTEGER` object, the [`SequenceOf`] object could
+/// be used to provide a simpler API.
+///
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Sequence<'a> {
+ /// Serialized DER representation of the sequence content
+ pub content: Cow<'a, [u8]>,
+}
+
+impl<'a> Sequence<'a> {
+ /// Build a sequence, given the provided content
+ pub const fn new(content: Cow<'a, [u8]>) -> Self {
+ Sequence { content }
+ }
+
+ /// Consume the sequence and return the content
+ #[inline]
+ pub fn into_content(self) -> Cow<'a, [u8]> {
+ self.content
+ }
+
+ /// Apply the parsing function to the sequence content, consuming the sequence
+ ///
+ /// Note: this function expects the caller to take ownership of content.
+ /// In some cases, handling the lifetime of objects is not easy (when keeping only references on
+ /// data). Other methods are provided (depending on the use case):
+ /// - [`Sequence::parse`] takes a reference on the sequence data, but does not consume it,
+ /// - [`Sequence::from_der_and_then`] does the parsing of the sequence and applying the function
+ /// in one step, ensuring there are only references (and dropping the temporary sequence).
+ pub fn and_then<U, F, E>(self, op: F) -> ParseResult<'a, U, E>
+ where
+ F: FnOnce(Cow<'a, [u8]>) -> ParseResult<U, E>,
+ {
+ op(self.content)
+ }
+
+ /// Same as [`Sequence::from_der_and_then`], but using BER encoding (no constraints).
+ pub fn from_ber_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
+ where
+ F: FnOnce(&'a [u8]) -> ParseResult<U, E>,
+ E: From<Error>,
+ {
+ let (rem, seq) = Sequence::from_ber(bytes).map_err(Err::convert)?;
+ let data = match seq.content {
+ Cow::Borrowed(b) => b,
+ // Since 'any' is built from 'bytes', it is borrowed by construction
+ Cow::Owned(_) => unreachable!(),
+ };
+ let (_, res) = op(data)?;
+ Ok((rem, res))
+ }
+
+ /// Parse a DER sequence and apply the provided parsing function to content
+ ///
+ /// After parsing, the sequence object and header are discarded.
+ ///
+ /// ```
+ /// use asn1_rs::{FromDer, ParseResult, Sequence};
+ ///
+ /// // Parse a SEQUENCE {
+ /// // a INTEGER (0..255),
+ /// // b INTEGER (0..4294967296)
+ /// // }
+ /// // and return only `(a,b)
+ /// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> {
+ /// Sequence::from_der_and_then(i, |i| {
+ /// let (i, a) = u8::from_der(i)?;
+ /// let (i, b) = u32::from_der(i)?;
+ /// Ok((i, (a, b)))
+ /// }
+ /// )
+ /// }
+ /// ```
+ pub fn from_der_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
+ where
+ F: FnOnce(&'a [u8]) -> ParseResult<U, E>,
+ E: From<Error>,
+ {
+ let (rem, seq) = Sequence::from_der(bytes).map_err(Err::convert)?;
+ let data = match seq.content {
+ Cow::Borrowed(b) => b,
+ // Since 'any' is built from 'bytes', it is borrowed by construction
+ Cow::Owned(_) => unreachable!(),
+ };
+ let (_, res) = op(data)?;
+ Ok((rem, res))
+ }
+
+ /// Apply the parsing function to the sequence content (non-consuming version)
+ pub fn parse<F, T, E>(&'a self, mut f: F) -> ParseResult<'a, T, E>
+ where
+ F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
+ {
+ let input: &[u8] = &self.content;
+ f(input)
+ }
+
+ /// Apply the parsing function to the sequence content (consuming version)
+ ///
+ /// Note: to parse and apply a parsing function in one step, use the
+ /// [`Sequence::from_der_and_then`] method.
+ ///
+ /// # Limitations
+ ///
+ /// This function fails if the sequence contains `Owned` data, because the parsing function
+ /// takes a reference on data (which is dropped).
+ pub fn parse_into<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E>
+ where
+ F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
+ E: From<Error>,
+ {
+ match self.content {
+ Cow::Borrowed(b) => f(b),
+ _ => Err(nom::Err::Error(Error::LifetimeError.into())),
+ }
+ }
+
+ /// Return an iterator over the sequence content, attempting to decode objects as BER
+ ///
+ /// This method can be used when all objects from the sequence have the same type.
+ pub fn ber_iter<T, E>(&'a self) -> SequenceIterator<'a, T, BerParser, E>
+ where
+ T: FromBer<'a, E>,
+ {
+ SequenceIterator::new(&self.content)
+ }
+
+ /// Return an iterator over the sequence content, attempting to decode objects as DER
+ ///
+ /// This method can be used when all objects from the sequence have the same type.
+ pub fn der_iter<T, E>(&'a self) -> SequenceIterator<'a, T, DerParser, E>
+ where
+ T: FromDer<'a, E>,
+ {
+ SequenceIterator::new(&self.content)
+ }
+
+ /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER), and return the parsed items as a `Vec`.
+ pub fn ber_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E>
+ where
+ T: FromBer<'a, E>,
+ E: From<Error>,
+ {
+ self.ber_iter().collect()
+ }
+
+ /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER), and return the parsed items as a `Vec`.
+ pub fn der_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E>
+ where
+ T: FromDer<'a, E>,
+ E: From<Error>,
+ {
+ self.der_iter().collect()
+ }
+
+ /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER) (consuming input),
+ /// and return the parsed items as a `Vec`.
+ ///
+ /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
+ pub fn into_ber_sequence_of<T, U, E>(self) -> Result<Vec<T>, E>
+ where
+ for<'b> T: FromBer<'b, E>,
+ E: From<Error>,
+ T: ToStatic<Owned = T>,
+ {
+ match self.content {
+ Cow::Borrowed(bytes) => SequenceIterator::<T, BerParser, E>::new(bytes).collect(),
+ Cow::Owned(data) => {
+ let v1 = SequenceIterator::<T, BerParser, E>::new(&data)
+ .collect::<Result<Vec<T>, E>>()?;
+ let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
+ Ok(v2)
+ }
+ }
+ }
+
+ /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER) (consuming input),
+ /// and return the parsed items as a `Vec`.
+ ///
+ /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
+ pub fn into_der_sequence_of<T, U, E>(self) -> Result<Vec<T>, E>
+ where
+ for<'b> T: FromDer<'b, E>,
+ E: From<Error>,
+ T: ToStatic<Owned = T>,
+ {
+ match self.content {
+ Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(),
+ Cow::Owned(data) => {
+ let v1 = SequenceIterator::<T, DerParser, E>::new(&data)
+ .collect::<Result<Vec<T>, E>>()?;
+ let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
+ Ok(v2)
+ }
+ }
+ }
+
+ pub fn into_der_sequence_of_ref<T, E>(self) -> Result<Vec<T>, E>
+ where
+ T: FromDer<'a, E>,
+ E: From<Error>,
+ {
+ match self.content {
+ Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(),
+ Cow::Owned(_) => Err(Error::LifetimeError.into()),
+ }
+ }
+}
+
+impl<'a> ToStatic for Sequence<'a> {
+ type Owned = Sequence<'static>;
+
+ fn to_static(&self) -> Self::Owned {
+ Sequence {
+ content: Cow::Owned(self.content.to_vec()),
+ }
+ }
+}
+
+impl<T, U> ToStatic for Vec<T>
+where
+ T: ToStatic<Owned = U>,
+ U: 'static,
+{
+ type Owned = Vec<U>;
+
+ fn to_static(&self) -> Self::Owned {
+ self.iter().map(|t| t.to_static()).collect()
+ }
+}
+
+impl<'a> AsRef<[u8]> for Sequence<'a> {
+ fn as_ref(&self) -> &[u8] {
+ &self.content
+ }
+}
+
+impl<'a> TryFrom<Any<'a>> for Sequence<'a> {
+ type Error = Error;
+
+ fn try_from(any: Any<'a>) -> Result<Sequence<'a>> {
+ TryFrom::try_from(&any)
+ }
+}
+
+impl<'a, 'b> TryFrom<&'b Any<'a>> for Sequence<'a> {
+ type Error = Error;
+
+ fn try_from(any: &'b Any<'a>) -> Result<Sequence<'a>> {
+ any.tag().assert_eq(Self::TAG)?;
+ any.header.assert_constructed()?;
+ Ok(Sequence {
+ content: Cow::Borrowed(any.data),
+ })
+ }
+}
+
+impl<'a> CheckDerConstraints for Sequence<'a> {
+ fn check_constraints(_any: &Any) -> Result<()> {
+ // TODO: iterate on ANY objects and check constraints? -> this will not be exhaustive
+ // test, for ex INTEGER encoding will not be checked
+ Ok(())
+ }
+}
+
+impl<'a> DerAutoDerive for Sequence<'a> {}
+
+impl<'a> Tagged for Sequence<'a> {
+ const TAG: Tag = Tag::Sequence;
+}
+
+#[cfg(feature = "std")]
+impl ToDer for Sequence<'_> {
+ fn to_der_len(&self) -> Result<usize> {
+ let sz = self.content.len();
+ if sz < 127 {
+ // 1 (class+tag) + 1 (length) + len
+ Ok(2 + sz)
+ } else {
+ // 1 (class+tag) + n (length) + len
+ let n = Length::Definite(sz).to_der_len()?;
+ Ok(1 + n + sz)
+ }
+ }
+
+ fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let header = Header::new(
+ Class::Universal,
+ true,
+ Self::TAG,
+ Length::Definite(self.content.len()),
+ );
+ header.write_der_header(writer).map_err(Into::into)
+ }
+
+ fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ writer.write(&self.content).map_err(Into::into)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a> Sequence<'a> {
+ /// Attempt to create a `Sequence` from an iterator over serializable objects (to DER)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use asn1_rs::Sequence;
+ ///
+ /// // build sequence
+ /// let it = [2, 3, 4].iter();
+ /// let seq = Sequence::from_iter_to_der(it).unwrap();
+ /// ```
+ pub fn from_iter_to_der<T, IT>(it: IT) -> SerializeResult<Self>
+ where
+ IT: Iterator<Item = T>,
+ T: ToDer,
+ T: Tagged,
+ {
+ let mut v = Vec::new();
+ for item in it {
+ let item_v = <T as ToDer>::to_der_vec(&item)?;
+ v.extend_from_slice(&item_v);
+ }
+ Ok(Sequence {
+ content: Cow::Owned(v),
+ })
+ }
+}