diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 59799a4..31a5fec 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -28,9 +28,16 @@ jobs: with: command: check - test: - name: Test Suite + check_features: + name: Check features runs-on: ubuntu-latest + strategy: + matrix: + features: + - --features=default + - --all-features + - --features=bigint,serialize,debug + - --features=bigint,serialize,trace steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -38,10 +45,12 @@ jobs: profile: minimal toolchain: stable override: true + - name: Cargo update + run: cargo update - uses: actions-rs/cargo@v1 with: command: test - args: --all-features + args: ${{ matrix.features }} no_std: name: no-std diff --git a/Cargo.toml b/Cargo.toml index ddedb47..b5e3904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,13 +42,16 @@ default = ["std"] bigint = ["num-bigint"] bits = ["bitvec"] datetime = ["time"] +debug = ["colored"] serialize = ["cookie-factory"] std = [] +trace = ["debug"] [dependencies] asn1-rs-derive = { version="0.5", path="./derive" } asn1-rs-impl = { version="0.2", path="./impl" } bitvec = { version="1.0", optional=true } +colored = { version="2.0", optional=true } cookie-factory = { version="0.3.0", optional=true } displaydoc = "0.2.2" nom = { version="7.0", default_features=false, features=["std"] } diff --git a/src/asn1_types/any.rs b/src/asn1_types/any.rs index 0679345..330b02a 100644 --- a/src/asn1_types/any.rs +++ b/src/asn1_types/any.rs @@ -1,9 +1,12 @@ use crate::ber::*; use crate::*; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] use alloc::string::String; use core::convert::{TryFrom, TryInto}; +use self::debug::trace; + /// The `Any` object is not strictly an ASN.1 type, but holds a generic description of any object /// that could be encoded. /// @@ -101,10 +104,10 @@ impl<'a> Any<'a> { let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(tag)) - .map_err(|e| nom::Err::Error(e.into()))?; + .map_err(|e| Err::Error(e.into()))?; any.class() .assert_eq(class) - .map_err(|e| nom::Err::Error(e.into()))?; + .map_err(|e| Err::Error(e.into()))?; let (_, res) = op(any.data)?; Ok((rem, res)) } @@ -125,10 +128,10 @@ impl<'a> Any<'a> { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(tag)) - .map_err(|e| nom::Err::Error(e.into()))?; + .map_err(|e| Err::Error(e.into()))?; any.class() .assert_eq(class) - .map_err(|e| nom::Err::Error(e.into()))?; + .map_err(|e| Err::Error(e.into()))?; let (_, res) = op(any.data)?; Ok((rem, res)) } @@ -320,21 +323,31 @@ impl<'a> Any<'a> { } } +pub(crate) fn parse_ber_any(input: &[u8]) -> ParseResult { + let (i, header) = Header::from_ber(input)?; + let (i, data) = BerParser::get_object_content(i, &header, MAX_RECURSION)?; + Ok((i, Any { header, data })) +} + +pub(crate) fn parse_der_any(input: &[u8]) -> ParseResult { + let (i, header) = Header::from_der(input)?; + // X.690 section 10.1: The definite form of length encoding shall be used + header.length.assert_definite()?; + let (i, data) = DerParser::get_object_content(i, &header, MAX_RECURSION)?; + Ok((i, Any { header, data })) +} + impl<'a> FromBer<'a> for Any<'a> { + #[inline] fn from_ber(bytes: &'a [u8]) -> ParseResult { - let (i, header) = Header::from_ber(bytes)?; - let (i, data) = BerParser::get_object_content(i, &header, MAX_RECURSION)?; - Ok((i, Any { header, data })) + trace("Any", parse_ber_any, bytes) } } impl<'a> FromDer<'a> for Any<'a> { + #[inline] fn from_der(bytes: &'a [u8]) -> ParseResult { - let (i, header) = Header::from_der(bytes)?; - // X.690 section 10.1: The definite form of length encoding shall be used - header.length.assert_definite()?; - let (i, data) = DerParser::get_object_content(i, &header, MAX_RECURSION)?; - Ok((i, Any { header, data })) + trace("Any", parse_der_any, bytes) } } diff --git a/src/asn1_types/generalizedtime.rs b/src/asn1_types/generalizedtime.rs index 6e039d8..42a5629 100644 --- a/src/asn1_types/generalizedtime.rs +++ b/src/asn1_types/generalizedtime.rs @@ -1,6 +1,6 @@ -use crate::datetime::decode_decimal; use crate::*; use alloc::format; +#[cfg(not(feature = "std"))] use alloc::string::String; use core::convert::TryFrom; use core::fmt; diff --git a/src/asn1_types/integer.rs b/src/asn1_types/integer.rs index 59f846a..cb463e4 100644 --- a/src/asn1_types/integer.rs +++ b/src/asn1_types/integer.rs @@ -96,19 +96,26 @@ macro_rules! impl_int { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { - any.tag().assert_eq(Self::TAG)?; - any.header.assert_primitive()?; - let uint = if is_highest_bit_set(any.as_bytes()) { - <$uint>::from_be_bytes(decode_array_int(&any)?) - } else { - // read as uint, but check if the value will fit in a signed integer - let u = <$uint>::from_be_bytes(decode_array_uint(&any)?); - if u > <$int>::MAX as $uint { - return Err(Error::IntegerTooLarge); - } - u - }; - Ok(uint as $int) + $crate::debug::trace_generic( + core::any::type_name::<$int>(), + "Conversion to int", + |any| { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let uint = if is_highest_bit_set(any.as_bytes()) { + <$uint>::from_be_bytes(decode_array_int(&any)?) + } else { + // read as uint, but check if the value will fit in a signed integer + let u = <$uint>::from_be_bytes(decode_array_uint(&any)?); + if u > <$int>::MAX as $uint { + return Err(Error::IntegerTooLarge); + } + u + }; + Ok(uint as $int) + }, + any, + ) } } @@ -162,10 +169,17 @@ macro_rules! impl_uint { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { - any.tag().assert_eq(Self::TAG)?; - any.header.assert_primitive()?; - let result = Self::from_be_bytes(decode_array_uint(any)?); - Ok(result) + $crate::debug::trace_generic( + core::any::type_name::<$ty>(), + "Conversion to uint", + |any| { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let result = Self::from_be_bytes(decode_array_uint(any)?); + Ok(result) + }, + any, + ) } } impl CheckDerConstraints for $ty { diff --git a/src/asn1_types/object_descriptor.rs b/src/asn1_types/object_descriptor.rs index db78870..8f60e7c 100644 --- a/src/asn1_types/object_descriptor.rs +++ b/src/asn1_types/object_descriptor.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; // X.680 section 44.3 diff --git a/src/asn1_types/oid.rs b/src/asn1_types/oid.rs index a0747e6..aa8bc43 100644 --- a/src/asn1_types/oid.rs +++ b/src/asn1_types/oid.rs @@ -2,14 +2,13 @@ use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::format; +#[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::{ convert::TryFrom, fmt, iter::FusedIterator, marker::PhantomData, ops::Shl, str::FromStr, }; - -#[cfg(feature = "bigint")] -use num_bigint::BigUint; use num_traits::Num; /// An error for OID parsing functions. diff --git a/src/asn1_types/real.rs b/src/asn1_types/real.rs index 91f64e2..0abcc34 100644 --- a/src/asn1_types/real.rs +++ b/src/asn1_types/real.rs @@ -1,7 +1,6 @@ use crate::*; use alloc::format; use core::convert::TryFrom; -use nom::Needed; mod f32; mod f64; diff --git a/src/asn1_types/sequence.rs b/src/asn1_types/sequence.rs index 61ab888..ebacf41 100644 --- a/src/asn1_types/sequence.rs +++ b/src/asn1_types/sequence.rs @@ -1,5 +1,6 @@ use crate::*; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; @@ -179,7 +180,7 @@ impl<'a> Sequence<'a> { { match self.content { Cow::Borrowed(b) => f(b), - _ => Err(nom::Err::Error(Error::LifetimeError.into())), + _ => Err(Err::Error(Error::LifetimeError.into())), } } diff --git a/src/asn1_types/sequence/sequence_of.rs b/src/asn1_types/sequence/sequence_of.rs index 8535b18..d0e5afa 100644 --- a/src/asn1_types/sequence/sequence_of.rs +++ b/src/asn1_types/sequence/sequence_of.rs @@ -1,9 +1,13 @@ use crate::*; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; +use core::fmt::{Debug, Display}; use core::iter::FromIterator; use core::ops::{Deref, DerefMut}; +use self::debug::{trace, trace_generic}; + /// The `SEQUENCE OF` object is an ordered list of homogeneous types. /// /// This type implements `Deref` and `DerefMut`, so all methods @@ -128,17 +132,25 @@ where impl<'a, T, E> FromDer<'a, E> for SequenceOf where T: FromDer<'a, E>, - E: From, + E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult { - let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; - any.header - .assert_tag(Self::TAG) - .map_err(|e| nom::Err::Error(e.into()))?; - let items = SequenceIterator::::new(any.data) - .collect::, E>>() - .map_err(nom::Err::Error)?; - Ok((rem, SequenceOf::new(items))) + trace_generic( + core::any::type_name::(), + "SequenceOf::from_der", + |bytes| { + let (rem, any) = trace(core::any::type_name::(), parse_der_any, bytes) + .map_err(Err::convert)?; + any.header + .assert_tag(Self::TAG) + .map_err(|e| Err::Error(e.into()))?; + let items = SequenceIterator::::new(any.data) + .collect::, E>>() + .map_err(Err::Error)?; + Ok((rem, SequenceOf::new(items))) + }, + bytes, + ) } } diff --git a/src/asn1_types/sequence/vec.rs b/src/asn1_types/sequence/vec.rs index d8beead..a5daea1 100644 --- a/src/asn1_types/sequence/vec.rs +++ b/src/asn1_types/sequence/vec.rs @@ -1,6 +1,10 @@ use crate::*; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; +use core::fmt::Debug; + +use self::debug::{trace, trace_generic}; // // XXX this compiles but requires bound TryFrom :/ // impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec @@ -49,10 +53,26 @@ where type Error = Error; fn try_from(any: Any<'a>) -> Result { - any.tag().assert_eq(Self::TAG)?; - any.header.assert_constructed()?; - let items = SetIterator::::new(any.data).collect::>>()?; - Ok(items) + trace_generic( + core::any::type_name::(), + "T::from(Any)", + |any| { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + let items = SetIterator::::new(any.data) + .collect::>>() + .map_err(|e| { + debug_eprintln!( + core::any::type_name::(), + "≠ {}", + "Conversion from Any failed".red() + ); + e + })?; + Ok(items) + }, + any, + ) } } @@ -91,17 +111,25 @@ impl Tagged for Vec { impl<'a, T, E> FromDer<'a, E> for Vec where T: FromDer<'a, E>, - E: From, + E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult { - let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; - any.header - .assert_tag(Self::TAG) - .map_err(|e| nom::Err::Error(e.into()))?; - let v = SequenceIterator::::new(any.data) - .collect::, E>>() - .map_err(nom::Err::Error)?; - Ok((rem, v)) + trace_generic( + core::any::type_name::(), + "Sequence::from_der", + |bytes| { + let (rem, any) = trace(core::any::type_name::(), parse_der_any, bytes) + .map_err(Err::convert)?; + any.header + .assert_tag(Self::TAG) + .map_err(|e| Err::Error(e.into()))?; + let v = SequenceIterator::::new(any.data) + .collect::, E>>() + .map_err(Err::Error)?; + Ok((rem, v)) + }, + bytes, + ) } } diff --git a/src/asn1_types/set.rs b/src/asn1_types/set.rs index fb21399..06503c0 100644 --- a/src/asn1_types/set.rs +++ b/src/asn1_types/set.rs @@ -1,5 +1,6 @@ use crate::*; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; @@ -180,7 +181,7 @@ impl<'a> Set<'a> { { match self.content { Cow::Borrowed(b) => f(b), - _ => Err(nom::Err::Error(Error::LifetimeError.into())), + _ => Err(Err::Error(Error::LifetimeError.into())), } } diff --git a/src/asn1_types/set/btreeset.rs b/src/asn1_types/set/btreeset.rs index a70f9f9..f852aba 100644 --- a/src/asn1_types/set/btreeset.rs +++ b/src/asn1_types/set/btreeset.rs @@ -1,6 +1,8 @@ use crate::*; use alloc::collections::BTreeSet; -use core::convert::TryFrom; +use core::{convert::TryFrom, fmt::Debug}; + +use self::debug::{trace, trace_generic}; impl Tagged for BTreeSet { const TAG: Tag = Tag::Set; @@ -14,10 +16,18 @@ where type Error = Error; fn try_from(any: Any<'a>) -> Result { - any.tag().assert_eq(Self::TAG)?; - any.header.assert_constructed()?; - let items = SetIterator::::new(any.data).collect::>>()?; - Ok(items) + trace_generic( + core::any::type_name::(), + "BTreeSet::from_der", + |any| { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + let items = + SetIterator::::new(any.data).collect::>>()?; + Ok(items) + }, + any, + ) } } @@ -41,20 +51,28 @@ impl<'a, T, E> FromDer<'a, E> for BTreeSet where T: FromDer<'a, E>, T: Ord, - E: From, + E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { - let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; - any.tag() - .assert_eq(Self::TAG) - .map_err(|e| nom::Err::Error(e.into()))?; - any.header - .assert_constructed() - .map_err(|e| nom::Err::Error(e.into()))?; - let items = SetIterator::::new(any.data) - .collect::, E>>() - .map_err(nom::Err::Error)?; - Ok((rem, items)) + trace_generic( + core::any::type_name::(), + "BTreeSet::from_der", + |bytes| { + let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) + .map_err(Err::convert)?; + any.tag() + .assert_eq(Self::TAG) + .map_err(|e| Err::Error(e.into()))?; + any.header + .assert_constructed() + .map_err(|e| Err::Error(e.into()))?; + let items = SetIterator::::new(any.data) + .collect::, E>>() + .map_err(Err::Error)?; + Ok((rem, items)) + }, + bytes, + ) } } diff --git a/src/asn1_types/set/hashset.rs b/src/asn1_types/set/hashset.rs index d505301..752b941 100644 --- a/src/asn1_types/set/hashset.rs +++ b/src/asn1_types/set/hashset.rs @@ -1,9 +1,12 @@ #![cfg(feature = "std")] use crate::*; +use core::fmt::Debug; use std::collections::HashSet; use std::convert::TryFrom; use std::hash::Hash; +use self::debug::{trace, trace_generic}; + impl Tagged for HashSet { const TAG: Tag = Tag::Set; } @@ -43,20 +46,28 @@ impl<'a, T, E> FromDer<'a, E> for HashSet where T: FromDer<'a, E>, T: Hash + Eq, - E: From, + E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { - let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; - any.tag() - .assert_eq(Self::TAG) - .map_err(|e| nom::Err::Error(e.into()))?; - any.header - .assert_constructed() - .map_err(|e| nom::Err::Error(e.into()))?; - let items = SetIterator::::new(any.data) - .collect::, E>>() - .map_err(nom::Err::Error)?; - Ok((rem, items)) + trace_generic( + core::any::type_name::(), + "BTreeSet::from_der", + |bytes| { + let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) + .map_err(Err::convert)?; + any.tag() + .assert_eq(Self::TAG) + .map_err(|e| Err::Error(e.into()))?; + any.header + .assert_constructed() + .map_err(|e| Err::Error(e.into()))?; + let items = SetIterator::::new(any.data) + .collect::, E>>() + .map_err(Err::Error)?; + Ok((rem, items)) + }, + bytes, + ) } } diff --git a/src/asn1_types/set/set_of.rs b/src/asn1_types/set/set_of.rs index 4339d64..c2846ff 100644 --- a/src/asn1_types/set/set_of.rs +++ b/src/asn1_types/set/set_of.rs @@ -1,9 +1,13 @@ use crate::*; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; +use core::fmt::{Debug, Display}; use core::iter::FromIterator; use core::ops::{Deref, DerefMut}; +use self::debug::{trace, trace_generic}; + /// The `SET OF` object is an unordered list of homogeneous types. /// /// This type implements `Deref` and `DerefMut`, so all methods @@ -128,17 +132,25 @@ where impl<'a, T, E> FromDer<'a, E> for SetOf where T: FromDer<'a, E>, - E: From, + E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult { - let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; - any.header - .assert_tag(Self::TAG) - .map_err(|e| nom::Err::Error(e.into()))?; - let items = SetIterator::::new(any.data) - .collect::, E>>() - .map_err(nom::Err::Error)?; - Ok((rem, SetOf::new(items))) + trace_generic( + core::any::type_name::(), + "SetOf::from_der", + |bytes| { + let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) + .map_err(Err::convert)?; + any.header + .assert_tag(Self::TAG) + .map_err(|e| Err::Error(e.into()))?; + let items = SetIterator::::new(any.data) + .collect::, E>>() + .map_err(Err::Error)?; + Ok((rem, SetOf::new(items))) + }, + bytes, + ) } } diff --git a/src/asn1_types/strings/bmpstring.rs b/src/asn1_types/strings/bmpstring.rs index 5fdaa7a..ff69ba4 100644 --- a/src/asn1_types/strings/bmpstring.rs +++ b/src/asn1_types/strings/bmpstring.rs @@ -3,7 +3,9 @@ use crate::*; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; /// ASN.1 `BMPSTRING` type @@ -42,7 +44,7 @@ impl<'a> From<&'a str> for BmpString<'a> { impl From for BmpString<'_> { fn from(s: String) -> Self { Self { - data: alloc::borrow::Cow::Owned(s), + data: Cow::Owned(s), } } } diff --git a/src/asn1_types/strings/generalstring.rs b/src/asn1_types/strings/generalstring.rs index f455d6a..d176abc 100644 --- a/src/asn1_types/strings/generalstring.rs +++ b/src/asn1_types/strings/generalstring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(GeneralString); diff --git a/src/asn1_types/strings/graphicstring.rs b/src/asn1_types/strings/graphicstring.rs index 3d84040..df4e56a 100644 --- a/src/asn1_types/strings/graphicstring.rs +++ b/src/asn1_types/strings/graphicstring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(GraphicString); diff --git a/src/asn1_types/strings/ia5string.rs b/src/asn1_types/strings/ia5string.rs index 4b37465..0fbf213 100644 --- a/src/asn1_types/strings/ia5string.rs +++ b/src/asn1_types/strings/ia5string.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(Ia5String); diff --git a/src/asn1_types/strings/numericstring.rs b/src/asn1_types/strings/numericstring.rs index dbe4ff1..e0462a1 100644 --- a/src/asn1_types/strings/numericstring.rs +++ b/src/asn1_types/strings/numericstring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(NumericString); diff --git a/src/asn1_types/strings/printablestring.rs b/src/asn1_types/strings/printablestring.rs index 5cd51fa..981704a 100644 --- a/src/asn1_types/strings/printablestring.rs +++ b/src/asn1_types/strings/printablestring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(PrintableString); diff --git a/src/asn1_types/strings/string.rs b/src/asn1_types/strings/string.rs index 81f6ba8..f4e0e2e 100644 --- a/src/asn1_types/strings/string.rs +++ b/src/asn1_types/strings/string.rs @@ -1,4 +1,5 @@ use crate::*; +#[cfg(not(feature = "std"))] use alloc::string::String; use core::convert::TryFrom; diff --git a/src/asn1_types/strings/teletexstring.rs b/src/asn1_types/strings/teletexstring.rs index 2f58804..590b5e9 100644 --- a/src/asn1_types/strings/teletexstring.rs +++ b/src/asn1_types/strings/teletexstring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(TeletexString); diff --git a/src/asn1_types/strings/universalstring.rs b/src/asn1_types/strings/universalstring.rs index 9006362..ae30612 100644 --- a/src/asn1_types/strings/universalstring.rs +++ b/src/asn1_types/strings/universalstring.rs @@ -3,7 +3,9 @@ use crate::*; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; use core::iter::FromIterator; @@ -43,7 +45,7 @@ impl<'a> From<&'a str> for UniversalString<'a> { impl From for UniversalString<'_> { fn from(s: String) -> Self { Self { - data: alloc::borrow::Cow::Owned(s), + data: Cow::Owned(s), } } } diff --git a/src/asn1_types/strings/utf8string.rs b/src/asn1_types/strings/utf8string.rs index 0bf87d4..78eae12 100644 --- a/src/asn1_types/strings/utf8string.rs +++ b/src/asn1_types/strings/utf8string.rs @@ -1,6 +1,7 @@ use crate::asn1_string; use crate::Result; use crate::TestValidCharset; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(Utf8String); diff --git a/src/asn1_types/strings/videotexstring.rs b/src/asn1_types/strings/videotexstring.rs index 51c5a46..d9324d6 100644 --- a/src/asn1_types/strings/videotexstring.rs +++ b/src/asn1_types/strings/videotexstring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(VideotexString); diff --git a/src/asn1_types/strings/visiblestring.rs b/src/asn1_types/strings/visiblestring.rs index 2b141dc..cb72348 100644 --- a/src/asn1_types/strings/visiblestring.rs +++ b/src/asn1_types/strings/visiblestring.rs @@ -1,5 +1,6 @@ use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(VisibleString); diff --git a/src/asn1_types/tagged/helpers.rs b/src/asn1_types/tagged/helpers.rs index dfb1018..5025785 100644 --- a/src/asn1_types/tagged/helpers.rs +++ b/src/asn1_types/tagged/helpers.rs @@ -34,7 +34,7 @@ where parse_der_container(tag, move |any: Any<'a>| { any.header .assert_tag(tag) - .map_err(|e| nom::Err::convert(e.into()))?; + .map_err(|e| Err::convert(e.into()))?; f(any.data, any.header) }) } @@ -51,9 +51,7 @@ where let tag = tag.into(); move |i| { let (rem, tagged) = TaggedParser::from_der(i)?; - tagged - .assert_tag(tag) - .map_err(|e| nom::Err::convert(e.into()))?; + tagged.assert_tag(tag).map_err(|e| Err::convert(e.into()))?; Ok((rem, tagged)) } } @@ -73,7 +71,7 @@ where // verify tag of external header any.header .assert_tag(tag) - .map_err(|e| nom::Err::convert(e.into()))?; + .map_err(|e| Err::convert(e.into()))?; // build a fake header with the expected tag let Any { header, data } = any; let header = Header { @@ -93,10 +91,10 @@ where E: ParseError<&'a [u8]> + From, { move |i: &[u8]| { - let (rem, any) = Any::from_der(i).map_err(nom::Err::convert)?; + let (rem, any) = Any::from_der(i).map_err(Err::convert)?; any.header .assert_tag(tag) - .map_err(|e| nom::Err::convert(e.into()))?; + .map_err(|e| Err::convert(e.into()))?; let (_, output) = f(any)?; Ok((rem, output)) } diff --git a/src/asn1_types/tagged/implicit.rs b/src/asn1_types/tagged/implicit.rs index 5594e49..01e6ae5 100644 --- a/src/asn1_types/tagged/implicit.rs +++ b/src/asn1_types/tagged/implicit.rs @@ -74,7 +74,7 @@ where }; match T::try_from(any) { Ok(inner) => Ok((rem, TaggedValue::implicit(inner))), - Err(e) => Err(nom::Err::Error(e)), + Err(e) => Err(Err::Error(e)), } } } @@ -182,7 +182,7 @@ where }; Ok((rem, tagged_value)) } - Err(e) => Err(nom::Err::Error(e)), + Err(e) => Err(Err::Error(e)), } } } @@ -217,7 +217,7 @@ where }, data, }; - T::check_constraints(&any).map_err(|e| nom::Err::Error(e.into()))?; + T::check_constraints(&any).map_err(|e| Err::Error(e.into()))?; match T::try_from(any) { Ok(t) => { let tagged_value = TaggedParser { @@ -228,7 +228,7 @@ where }; Ok((rem, tagged_value)) } - Err(e) => Err(nom::Err::Error(e)), + Err(e) => Err(Err::Error(e)), } } } diff --git a/src/asn1_types/tagged/optional.rs b/src/asn1_types/tagged/optional.rs index 00504df..dcf8307 100644 --- a/src/asn1_types/tagged/optional.rs +++ b/src/asn1_types/tagged/optional.rs @@ -1,4 +1,3 @@ -use super::{explicit::TaggedExplicit, implicit::TaggedImplicit}; use crate::*; /// Helper object to parse TAGGED OPTIONAL types (explicit or implicit) diff --git a/src/asn1_types/utctime.rs b/src/asn1_types/utctime.rs index 8604914..3a67598 100644 --- a/src/asn1_types/utctime.rs +++ b/src/asn1_types/utctime.rs @@ -1,4 +1,3 @@ -use crate::datetime::decode_decimal; use crate::*; use core::convert::TryFrom; use core::fmt; diff --git a/src/datetime.rs b/src/datetime.rs index f3a48b7..33125ae 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,5 +1,6 @@ use crate::{Result, Tag}; use alloc::format; +#[cfg(not(feature = "std"))] use alloc::string::ToString; use core::fmt; #[cfg(feature = "datetime")] diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 0000000..aa0c707 --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,244 @@ +use crate::ParseResult; + +#[macro_export] +macro_rules! debug_eprintln { + ($msg: expr, $( $args:expr ),* ) => { + #[cfg(feature = "debug")] + { + use colored::Colorize; + let s = $msg.to_string().green(); + eprintln!("{} {}", s, format!($($args),*)); + } + }; +} + +#[macro_export] +macro_rules! trace_eprintln { + ($msg: expr, $( $args:expr ),* ) => { + #[cfg(feature = "trace")] + { + use colored::Colorize; + let s = $msg.to_string().green(); + eprintln!("{} {}", s, format!($($args),*)); + } + }; +} + +#[cfg(feature = "debug")] +fn eprintln_hex_dump(bytes: &[u8], max_len: usize) { + use core::cmp::min; + use nom::HexDisplay; + + let m = min(bytes.len(), max_len); + eprint!("{}", &bytes[..m].to_hex(16)); + if bytes.len() > max_len { + eprintln!("... "); + } +} + +#[cfg(not(feature = "debug"))] +#[inline] +pub fn trace_generic(_msg: &str, _fname: &str, f: F, input: I) -> Result +where + F: Fn(I) -> Result, +{ + f(input) +} + +#[cfg(feature = "debug")] +pub fn trace_generic(msg: &str, fname: &str, f: F, input: I) -> Result +where + F: Fn(I) -> Result, + E: core::fmt::Display, +{ + trace_eprintln!(msg, "⤷ {}", fname); + let output = f(input); + match &output { + Err(e) => { + debug_eprintln!(msg, "↯ {} failed: {}", fname, e.to_string().red()); + } + _ => { + debug_eprintln!(msg, "⤶ {}", fname); + } + } + output +} + +#[cfg(not(feature = "debug"))] +#[inline] +pub fn trace<'a, T, E, F>(_msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E> +where + F: Fn(&'a [u8]) -> ParseResult<'a, T, E>, +{ + f(input) +} + +#[cfg(feature = "debug")] +pub fn trace<'a, T, E, F>(msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E> +where + F: Fn(&'a [u8]) -> ParseResult<'a, T, E>, +{ + trace_eprintln!( + msg, + "⤷ input (len={}, type={})", + input.len(), + core::any::type_name::() + ); + let res = f(input); + match &res { + Ok((_rem, _)) => { + trace_eprintln!( + msg, + "⤶ Parsed {} bytes, {} remaining", + input.len() - _rem.len(), + _rem.len() + ); + } + Err(_) => { + // NOTE: we do not need to print error, caller should print it + debug_eprintln!(msg, "↯ Parsing failed at location:"); + eprintln_hex_dump(input, 16); + } + } + res +} + +#[cfg(feature = "debug")] +#[cfg(test)] +mod tests { + + use std::collections::HashSet; + + use crate::*; + use alloc::collections::BTreeSet; + use hex_literal::hex; + + #[test] + fn debug_from_ber_any() { + assert!(Any::from_ber(&hex!("01 01 ff")).is_ok()); + } + + #[test] + fn debug_from_ber_failures() { + // wrong type + eprintln!("--"); + assert!(>::from_ber(&hex!("02 01 00")).is_err()); + } + + #[test] + fn debug_from_ber_sequence_indefinite() { + let input = &hex!("30 80 02 03 01 00 01 00 00"); + let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..7]); + assert_eq!(rem, &[]); + eprintln!("--"); + let (rem, result) = >::from_ber(input).expect("parsing failed"); + assert_eq!(&result, &[65537]); + assert_eq!(rem, &[]); + } + + #[test] + fn debug_from_ber_sequence_of() { + // parsing failure (wrong type) + let input = &hex!("30 03 01 01 00"); + eprintln!("--"); + let _ = >::from_ber(input).expect_err("parsing should fail"); + eprintln!("--"); + let _ = >::from_ber(input).expect_err("parsing should fail"); + } + + #[test] + fn debug_from_ber_u32() { + assert!(u32::from_ber(&hex!("02 01 01")).is_ok()); + } + + #[test] + fn debug_from_der_any() { + assert!(Any::from_der(&hex!("01 01 ff")).is_ok()); + } + + #[test] + fn debug_from_der_failures() { + use crate::Sequence; + + // parsing any failed + eprintln!("--"); + assert!(u16::from_der(&hex!("ff 00")).is_err()); + // Indefinite length + eprintln!("--"); + assert!(u16::from_der(&hex!("30 80 00 00")).is_err()); + // DER constraints failed + eprintln!("--"); + assert!(bool::from_der(&hex!("01 01 7f")).is_err()); + // Incomplete sequence + eprintln!("--"); + let _ = Sequence::from_der(&hex!("30 81 04 00 00")); + } + + #[test] + fn debug_from_der_sequence() { + // parsing OK, recursive + let input = &hex!("30 08 02 03 01 00 01 02 01 01"); + let (rem, result) = >::from_der(input).expect("parsing failed"); + assert_eq!(&result, &[65537, 1]); + assert_eq!(rem, &[]); + } + + #[test] + fn debug_from_der_sequence_fail() { + // tag is wrong + let input = &hex!("31 03 01 01 44"); + let _ = >::from_der(input).expect_err("parsing should fail"); + // sequence is ok but contraint fails on element + let input = &hex!("30 03 01 01 44"); + let _ = >::from_der(input).expect_err("parsing should fail"); + } + + #[test] + fn debug_from_der_sequence_of() { + use crate::SequenceOf; + // parsing failure (wrong type) + let input = &hex!("30 03 01 01 00"); + eprintln!("--"); + let _ = >::from_der(input).expect_err("parsing should fail"); + eprintln!("--"); + let _ = >::from_der(input).expect_err("parsing should fail"); + } + + #[test] + fn debug_from_der_set_fail() { + // set is ok but contraint fails on element + let input = &hex!("31 03 01 01 44"); + let _ = >::from_der(input).expect_err("parsing should fail"); + } + + #[test] + fn debug_from_der_set_of() { + use crate::SetOf; + use alloc::collections::BTreeSet; + + // parsing failure (wrong type) + let input = &hex!("31 03 01 01 00"); + eprintln!("--"); + let _ = >::from_der(input).expect_err("parsing should fail"); + eprintln!("--"); + let _ = >::from_der(input).expect_err("parsing should fail"); + eprintln!("--"); + let _ = >::from_der(input).expect_err("parsing should fail"); + } + + #[test] + fn debug_from_der_string_ok() { + let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); + let (rem, result) = Utf8String::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), "Some-State"); + assert_eq!(rem, &[]); + } + + #[test] + fn debug_from_der_string_fail() { + // wrong charset + let input = &hex!("12 03 41 42 43"); + let _ = NumericString::from_der(input).expect_err("parsing should fail"); + } +} diff --git a/src/error.rs b/src/error.rs index e1414c3..8ec409a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ use crate::{Class, Tag}; use alloc::str; use alloc::string; +#[cfg(not(feature = "std"))] use alloc::string::String; use displaydoc::Display; use nom::error::{ErrorKind, FromExternalError, ParseError}; @@ -90,7 +91,7 @@ pub enum Error { /// Invalid Date or Time InvalidDateTime, - /// DER Failed constraint + /// DER Failed constraint: {0:?} DerConstraintFailed(DerConstraint), /// Requesting borrowed data from a temporary object diff --git a/src/header.rs b/src/header.rs index c2b9aa5..7291cb7 100644 --- a/src/header.rs +++ b/src/header.rs @@ -248,17 +248,17 @@ impl<'a> FromBer<'a> for Header<'a> { (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { - return Err(::nom::Err::Error(Error::InvalidLength)); + return Err(nom::Err::Error(Error::InvalidLength)); } let (i3, llen) = take(l1)(i2)?; match bytes_to_u64(llen) { Ok(l) => { let l = - usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; + usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { - return Err(::nom::Err::Error(Error::InvalidLength)); + return Err(nom::Err::Error(Error::InvalidLength)); } } } @@ -284,14 +284,14 @@ impl<'a> FromDer<'a> for Header<'a> { } (_, 0) => { // Indefinite form is not allowed in DER (10.1) - return Err(::nom::Err::Error(Error::DerConstraintFailed( + return Err(nom::Err::Error(Error::DerConstraintFailed( DerConstraint::IndefiniteLength, ))); } (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { - return Err(::nom::Err::Error(Error::InvalidLength)); + return Err(nom::Err::Error(Error::InvalidLength)); } // DER(9.1) if len is 0 (indefinite form), obj must be constructed der_constraint_fail_if!( @@ -305,11 +305,11 @@ impl<'a> FromDer<'a> for Header<'a> { // DER: should have been encoded in short form (< 127) // XXX der_constraint_fail_if!(i, l < 127); let l = - usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; + usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { - return Err(::nom::Err::Error(Error::InvalidLength)); + return Err(nom::Err::Error(Error::InvalidLength)); } } } diff --git a/src/lib.rs b/src/lib.rs index 4b6e250..4d65dcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,7 @@ mod asn1_types; mod ber; mod class; mod datetime; +mod debug; mod derive; mod error; mod header; diff --git a/src/tag.rs b/src/tag.rs index 8ce7c57..d610a2f 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -1,4 +1,5 @@ use crate::{Error, Result}; +#[cfg(not(feature = "std"))] use alloc::string::ToString; use rusticata_macros::newtype_enum; diff --git a/src/traits.rs b/src/traits.rs index eea4cb9..c0666bc 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,6 +1,8 @@ +use crate::debug::{trace, trace_generic}; use crate::error::*; -use crate::{Any, Class, Explicit, Implicit, Tag, TaggedParser}; +use crate::{parse_der_any, Any, Class, Explicit, Implicit, Tag, TaggedParser}; use core::convert::{TryFrom, TryInto}; +use core::fmt::{Debug, Display}; #[cfg(feature = "std")] use std::io::Write; @@ -178,15 +180,22 @@ where T: TryFrom, Error = E>, T: CheckDerConstraints, T: DerAutoDerive, - E: From, + E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult { - // Note: Any::from_der checks than length is definite - let (i, any) = Any::from_der(bytes).map_err(nom::Err::convert)?; - ::check_constraints(&any) - .map_err(|e| nom::Err::Error(e.into()))?; - let result = any.try_into().map_err(nom::Err::Error)?; - Ok((i, result)) + trace_generic( + core::any::type_name::(), + "Any::from_der", + |bytes| { + let (i, any) = trace(core::any::type_name::(), parse_der_any, bytes) + .map_err(nom::Err::convert)?; + ::check_constraints(&any) + .map_err(|e| nom::Err::Error(e.into()))?; + let result = any.try_into().map_err(nom::Err::Error)?; + Ok((i, result)) + }, + bytes, + ) } } diff --git a/tests/der.rs b/tests/der.rs index 5fd87d7..5f6b682 100644 --- a/tests/der.rs +++ b/tests/der.rs @@ -205,6 +205,18 @@ fn from_der_null() { assert_eq!(rem, &[0xff, 0xff]); } +#[test] +fn from_der_numericstring() { + // + let input = &hex!("12 03 31 32 33"); + let (rem, result) = NumericString::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), "123"); + assert_eq!(rem, &[]); + // wrong charset + let input = &hex!("12 03 41 42 43"); + let _ = NumericString::from_der(input).expect_err("parsing should fail"); +} + #[test] fn from_der_octetstring() { // coverage