From c5bc87e7713cee45f156f1f0a14e7f50771af7fa Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:02:02 +0100 Subject: [PATCH 1/4] fix(der): derive Sequence on array fields [u8; N] --- der/src/asn1/bit_string.rs | 28 +++++++++++++ der/src/asn1/octet_string.rs | 24 +++++++++++ der/tests/derive.rs | 81 +++++++++++++++++++++++++++++++++++- 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index 146541bf6..bf0bd4f46 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -193,6 +193,34 @@ impl<'a> TryFrom<&&'a [u8]> for BitStringRef<'a> { } } +/// Hack for simplifying the custom derive use case. +impl<'a, const N: usize> TryFrom<&'a [u8; N]> for BitStringRef<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8; N]) -> Result> { + BitStringRef::from_bytes(bytes) + } +} + +/// Hack for simplifying the custom derive use case. +impl<'a, const N: usize> TryFrom> for [u8; N] { + type Error = Error; + + fn try_from(bit_string: BitStringRef<'a>) -> Result { + let expected_len = Length::new_usize(N)?; + let bytes = bit_string + .as_bytes() + .ok_or_else(|| Tag::BitString.value_error())?; + + bytes.try_into().map_err(|_| { + Error::from_kind(ErrorKind::Incomplete { + expected_len, + actual_len: bit_string.byte_len(), + }) + }) + } +} + impl<'a> TryFrom> for &'a [u8] { type Error = Error; diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index e4bf0aff2..83e220b4a 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -113,6 +113,30 @@ impl<'a> TryFrom<&&'a [u8]> for OctetStringRef<'a> { } } +/// Hack for simplifying the custom derive use case. +impl<'a, const N: usize> TryFrom<&'a [u8; N]> for OctetStringRef<'a> { + type Error = Error; + + fn try_from(byte_slice: &'a [u8; N]) -> Result { + OctetStringRef::new(byte_slice) + } +} + +/// Hack for simplifying the custom derive use case. +impl<'a, const N: usize> TryFrom> for [u8; N] { + type Error = Error; + + fn try_from(octet_string: OctetStringRef<'a>) -> Result { + let expected_len = Length::new_usize(N)?; + octet_string.as_bytes().try_into().map_err(|_| { + Error::from_kind(ErrorKind::Incomplete { + expected_len, + actual_len: octet_string.len(), + }) + }) + } +} + #[cfg(feature = "alloc")] pub use self::allocating::OctetString; diff --git a/der/tests/derive.rs b/der/tests/derive.rs index 0624ba21f..3c9abbb52 100644 --- a/der/tests/derive.rs +++ b/der/tests/derive.rs @@ -528,7 +528,7 @@ mod sequence { pub owned_optional_implicit_bytes: Option>, #[asn1( - type = "OCTET STRING", + type = "BIT STRING", context_specific = "6", optional = "true", tag_mode = "EXPLICIT" @@ -568,6 +568,85 @@ mod sequence { assert_eq!(obj, obj_decoded); } + #[derive(Sequence, Default, Eq, PartialEq, Debug)] + #[asn1(tag_mode = "IMPLICIT")] + pub struct TypeCheckArraysSequenceFieldAttributeCombinations { + #[asn1(type = "OCTET STRING", deref = "true")] + pub array_bytes: [u8; 2], + + #[asn1(type = "BIT STRING", deref = "true")] + pub array_bits: [u8; 2], + + #[asn1(type = "OCTET STRING", context_specific = "0", deref = "true")] + pub array_implicit_bytes: [u8; 2], + + #[asn1(type = "BIT STRING", context_specific = "1", deref = "true")] + pub array_implicit_bits: [u8; 2], + + #[asn1( + type = "OCTET STRING", + context_specific = "2", + tag_mode = "EXPLICIT", + deref = "true" + )] + pub array_explicit_bytes: [u8; 2], + + #[asn1( + type = "BIT STRING", + context_specific = "3", + tag_mode = "EXPLICIT", + deref = "true" + )] + pub array_explicit_bits: [u8; 2], + + #[asn1(type = "BIT STRING", context_specific = "4", optional = "true")] + pub array_optional_implicit_bits: Option<[u8; 2]>, + + #[asn1(type = "OCTET STRING", context_specific = "5", optional = "true")] + pub array_optional_implicit_bytes: Option<[u8; 2]>, + + #[asn1( + type = "BIT STRING", + context_specific = "6", + optional = "true", + tag_mode = "EXPLICIT" + )] + pub array_optional_explicit_bits: Option<[u8; 2]>, + + #[asn1( + type = "OCTET STRING", + context_specific = "7", + optional = "true", + tag_mode = "EXPLICIT" + )] + pub array_optional_explicit_bytes: Option<[u8; 2]>, + } + + #[test] + fn type_combinations_arrays_instance() { + let obj = TypeCheckArraysSequenceFieldAttributeCombinations { + array_bytes: [0xAA, 0xBB], + array_bits: [0xCC, 0xDD], + + array_implicit_bytes: [0, 1], + array_implicit_bits: [2, 3], + + array_explicit_bytes: [4, 5], + array_explicit_bits: [6, 7], + + array_optional_implicit_bits: Some([8, 9]), + array_optional_implicit_bytes: Some([10, 11]), + + array_optional_explicit_bits: Some([12, 13]), + array_optional_explicit_bytes: Some([14, 15]), + }; + + let der_encoded = obj.to_der().unwrap(); + let obj_decoded = + TypeCheckArraysSequenceFieldAttributeCombinations::from_der(&der_encoded).unwrap(); + assert_eq!(obj, obj_decoded); + } + #[derive(Sequence)] #[asn1(error = CustomError)] pub struct TypeWithCustomError { From 9d7e0e24d5764c9e168de5e5eae34d70a1028d81 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:10:20 +0100 Subject: [PATCH 2/4] der: Length::try_from usize --- der/src/asn1/bit_string.rs | 2 +- der/src/asn1/octet_string.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index bf0bd4f46..603274605 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -207,7 +207,7 @@ impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; fn try_from(bit_string: BitStringRef<'a>) -> Result { - let expected_len = Length::new_usize(N)?; + let expected_len = Length::try_from(N)?; let bytes = bit_string .as_bytes() .ok_or_else(|| Tag::BitString.value_error())?; diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 83e220b4a..515f3b9ca 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -127,7 +127,7 @@ impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; fn try_from(octet_string: OctetStringRef<'a>) -> Result { - let expected_len = Length::new_usize(N)?; + let expected_len = Length::try_from(N)?; octet_string.as_bytes().try_into().map_err(|_| { Error::from_kind(ErrorKind::Incomplete { expected_len, From 1679dde7f8d059a3872acd1e64562e69ef6b1d34 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:12:51 +0100 Subject: [PATCH 3/4] docs: der: rm 'hack' comment --- der/src/asn1/bit_string.rs | 2 -- der/src/asn1/octet_string.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index 603274605..a4d7d910f 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -193,7 +193,6 @@ impl<'a> TryFrom<&&'a [u8]> for BitStringRef<'a> { } } -/// Hack for simplifying the custom derive use case. impl<'a, const N: usize> TryFrom<&'a [u8; N]> for BitStringRef<'a> { type Error = Error; @@ -202,7 +201,6 @@ impl<'a, const N: usize> TryFrom<&'a [u8; N]> for BitStringRef<'a> { } } -/// Hack for simplifying the custom derive use case. impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 515f3b9ca..5d96eca80 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -113,7 +113,6 @@ impl<'a> TryFrom<&&'a [u8]> for OctetStringRef<'a> { } } -/// Hack for simplifying the custom derive use case. impl<'a, const N: usize> TryFrom<&'a [u8; N]> for OctetStringRef<'a> { type Error = Error; @@ -122,7 +121,6 @@ impl<'a, const N: usize> TryFrom<&'a [u8; N]> for OctetStringRef<'a> { } } -/// Hack for simplifying the custom derive use case. impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; From 8e80f099c329a3431223718f17fbd432546765e7 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:19:02 +0100 Subject: [PATCH 4/4] der: Tag::length_error --- der/src/asn1/bit_string.rs | 12 ++---------- der/src/asn1/octet_string.rs | 11 ++++------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index a4d7d910f..c182edab8 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -205,17 +205,9 @@ impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; fn try_from(bit_string: BitStringRef<'a>) -> Result { - let expected_len = Length::try_from(N)?; - let bytes = bit_string - .as_bytes() - .ok_or_else(|| Tag::BitString.value_error())?; + let bytes: &[u8] = TryFrom::try_from(bit_string)?; - bytes.try_into().map_err(|_| { - Error::from_kind(ErrorKind::Incomplete { - expected_len, - actual_len: bit_string.byte_len(), - }) - }) + bytes.try_into().map_err(|_| Tag::BitString.length_error()) } } diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 5d96eca80..76b2d1756 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -125,13 +125,10 @@ impl<'a, const N: usize> TryFrom> for [u8; N] { type Error = Error; fn try_from(octet_string: OctetStringRef<'a>) -> Result { - let expected_len = Length::try_from(N)?; - octet_string.as_bytes().try_into().map_err(|_| { - Error::from_kind(ErrorKind::Incomplete { - expected_len, - actual_len: octet_string.len(), - }) - }) + octet_string + .as_bytes() + .try_into() + .map_err(|_| Tag::OctetString.length_error()) } }