summaryrefslogtreecommitdiff
path: root/src/traits.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/traits.rs')
-rw-r--r--src/traits.rs348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/traits.rs b/src/traits.rs
new file mode 100644
index 0000000..eea4cb9
--- /dev/null
+++ b/src/traits.rs
@@ -0,0 +1,348 @@
+use crate::error::*;
+use crate::{Any, Class, Explicit, Implicit, Tag, TaggedParser};
+use core::convert::{TryFrom, TryInto};
+#[cfg(feature = "std")]
+use std::io::Write;
+
+/// Phantom type representing a BER parser
+#[doc(hidden)]
+#[derive(Debug)]
+pub enum BerParser {}
+
+/// Phantom type representing a DER parser
+#[doc(hidden)]
+#[derive(Debug)]
+pub enum DerParser {}
+
+#[doc(hidden)]
+pub trait ASN1Parser {}
+
+impl ASN1Parser for BerParser {}
+impl ASN1Parser for DerParser {}
+
+pub trait Tagged {
+ const TAG: Tag;
+}
+
+impl<T> Tagged for &'_ T
+where
+ T: Tagged,
+{
+ const TAG: Tag = T::TAG;
+}
+
+pub trait DynTagged {
+ fn tag(&self) -> Tag;
+}
+
+impl<T> DynTagged for T
+where
+ T: Tagged,
+{
+ fn tag(&self) -> Tag {
+ T::TAG
+ }
+}
+
+/// Base trait for BER object parsers
+///
+/// Library authors should usually not directly implement this trait, but should prefer implementing the
+/// [`TryFrom<Any>`] trait,
+/// which offers greater flexibility and provides an equivalent `FromBer` implementation for free.
+///
+/// # Examples
+///
+/// ```
+/// use asn1_rs::{Any, Result, Tag};
+/// use std::convert::TryFrom;
+///
+/// // The type to be decoded
+/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+/// pub struct MyType(pub u32);
+///
+/// impl<'a> TryFrom<Any<'a>> for MyType {
+/// type Error = asn1_rs::Error;
+///
+/// fn try_from(any: Any<'a>) -> Result<MyType> {
+/// any.tag().assert_eq(Tag::Integer)?;
+/// // for this fictive example, the type contains the number of characters
+/// let n = any.data.len() as u32;
+/// Ok(MyType(n))
+/// }
+/// }
+///
+/// // The above code provides a `FromBer` implementation for free.
+///
+/// // Example of parsing code:
+/// use asn1_rs::FromBer;
+///
+/// let input = &[2, 1, 2];
+/// // Objects can be parsed using `from_ber`, which returns the remaining bytes
+/// // and the parsed object:
+/// let (rem, my_type) = MyType::from_ber(input).expect("parsing failed");
+/// ```
+pub trait FromBer<'a, E = Error>: Sized {
+ /// Attempt to parse input bytes into a BER object
+ fn from_ber(bytes: &'a [u8]) -> ParseResult<Self, E>;
+}
+
+impl<'a, T, E> FromBer<'a, E> for T
+where
+ T: TryFrom<Any<'a>, Error = E>,
+ E: From<Error>,
+{
+ fn from_ber(bytes: &'a [u8]) -> ParseResult<T, E> {
+ let (i, any) = Any::from_ber(bytes).map_err(nom::Err::convert)?;
+ let result = any.try_into().map_err(nom::Err::Error)?;
+ Ok((i, result))
+ }
+}
+
+/// Base trait for DER object parsers
+///
+/// Library authors should usually not directly implement this trait, but should prefer implementing the
+/// [`TryFrom<Any>`] + [`CheckDerConstraints`] traits,
+/// which offers greater flexibility and provides an equivalent `FromDer` implementation for free
+/// (in fact, it provides both [`FromBer`] and `FromDer`).
+///
+/// Note: if you already implemented [`TryFrom<Any>`] and [`CheckDerConstraints`],
+/// you can get a free [`FromDer`] implementation by implementing the
+/// [`DerAutoDerive`] trait. This is not automatic, so it is also possible to manually
+/// implement [`FromDer`] if preferred.
+///
+/// # Examples
+///
+/// ```
+/// use asn1_rs::{Any, CheckDerConstraints, DerAutoDerive, Result, Tag};
+/// use std::convert::TryFrom;
+///
+/// // The type to be decoded
+/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+/// pub struct MyType(pub u32);
+///
+/// impl<'a> TryFrom<Any<'a>> for MyType {
+/// type Error = asn1_rs::Error;
+///
+/// fn try_from(any: Any<'a>) -> Result<MyType> {
+/// any.tag().assert_eq(Tag::Integer)?;
+/// // for this fictive example, the type contains the number of characters
+/// let n = any.data.len() as u32;
+/// Ok(MyType(n))
+/// }
+/// }
+///
+/// impl CheckDerConstraints for MyType {
+/// fn check_constraints(any: &Any) -> Result<()> {
+/// any.header.assert_primitive()?;
+/// Ok(())
+/// }
+/// }
+///
+/// impl DerAutoDerive for MyType {}
+///
+/// // The above code provides a `FromDer` implementation for free.
+///
+/// // Example of parsing code:
+/// use asn1_rs::FromDer;
+///
+/// let input = &[2, 1, 2];
+/// // Objects can be parsed using `from_der`, which returns the remaining bytes
+/// // and the parsed object:
+/// let (rem, my_type) = MyType::from_der(input).expect("parsing failed");
+/// ```
+pub trait FromDer<'a, E = Error>: Sized {
+ /// Attempt to parse input bytes into a DER object (enforcing constraints)
+ fn from_der(bytes: &'a [u8]) -> ParseResult<Self, E>;
+}
+
+/// Trait to automatically derive `FromDer`
+///
+/// This trait is only a marker to control if a DER parser should be automatically derived. It is
+/// empty.
+///
+/// This trait is used in combination with others:
+/// after implementing [`TryFrom<Any>`] and [`CheckDerConstraints`] for a type,
+/// a free [`FromDer`] implementation is provided by implementing the
+/// [`DerAutoDerive`] trait. This is the most common case.
+///
+/// However, this is not automatic so it is also possible to manually
+/// implement [`FromDer`] if preferred.
+/// Manual implementation is generally only needed for generic containers (for ex. `Vec<T>`),
+/// because the default implementation adds a constraint on `T` to implement also `TryFrom<Any>`
+/// and `CheckDerConstraints`. This is problematic when `T` only provides `FromDer`, and can be
+/// solved by providing a manual implementation of [`FromDer`].
+pub trait DerAutoDerive {}
+
+impl<'a, T, E> FromDer<'a, E> for T
+where
+ T: TryFrom<Any<'a>, Error = E>,
+ T: CheckDerConstraints,
+ T: DerAutoDerive,
+ E: From<Error>,
+{
+ fn from_der(bytes: &'a [u8]) -> ParseResult<T, E> {
+ // Note: Any::from_der checks than length is definite
+ let (i, any) = Any::from_der(bytes).map_err(nom::Err::convert)?;
+ <T as CheckDerConstraints>::check_constraints(&any)
+ .map_err(|e| nom::Err::Error(e.into()))?;
+ let result = any.try_into().map_err(nom::Err::Error)?;
+ Ok((i, result))
+ }
+}
+
+/// Verification of DER constraints
+pub trait CheckDerConstraints {
+ fn check_constraints(any: &Any) -> Result<()>;
+}
+
+/// Common trait for all objects that can be encoded using the DER representation
+///
+/// # Examples
+///
+/// Objects from this crate can be encoded as DER:
+///
+/// ```
+/// use asn1_rs::{Integer, ToDer};
+///
+/// let int = Integer::from(4u32);
+/// let mut writer = Vec::new();
+/// let sz = int.write_der(&mut writer).expect("serialization failed");
+///
+/// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
+/// # assert_eq!(sz, 3);
+/// ```
+///
+/// Many of the primitive types can also directly be encoded as DER:
+///
+/// ```
+/// use asn1_rs::ToDer;
+///
+/// let mut writer = Vec::new();
+/// let sz = 4.write_der(&mut writer).expect("serialization failed");
+///
+/// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
+/// # assert_eq!(sz, 3);
+/// ```
+#[cfg(feature = "std")]
+pub trait ToDer
+where
+ Self: DynTagged,
+{
+ /// Get the length of the object (including the header), when encoded
+ ///
+ // Since we are using DER, length cannot be Indefinite, so we can use `usize`.
+ // XXX can this function fail?
+ fn to_der_len(&self) -> Result<usize>;
+
+ /// Write the DER encoded representation to a newly allocated `Vec<u8>`.
+ fn to_der_vec(&self) -> SerializeResult<Vec<u8>> {
+ let mut v = Vec::new();
+ let _ = self.write_der(&mut v)?;
+ Ok(v)
+ }
+
+ /// Similar to using `to_vec`, but uses provided values without changes.
+ /// This can generate an invalid encoding for a DER object.
+ fn to_der_vec_raw(&self) -> SerializeResult<Vec<u8>> {
+ let mut v = Vec::new();
+ let _ = self.write_der_raw(&mut v)?;
+ Ok(v)
+ }
+
+ /// Attempt to write the DER encoded representation (header and content) into this writer.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use asn1_rs::{Integer, ToDer};
+ ///
+ /// let int = Integer::from(4u32);
+ /// let mut writer = Vec::new();
+ /// let sz = int.write_der(&mut writer).expect("serialization failed");
+ ///
+ /// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
+ /// # assert_eq!(sz, 3);
+ /// ```
+ fn write_der(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
+ let sz = self.write_der_header(writer)?;
+ let sz = sz + self.write_der_content(writer)?;
+ Ok(sz)
+ }
+
+ /// Attempt to write the DER header to this writer.
+ fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize>;
+
+ /// Attempt to write the DER content (all except header) to this writer.
+ fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize>;
+
+ /// Similar to using `to_der`, but uses provided values without changes.
+ /// This can generate an invalid encoding for a DER object.
+ fn write_der_raw(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
+ self.write_der(writer)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a, T> ToDer for &'a T
+where
+ T: ToDer,
+ &'a T: DynTagged,
+{
+ fn to_der_len(&self) -> Result<usize> {
+ (*self).to_der_len()
+ }
+
+ fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
+ (*self).write_der_header(writer)
+ }
+
+ fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
+ (*self).write_der_content(writer)
+ }
+}
+
+/// Helper trait for creating tagged EXPLICIT values
+///
+/// # Examples
+///
+/// ```
+/// use asn1_rs::{AsTaggedExplicit, Class, Error, TaggedParser};
+///
+/// // create a `[1] EXPLICIT INTEGER` value
+/// let tagged: TaggedParser<_, _, Error> = 4u32.explicit(Class::ContextSpecific, 1);
+/// ```
+pub trait AsTaggedExplicit<'a, E = Error>: Sized {
+ fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E> {
+ TaggedParser::new_explicit(class, tag, self)
+ }
+}
+
+impl<'a, T, E> AsTaggedExplicit<'a, E> for T where T: Sized + 'a {}
+
+/// Helper trait for creating tagged IMPLICIT values
+///
+/// # Examples
+///
+/// ```
+/// use asn1_rs::{AsTaggedImplicit, Class, Error, TaggedParser};
+///
+/// // create a `[1] IMPLICIT INTEGER` value, not constructed
+/// let tagged: TaggedParser<_, _, Error> = 4u32.implicit(Class::ContextSpecific, false, 1);
+/// ```
+pub trait AsTaggedImplicit<'a, E = Error>: Sized {
+ fn implicit(
+ self,
+ class: Class,
+ constructed: bool,
+ tag: u32,
+ ) -> TaggedParser<'a, Implicit, Self, E> {
+ TaggedParser::new_implicit(class, constructed, tag, self)
+ }
+}
+
+impl<'a, T, E> AsTaggedImplicit<'a, E> for T where T: Sized + 'a {}
+
+pub trait ToStatic {
+ type Owned: 'static;
+ fn to_static(&self) -> Self::Owned;
+}