summaryrefslogtreecommitdiff
path: root/src/asn1_types/integer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/asn1_types/integer.rs')
-rw-r--r--src/asn1_types/integer.rs712
1 files changed, 712 insertions, 0 deletions
diff --git a/src/asn1_types/integer.rs b/src/asn1_types/integer.rs
new file mode 100644
index 0000000..59f846a
--- /dev/null
+++ b/src/asn1_types/integer.rs
@@ -0,0 +1,712 @@
+use crate::*;
+use alloc::borrow::Cow;
+use alloc::vec;
+use core::convert::{TryFrom, TryInto};
+
+#[cfg(feature = "bigint")]
+#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
+pub use num_bigint::{BigInt, BigUint, Sign};
+
+/// Decode an unsigned integer into a big endian byte slice with all leading
+/// zeroes removed (if positive) and extra 0xff remove (if negative)
+fn trim_slice<'a>(any: &'a Any<'_>) -> Result<&'a [u8]> {
+ let bytes = any.data;
+
+ if bytes.is_empty() || (bytes[0] != 0x00 && bytes[0] != 0xff) {
+ return Ok(bytes);
+ }
+
+ match bytes.iter().position(|&b| b != 0) {
+ // first byte is not 0
+ Some(0) => (),
+ // all bytes are 0
+ None => return Ok(&bytes[bytes.len() - 1..]),
+ Some(first) => return Ok(&bytes[first..]),
+ }
+
+ // same for negative integers : skip byte 0->n if byte 0->n = 0xff AND byte n+1 >= 0x80
+ match bytes.windows(2).position(|s| match s {
+ &[a, b] => !(a == 0xff && b >= 0x80),
+ _ => true,
+ }) {
+ // first byte is not 0xff
+ Some(0) => (),
+ // all bytes are 0xff
+ None => return Ok(&bytes[bytes.len() - 1..]),
+ Some(first) => return Ok(&bytes[first..]),
+ }
+
+ Ok(bytes)
+}
+
+/// Decode an unsigned integer into a byte array of the requested size
+/// containing a big endian integer.
+fn decode_array_uint<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> {
+ if is_highest_bit_set(any.data) {
+ return Err(Error::IntegerNegative);
+ }
+ let input = trim_slice(any)?;
+
+ if input.len() > N {
+ return Err(Error::IntegerTooLarge);
+ }
+
+ // Input has leading zeroes removed, so we need to add them back
+ let mut output = [0u8; N];
+ assert!(input.len() <= N);
+ output[N.saturating_sub(input.len())..].copy_from_slice(input);
+ Ok(output)
+}
+
+/// Decode an unsigned integer of the specified size.
+///
+/// Returns a byte array of the requested size containing a big endian integer.
+fn decode_array_int<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> {
+ if any.data.len() > N {
+ return Err(Error::IntegerTooLarge);
+ }
+
+ // any.tag().assert_eq(Tag::Integer)?;
+ let mut output = [0xFFu8; N];
+ let offset = N.saturating_sub(any.as_bytes().len());
+ output[offset..].copy_from_slice(any.as_bytes());
+ Ok(output)
+}
+
+/// Is the highest bit of the first byte in the slice 1? (if present)
+#[inline]
+fn is_highest_bit_set(bytes: &[u8]) -> bool {
+ bytes
+ .first()
+ .map(|byte| byte & 0b10000000 != 0)
+ .unwrap_or(false)
+}
+
+macro_rules! impl_int {
+ ($uint:ty => $int:ty) => {
+ impl<'a> TryFrom<Any<'a>> for $int {
+ type Error = Error;
+
+ fn try_from(any: Any<'a>) -> Result<Self> {
+ TryFrom::try_from(&any)
+ }
+ }
+
+ impl<'a, 'b> TryFrom<&'b Any<'a>> for $int {
+ type Error = Error;
+
+ fn try_from(any: &'b Any<'a>) -> Result<Self> {
+ 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)
+ }
+ }
+
+ impl CheckDerConstraints for $int {
+ fn check_constraints(any: &Any) -> Result<()> {
+ check_der_int_constraints(any)
+ }
+ }
+
+ impl DerAutoDerive for $int {}
+
+ impl Tagged for $int {
+ const TAG: Tag = Tag::Integer;
+ }
+
+ #[cfg(feature = "std")]
+ impl ToDer for $int {
+ fn to_der_len(&self) -> Result<usize> {
+ let int = Integer::from(*self);
+ int.to_der_len()
+ }
+
+ fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der(writer)
+ }
+
+ fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der_header(writer)
+ }
+
+ fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der_content(writer)
+ }
+ }
+ };
+}
+
+macro_rules! impl_uint {
+ ($ty:ty) => {
+ impl<'a> TryFrom<Any<'a>> for $ty {
+ type Error = Error;
+
+ fn try_from(any: Any<'a>) -> Result<Self> {
+ TryFrom::try_from(&any)
+ }
+ }
+ impl<'a, 'b> TryFrom<&'b Any<'a>> for $ty {
+ type Error = Error;
+
+ fn try_from(any: &'b Any<'a>) -> Result<Self> {
+ any.tag().assert_eq(Self::TAG)?;
+ any.header.assert_primitive()?;
+ let result = Self::from_be_bytes(decode_array_uint(any)?);
+ Ok(result)
+ }
+ }
+ impl CheckDerConstraints for $ty {
+ fn check_constraints(any: &Any) -> Result<()> {
+ check_der_int_constraints(any)
+ }
+ }
+
+ impl DerAutoDerive for $ty {}
+
+ impl Tagged for $ty {
+ const TAG: Tag = Tag::Integer;
+ }
+
+ #[cfg(feature = "std")]
+ impl ToDer for $ty {
+ fn to_der_len(&self) -> Result<usize> {
+ let int = Integer::from(*self);
+ int.to_der_len()
+ }
+
+ fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der(writer)
+ }
+
+ fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der_header(writer)
+ }
+
+ fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
+ let int = Integer::from(*self);
+ int.write_der_content(writer)
+ }
+ }
+ };
+}
+
+impl_uint!(u8);
+impl_uint!(u16);
+impl_uint!(u32);
+impl_uint!(u64);
+impl_uint!(u128);
+impl_int!(u8 => i8);
+impl_int!(u16 => i16);
+impl_int!(u32 => i32);
+impl_int!(u64 => i64);
+impl_int!(u128 => i128);
+
+/// ASN.1 `INTEGER` type
+///
+/// Generic representation for integer types.
+/// BER/DER integers can be of any size, so it is not possible to store them as simple integers (they
+/// are stored as raw bytes).
+///
+/// The internal representation can be obtained using `.as_ref()`.
+///
+/// # Note
+///
+/// Methods from/to BER and DER encodings are also implemented for primitive types
+/// (`u8`, `u16` to `u128`, and `i8` to `i128`).
+/// In most cases, it is easier to use these types directly.
+///
+/// # Examples
+///
+/// Creating an `Integer`
+///
+/// ```
+/// use asn1_rs::Integer;
+///
+/// // unsigned
+/// let i = Integer::from(4);
+/// assert_eq!(i.as_ref(), &[4]);
+/// // signed
+/// let j = Integer::from(-2);
+/// assert_eq!(j.as_ref(), &[0xfe]);
+/// ```
+///
+/// Converting an `Integer` to a primitive type (using the `TryInto` trait)
+///
+/// ```
+/// use asn1_rs::{Error, Integer};
+/// use std::convert::TryInto;
+///
+/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]);
+/// // converts to an u32
+/// let n: u32 = i.try_into().unwrap();
+///
+/// // Same, but converting to an u16: will fail, value cannot fit into an u16
+/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]);
+/// assert_eq!(i.try_into() as Result<u16, _>, Err(Error::IntegerTooLarge));
+/// ```
+///
+/// Encoding an `Integer` to DER
+///
+/// ```
+/// use asn1_rs::{Integer, ToDer};
+///
+/// let i = Integer::from(4);
+/// let v = i.to_der_vec().unwrap();
+/// assert_eq!(&v, &[2, 1, 4]);
+///
+/// // same, with primitive types
+/// let v = 4.to_der_vec().unwrap();
+/// assert_eq!(&v, &[2, 1, 4]);
+/// ```
+#[derive(Debug, Eq, PartialEq)]
+pub struct Integer<'a> {
+ pub(crate) data: Cow<'a, [u8]>,
+}
+
+impl<'a> Integer<'a> {
+ /// Creates a new `Integer` containing the given value (borrowed).
+ #[inline]
+ pub const fn new(s: &'a [u8]) -> Self {
+ Integer {
+ data: Cow::Borrowed(s),
+ }
+ }
+
+ /// Creates a borrowed `Any` for this object
+ #[inline]
+ pub fn any(&'a self) -> Any<'a> {
+ Any::from_tag_and_data(Self::TAG, &self.data)
+ }
+
+ /// Returns a `BigInt` built from this `Integer` value.
+ #[cfg(feature = "bigint")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
+ pub fn as_bigint(&self) -> BigInt {
+ BigInt::from_signed_bytes_be(&self.data)
+ }
+
+ /// Returns a `BigUint` built from this `Integer` value.
+ #[cfg(feature = "bigint")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
+ pub fn as_biguint(&self) -> Result<BigUint> {
+ if is_highest_bit_set(&self.data) {
+ Err(Error::IntegerNegative)
+ } else {
+ Ok(BigUint::from_bytes_be(&self.data))
+ }
+ }
+
+ /// Build an `Integer` from a constant array of bytes representation of an integer.
+ pub fn from_const_array<const N: usize>(b: [u8; N]) -> Self {
+ // if high bit set -> add leading 0 to ensure unsigned
+ if is_highest_bit_set(&b) {
+ let mut bytes = vec![0];
+ bytes.extend_from_slice(&b);
+
+ Integer {
+ data: Cow::Owned(bytes),
+ }
+ }
+ // otherwise -> remove 0 unless next has high bit set
+ else {
+ let mut idx = 0;
+
+ while idx < b.len() - 1 {
+ if b[idx] == 0 && b[idx + 1] < 0x80 {
+ idx += 1;
+ continue;
+ }
+ break;
+ }
+
+ Integer {
+ data: Cow::Owned(b[idx..].to_vec()),
+ }
+ }
+ }
+
+ fn from_const_array_negative<const N: usize>(b: [u8; N]) -> Self {
+ let mut idx = 0;
+
+ // Skip leading FF unless next has high bit clear
+ while idx < b.len() - 1 {
+ if b[idx] == 0xFF && b[idx + 1] >= 0x80 {
+ idx += 1;
+ continue;
+ }
+ break;
+ }
+
+ if idx == b.len() {
+ Integer {
+ data: Cow::Borrowed(&[0]),
+ }
+ } else {
+ Integer {
+ data: Cow::Owned(b[idx..].to_vec()),
+ }
+ }
+ }
+}
+
+macro_rules! impl_from_to {
+ ($ty:ty, $sty:expr, $from:ident, $to:ident) => {
+ impl From<$ty> for Integer<'_> {
+ fn from(i: $ty) -> Self {
+ Self::$from(i)
+ }
+ }
+
+ impl TryFrom<Integer<'_>> for $ty {
+ type Error = Error;
+
+ fn try_from(value: Integer<'_>) -> Result<Self> {
+ value.$to()
+ }
+ }
+
+ impl Integer<'_> {
+ #[doc = "Attempts to convert an `Integer` to a `"]
+ #[doc = $sty]
+ #[doc = "`."]
+ #[doc = ""]
+ #[doc = "This function returns an `IntegerTooLarge` error if the integer will not fit into the output type."]
+ pub fn $to(&self) -> Result<$ty> {
+ self.any().try_into()
+ }
+ }
+ };
+ (IMPL SIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => {
+ impl_from_to!($ty, $sty, $from, $to);
+
+ impl Integer<'_> {
+ #[doc = "Converts a `"]
+ #[doc = $sty]
+ #[doc = "` to an `Integer`"]
+ #[doc = ""]
+ #[doc = "Note: this function allocates data."]
+ pub fn $from(i: $ty) -> Self {
+ let b = i.to_be_bytes();
+ if i >= 0 {
+ Self::from_const_array(b)
+ } else {
+ Self::from_const_array_negative(b)
+ }
+ }
+ }
+ };
+ (IMPL UNSIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => {
+ impl_from_to!($ty, $sty, $from, $to);
+
+ impl Integer<'_> {
+ #[doc = "Converts a `"]
+ #[doc = $sty]
+ #[doc = "` to an `Integer`"]
+ #[doc = ""]
+ #[doc = "Note: this function allocates data."]
+ pub fn $from(i: $ty) -> Self {
+ Self::from_const_array(i.to_be_bytes())
+ }
+ }
+ };
+ (SIGNED $ty:ty, $from:ident, $to:ident) => {
+ impl_from_to!(IMPL SIGNED $ty, stringify!($ty), $from, $to);
+ };
+ (UNSIGNED $ty:ty, $from:ident, $to:ident) => {
+ impl_from_to!(IMPL UNSIGNED $ty, stringify!($ty), $from, $to);
+ };
+}
+
+impl_from_to!(SIGNED i8, from_i8, as_i8);
+impl_from_to!(SIGNED i16, from_i16, as_i16);
+impl_from_to!(SIGNED i32, from_i32, as_i32);
+impl_from_to!(SIGNED i64, from_i64, as_i64);
+impl_from_to!(SIGNED i128, from_i128, as_i128);
+
+impl_from_to!(UNSIGNED u8, from_u8, as_u8);
+impl_from_to!(UNSIGNED u16, from_u16, as_u16);
+impl_from_to!(UNSIGNED u32, from_u32, as_u32);
+impl_from_to!(UNSIGNED u64, from_u64, as_u64);
+impl_from_to!(UNSIGNED u128, from_u128, as_u128);
+
+impl<'a> AsRef<[u8]> for Integer<'a> {
+ fn as_ref(&self) -> &[u8] {
+ &self.data
+ }
+}
+
+impl<'a> TryFrom<Any<'a>> for Integer<'a> {
+ type Error = Error;
+
+ fn try_from(any: Any<'a>) -> Result<Integer<'a>> {
+ TryFrom::try_from(&any)
+ }
+}
+
+impl<'a, 'b> TryFrom<&'b Any<'a>> for Integer<'a> {
+ type Error = Error;
+
+ fn try_from(any: &'b Any<'a>) -> Result<Integer<'a>> {
+ any.tag().assert_eq(Self::TAG)?;
+ Ok(Integer {
+ data: Cow::Borrowed(any.data),
+ })
+ }
+}
+
+impl<'a> CheckDerConstraints for Integer<'a> {
+ fn check_constraints(any: &Any) -> Result<()> {
+ check_der_int_constraints(any)
+ }
+}
+
+fn check_der_int_constraints(any: &Any) -> Result<()> {
+ any.header.assert_primitive()?;
+ any.header.length.assert_definite()?;
+ match any.as_bytes() {
+ [] => Err(Error::DerConstraintFailed(DerConstraint::IntegerEmpty)),
+ [0] => Ok(()),
+ // leading zeroes
+ [0, byte, ..] if *byte < 0x80 => Err(Error::DerConstraintFailed(
+ DerConstraint::IntegerLeadingZeroes,
+ )),
+ // negative integer with non-minimal encoding
+ [0xff, byte, ..] if *byte >= 0x80 => {
+ Err(Error::DerConstraintFailed(DerConstraint::IntegerLeadingFF))
+ }
+ _ => Ok(()),
+ }
+}
+
+impl DerAutoDerive for Integer<'_> {}
+
+impl<'a> Tagged for Integer<'a> {
+ const TAG: Tag = Tag::Integer;
+}
+
+#[cfg(feature = "std")]
+impl ToDer for Integer<'_> {
+ fn to_der_len(&self) -> Result<usize> {
+ let sz = self.data.len();
+ if sz < 127 {
+ // 1 (class+tag) + 1 (length) + len
+ Ok(2 + sz)
+ } else {
+ // hmm, a very long integer. anyway:
+ // 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,
+ false,
+ Self::TAG,
+ Length::Definite(self.data.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.data).map_err(Into::into)
+ }
+}
+
+/// Helper macro to declare integers at compile-time
+///
+/// [`Integer`] stores the encoded representation of the integer, so declaring
+/// an integer requires to either use a runtime function or provide the encoded value.
+/// This macro simplifies this task by encoding the value.
+/// It can be used the following ways:
+///
+/// - `int!(1234)`: Create a const expression for the corresponding `Integer<'static>`
+/// - `int!(raw 1234)`: Return the DER encoded form as a byte array (hex-encoded, big-endian
+/// representation from the integer, with leading zeroes removed).
+///
+/// # Examples
+///
+/// ```rust
+/// use asn1_rs::{int, Integer};
+///
+/// const INT0: Integer = int!(1234);
+/// ```
+#[macro_export]
+macro_rules! int {
+ (raw $item:expr) => {
+ $crate::exports::asn1_rs_impl::encode_int!($item)
+ };
+ (rel $item:expr) => {
+ $crate::exports::asn1_rs_impl::encode_int!(rel $item)
+ };
+ ($item:expr) => {
+ $crate::Integer::new(
+ &$crate::int!(raw $item),
+ )
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Any, FromDer, Header, Tag, ToDer};
+ use std::convert::TryInto;
+
+ // Vectors from Section 5.7 of:
+ // https://luca.ntop.org/Teaching/Appunti/asn1.html
+ pub(crate) const I0_BYTES: &[u8] = &[0x02, 0x01, 0x00];
+ pub(crate) const I127_BYTES: &[u8] = &[0x02, 0x01, 0x7F];
+ pub(crate) const I128_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0x80];
+ pub(crate) const I256_BYTES: &[u8] = &[0x02, 0x02, 0x01, 0x00];
+ pub(crate) const INEG128_BYTES: &[u8] = &[0x02, 0x01, 0x80];
+ pub(crate) const INEG129_BYTES: &[u8] = &[0x02, 0x02, 0xFF, 0x7F];
+
+ // Additional vectors
+ pub(crate) const I255_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0xFF];
+ pub(crate) const I32767_BYTES: &[u8] = &[0x02, 0x02, 0x7F, 0xFF];
+ pub(crate) const I65535_BYTES: &[u8] = &[0x02, 0x03, 0x00, 0xFF, 0xFF];
+ pub(crate) const INEG32768_BYTES: &[u8] = &[0x02, 0x02, 0x80, 0x00];
+
+ #[test]
+ fn decode_i8() {
+ assert_eq!(0, i8::from_der(I0_BYTES).unwrap().1);
+ assert_eq!(127, i8::from_der(I127_BYTES).unwrap().1);
+ assert_eq!(-128, i8::from_der(INEG128_BYTES).unwrap().1);
+ }
+
+ #[test]
+ fn encode_i8() {
+ assert_eq!(0i8.to_der_vec().unwrap(), I0_BYTES);
+ assert_eq!(127i8.to_der_vec().unwrap(), I127_BYTES);
+ assert_eq!((-128i8).to_der_vec().unwrap(), INEG128_BYTES);
+ }
+
+ #[test]
+ fn decode_i16() {
+ assert_eq!(0, i16::from_der(I0_BYTES).unwrap().1);
+ assert_eq!(127, i16::from_der(I127_BYTES).unwrap().1);
+ assert_eq!(128, i16::from_der(I128_BYTES).unwrap().1);
+ assert_eq!(255, i16::from_der(I255_BYTES).unwrap().1);
+ assert_eq!(256, i16::from_der(I256_BYTES).unwrap().1);
+ assert_eq!(32767, i16::from_der(I32767_BYTES).unwrap().1);
+ assert_eq!(-128, i16::from_der(INEG128_BYTES).unwrap().1);
+ assert_eq!(-129, i16::from_der(INEG129_BYTES).unwrap().1);
+ assert_eq!(-32768, i16::from_der(INEG32768_BYTES).unwrap().1);
+ }
+
+ #[test]
+ fn encode_i16() {
+ assert_eq!(0i16.to_der_vec().unwrap(), I0_BYTES);
+ assert_eq!(127i16.to_der_vec().unwrap(), I127_BYTES);
+ assert_eq!(128i16.to_der_vec().unwrap(), I128_BYTES);
+ assert_eq!(255i16.to_der_vec().unwrap(), I255_BYTES);
+ assert_eq!(256i16.to_der_vec().unwrap(), I256_BYTES);
+ assert_eq!(32767i16.to_der_vec().unwrap(), I32767_BYTES);
+ assert_eq!((-128i16).to_der_vec().unwrap(), INEG128_BYTES);
+ assert_eq!((-129i16).to_der_vec().unwrap(), INEG129_BYTES);
+ assert_eq!((-32768i16).to_der_vec().unwrap(), INEG32768_BYTES);
+ }
+
+ #[test]
+ fn decode_u8() {
+ assert_eq!(0, u8::from_der(I0_BYTES).unwrap().1);
+ assert_eq!(127, u8::from_der(I127_BYTES).unwrap().1);
+ assert_eq!(255, u8::from_der(I255_BYTES).unwrap().1);
+ }
+
+ #[test]
+ fn encode_u8() {
+ assert_eq!(0u8.to_der_vec().unwrap(), I0_BYTES);
+ assert_eq!(127u8.to_der_vec().unwrap(), I127_BYTES);
+ assert_eq!(255u8.to_der_vec().unwrap(), I255_BYTES);
+ }
+
+ #[test]
+ fn decode_u16() {
+ assert_eq!(0, u16::from_der(I0_BYTES).unwrap().1);
+ assert_eq!(127, u16::from_der(I127_BYTES).unwrap().1);
+ assert_eq!(255, u16::from_der(I255_BYTES).unwrap().1);
+ assert_eq!(256, u16::from_der(I256_BYTES).unwrap().1);
+ assert_eq!(32767, u16::from_der(I32767_BYTES).unwrap().1);
+ assert_eq!(65535, u16::from_der(I65535_BYTES).unwrap().1);
+ }
+
+ #[test]
+ fn encode_u16() {
+ assert_eq!(0u16.to_der_vec().unwrap(), I0_BYTES);
+ assert_eq!(127u16.to_der_vec().unwrap(), I127_BYTES);
+ assert_eq!(255u16.to_der_vec().unwrap(), I255_BYTES);
+ assert_eq!(256u16.to_der_vec().unwrap(), I256_BYTES);
+ assert_eq!(32767u16.to_der_vec().unwrap(), I32767_BYTES);
+ assert_eq!(65535u16.to_der_vec().unwrap(), I65535_BYTES);
+ }
+
+ /// Integers must be encoded with a minimum number of octets
+ #[test]
+ fn reject_non_canonical() {
+ assert!(i8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(i16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(u8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(u16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ }
+
+ #[test]
+ fn declare_int() {
+ let int = super::int!(1234);
+ assert_eq!(int.try_into(), Ok(1234));
+ }
+
+ #[test]
+ fn trim_slice() {
+ use super::trim_slice;
+ let h = Header::new_simple(Tag(0));
+ // no zero nor ff - nothing to remove
+ let input: &[u8] = &[0x7f, 0xff, 0x00, 0x02];
+ assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
+ //
+ // 0x00
+ //
+ // empty - nothing to remove
+ let input: &[u8] = &[];
+ assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
+ // one zero - nothing to remove
+ let input: &[u8] = &[0];
+ assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
+ // all zeroes - keep only one
+ let input: &[u8] = &[0, 0, 0];
+ assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
+ // some zeroes - keep only the non-zero part
+ let input: &[u8] = &[0, 0, 1];
+ assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
+ //
+ // 0xff
+ //
+ // one ff - nothing to remove
+ let input: &[u8] = &[0xff];
+ assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
+ // all ff - keep only one
+ let input: &[u8] = &[0xff, 0xff, 0xff];
+ assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
+ // some ff - keep only the non-zero part
+ let input: &[u8] = &[0xff, 0xff, 1];
+ assert_eq!(Ok(&input[1..]), trim_slice(&Any::new(h.clone(), input)));
+ // some ff and a MSB 1 - keep only the non-zero part
+ let input: &[u8] = &[0xff, 0xff, 0x80, 1];
+ assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
+ }
+}