Skip to content

der: add ApplicationExplicit, ContextSpecificExplicit, PrivateExplicit #1945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ mod videotex_string;

pub use self::{
any::AnyRef,
application::{Application, ApplicationRef},
application::{Application, ApplicationExplicit, ApplicationRef},
bit_string::{BitStringIter, BitStringRef},
choice::Choice,
context_specific::{ContextSpecific, ContextSpecificRef},
context_specific::{ContextSpecific, ContextSpecificExplicit, ContextSpecificRef},
general_string::GeneralStringRef,
generalized_time::GeneralizedTime,
ia5_string::Ia5StringRef,
integer::{int::IntRef, uint::UintRef},
null::Null,
octet_string::OctetStringRef,
printable_string::PrintableStringRef,
private::{Private, PrivateRef},
private::{Private, PrivateExplicit, PrivateRef},
sequence::{Sequence, SequenceRef},
sequence_of::{SequenceOf, SequenceOfIter},
set_of::{SetOf, SetOfIter},
Expand Down
11 changes: 9 additions & 2 deletions der/src/asn1/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use crate::{
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
tag::IsConstructed,
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
asn1::AnyRef, tag::IsConstructed,
};
use core::cmp::Ordering;

Expand All @@ -12,3 +12,10 @@ use crate::ErrorKind;

impl_custom_class!(Application, Application, "APPLICATION", "0b01000000");
impl_custom_class_ref!(ApplicationRef, Application, "APPLICATION", "0b01000000");

impl_custom_class_explicit!(
ApplicationExplicit,
Application,
"APPLICATION",
"0b01000000"
);
46 changes: 43 additions & 3 deletions der/src/asn1/context_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use crate::{
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
tag::IsConstructed,
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
asn1::AnyRef, tag::IsConstructed,
};
use core::cmp::Ordering;

Expand All @@ -23,13 +23,23 @@ impl_custom_class_ref!(
"0b10000000"
);

impl_custom_class_explicit!(
ContextSpecificExplicit,
ContextSpecific,
"CONTEXT-SPECIFIC",
"0b10000000"
);

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::ContextSpecific;
use crate::{
Decode, Encode, SliceReader, TagMode, TagNumber,
asn1::{BitStringRef, ContextSpecificRef, SetOf, Utf8StringRef},
asn1::{
BitStringRef, ContextSpecificRef, SetOf, Utf8StringRef,
context_specific::ContextSpecificExplicit,
},
};
use hex_literal::hex;

Expand Down Expand Up @@ -194,4 +204,34 @@ mod tests {
assert_eq!(field.value.get(0).cloned(), Some(hello));
assert_eq!(field.value.get(1).cloned(), Some(world));
}

#[test]
fn round_trip_explicit() {
let field =
ContextSpecificExplicit::<1, BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
assert_eq!(
field.value,
BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
);
assert_eq!(
ContextSpecificExplicit::<1, BitStringRef<'_>>::tag_mode(),
TagMode::Explicit
);
assert_eq!(
ContextSpecificExplicit::<1, BitStringRef<'_>>::tag_number(),
TagNumber(1)
);

let mut buf = [0u8; 128];
let encoded = field.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded, EXAMPLE_BYTES);

// should not decode as tag CONTEXT-SPECIFIC [2]
assert!(ContextSpecificExplicit::<2, BitStringRef<'_>>::from_der(EXAMPLE_BYTES).is_err());

// should be different than CONTEXT-SPECIFIC [1]
let invalid_field = ContextSpecificExplicit::<2, BitStringRef<'_>> { value: field.value };
let invalid_encoded = invalid_field.encode_to_slice(&mut buf).unwrap();
assert_ne!(invalid_encoded, EXAMPLE_BYTES);
}
}
70 changes: 70 additions & 0 deletions der/src/asn1/internal_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,73 @@ macro_rules! impl_custom_class_ref {
}
};
}

macro_rules! impl_custom_class_explicit {
($explicit_class_type_name: ident, $class_enum_name: ident, $asn1_class_name: literal, $class_bits_str: literal) => {
#[doc = concat!("`", $asn1_class_name, "` field reference, with const `EXPLICIT` encoding.")]
///
///
/// This type encodes a field which is specific to a particular context
/// and is identified by a [`TagNumber`].
///
/// Inner value might implement [`Encode`], [`Decode`] or both.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct $explicit_class_type_name<const NUMBER: u32, T> {
/// Inner value might implement [`Encode`], [`Decode`] or both.
pub value: T,
}

impl<const NUMBER: u32, T> $explicit_class_type_name<NUMBER, T> {
#[doc = concat!("Returns const [`TagNumber`], associated with this `EXPLICIT` `", $asn1_class_name, "` wrapper.")]
pub const fn tag_number() -> TagNumber {
TagNumber(NUMBER)
}

#[doc = concat!("Returns const [`TagMode::Explicit`], associated with this `EXPLICIT` `", $asn1_class_name, "` wrapper.")]
pub const fn tag_mode() -> TagMode {
TagMode::Explicit
}
}

impl<const NUMBER: u32, T> EncodeValue for $explicit_class_type_name<NUMBER, T>
where
T: Encode,
{
fn value_len(&self) -> Result<Length, Error> {
self.value.encoded_len()
}

fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
// Encode EXPLICIT value (with tag and length)
self.value.encode(writer)
}
}

impl<'a, const NUMBER: u32, T> DecodeValue<'a> for $explicit_class_type_name<NUMBER, T>
where
T: Decode<'a>,
{
type Error = T::Error;

fn decode_value<R: Reader<'a>>(
reader: &mut R,
header: Header,
) -> Result<Self, Self::Error> {
// encoding shall be constructed
if !header.tag.is_constructed() {
return Err(reader.error(header.tag.non_canonical_error()).into());
}
Ok(Self {
value: T::decode(reader)?,
})
}
}

impl<const NUMBER: u32, T> FixedTag for $explicit_class_type_name<NUMBER, T> {
const TAG: Tag = Tag::$class_enum_name {
constructed: true,
number: Self::tag_number(),
};
}
};
}
5 changes: 3 additions & 2 deletions der/src/asn1/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use crate::{
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
tag::IsConstructed,
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
asn1::AnyRef, tag::IsConstructed,
};
use core::cmp::Ordering;

Expand All @@ -12,3 +12,4 @@ use crate::ErrorKind;

impl_custom_class!(Private, Private, "PRIVATE", "0b11000000");
impl_custom_class_ref!(PrivateRef, Private, "PRIVATE", "0b11000000");
impl_custom_class_explicit!(PrivateExplicit, Private, "PRIVATE", "0b11000000");