Skip to content
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
7 changes: 4 additions & 3 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(crate) mod bit_string;
mod bmp_string;
mod boolean;
mod choice;
mod class_tagged;
mod context_specific;
mod general_string;
mod generalized_time;
Expand All @@ -35,18 +36,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
9 changes: 7 additions & 2 deletions der/src/asn1/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

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

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

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

/// Application class, EXPLICIT
pub type ApplicationExplicit<const NUMBER: u32, T> =
ClassTaggedExplicit<NUMBER, T, CLASS_APPLICATION>;
68 changes: 68 additions & 0 deletions der/src/asn1/class_tagged.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::{
Class, Decode, DecodeValue, Encode, EncodeValue, Error, FixedTag, Header, Length, Reader, Tag,
TagMode, TagNumber, Writer,
};

/// `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` 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 ClassTaggedExplicit<const NUMBER: u32, T, const CLASS_BITS: u8> {
/// Inner value might implement [`Encode`], [`Decode`] or both.
pub value: T,
}

impl<const NUMBER: u32, T, const CLASS_BITS: u8> ClassTaggedExplicit<NUMBER, T, CLASS_BITS> {
/// Returns const [`TagNumber`], associated with this `EXPLICIT` tag wrapper.
pub const fn tag_number() -> TagNumber {
TagNumber(NUMBER)
}

/// Returns const [`TagMode::Explicit`], associated with this `EXPLICIT` tag wrapper.
pub const fn tag_mode() -> TagMode {
TagMode::Explicit
}
}

impl<const NUMBER: u32, T, const CLASS_BITS: u8> EncodeValue
for ClassTaggedExplicit<NUMBER, T, CLASS_BITS>
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, const CLASS_BITS: u8> DecodeValue<'a>
for ClassTaggedExplicit<NUMBER, T, CLASS_BITS>
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, const CLASS_BITS: u8> FixedTag
for ClassTaggedExplicit<NUMBER, T, CLASS_BITS>
{
const TAG: Tag = Tag::new_non_universal(Class::from_bits(CLASS_BITS), TagNumber(NUMBER), true);
}
41 changes: 38 additions & 3 deletions der/src/asn1/context_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

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

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

/// ContextSpecific class, EXPLICIT
pub type ContextSpecificExplicit<const NUMBER: u32, T> =
ClassTaggedExplicit<NUMBER, T, CLASS_CONTEXT_SPECIFIC>;

#[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, ContextSpecificExplicit, ContextSpecificRef, SetOf, Utf8StringRef},
};
use hex_literal::hex;

Expand Down Expand Up @@ -194,4 +199,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);
}
}
7 changes: 6 additions & 1 deletion der/src/asn1/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

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

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

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

/// Private class, EXPLICIT
pub type PrivateExplicit<const NUMBER: u32, T> = ClassTaggedExplicit<NUMBER, T, CLASS_PRIVATE>;
27 changes: 26 additions & 1 deletion der/src/tag.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! ASN.1 tags.
#![cfg_attr(feature = "arbitrary", allow(clippy::arithmetic_side_effects))]

mod class;
pub(crate) mod class;
mod mode;
mod number;

Expand Down Expand Up @@ -165,6 +165,31 @@ impl Tag {
/// rules implemented by this crate.
pub(crate) const MAX_SIZE: usize = 6;

/// Creates a [`Tag`] of non-`UNIVERSAL` class.
/// Allowed classes are:
/// - `APPLICATION` [`Class::Application`],
/// - `CONTEXT-SPECIFIC` [`Class::ContextSpecific`],
/// - `PRIVATE` [`Class::Private`],
///
/// Returns [`Tag::Null`] otherwise.
pub const fn new_non_universal(class: Class, number: TagNumber, constructed: bool) -> Tag {
match class {
Class::Application => Tag::Application {
constructed,
number,
},
Class::ContextSpecific => Tag::ContextSpecific {
constructed,
number,
},
Class::Private => Tag::Private {
constructed,
number,
},
Class::Universal => Tag::Null,
}
}

/// Decode a [`Tag`] in addition to returning the value of the constructed bit.
pub(crate) fn decode_with_constructed_bit<'a>(
reader: &mut impl Reader<'a>,
Expand Down