Skip to content

Commit

Permalink
feat: add ContextSpecificRef back (ContextSpecificExplicitRef)
Browse files Browse the repository at this point in the history
  • Loading branch information
dishmaker committed Oct 7, 2024
1 parent cadeec5 commit a791614
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 485 deletions.
11 changes: 7 additions & 4 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod internal_macros;

mod any;
mod any_custom_class;
mod application;
mod bit_string;
#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -35,19 +36,21 @@ mod videotex_string;

pub use self::{
any::AnyRef,
any_custom_class::{AnyCustomClassExplicit, AnyCustomClassImplicit},
bit_string::{BitStringIter, BitStringRef},
choice::Choice,
//context_specific::{ContextSpecific, ContextSpecificRef},
context_specific::{ContextSpecificExplicit, ContextSpecificImplicit},
custom_class::{AnyCustomClassExplicit, AnyCustomClassImplicit},
context_specific::{
ContextSpecificExplicit, ContextSpecificExplicitRef, ContextSpecificImplicit,
ContextSpecificImplicitRef,
},
generalized_time::GeneralizedTime,
ia5_string::Ia5StringRef,
integer::{int::IntRef, uint::UintRef},
null::Null,
octet_string::OctetStringRef,
printable_string::PrintableStringRef,
//private::{Private, PrivateRef},
private::{PrivateExplicit, PrivateImplicit},
private::{PrivateExplicit, PrivateExplicitRef, PrivateImplicit, PrivateImplicitRef},
sequence::{Sequence, SequenceRef},
sequence_of::{SequenceOf, SequenceOfIter},
set_of::{SetOf, SetOfIter},
Expand Down
184 changes: 184 additions & 0 deletions der/src/asn1/any_custom_class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
use crate::{Class, Decode, DecodeValue, Error, Header, Reader, Tag, TagNumber, Tagged};

use super::AnyRef;

/// `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
/// `EXPLICIT` encoding - always constructed.
pub struct AnyCustomClassExplicit<T> {
/// Value of the field. Should implement [`Decode`]
pub value: T,

/// Class of the field.
///
/// Supported classes: [`Class::Application`], [`Class::ContextSpecific`], [`Class::Private`]
pub class: Class,

/// Tag number without the leading class bits `0b11000000`
/// and without constructed `0b00100000` flag.
pub tag_number: TagNumber,
}

/// `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
/// `IMPLICIT` encoding - constructed bit should match inner value's tag.
pub struct AnyCustomClassImplicit<T> {
/// Value of the field. Should implement [`DecodeValue`]
pub value: T,

/// Class of the field.
///
/// Supported classes: [`Class::Application`], [`Class::ContextSpecific`], [`Class::Private`]
pub class: Class,

/// Tag number without the leading class bits `0b11000000`
/// and without constructed `0b00100000` flag.
pub tag_number: TagNumber,

/// Constructed flag. Should match value's tag constructed flag.
pub constructed: bool,
}

impl<'a, T> AnyCustomClassExplicit<T>
where
T: Decode<'a>,
{
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
///
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
///
/// Skips `CONTEXT-SPECIFIC` fields, lower than [`TagNumber`].
pub fn decode_skipping<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Option<Self>, T::Error> {
decode_peeking(reader, class, tag_number, |reader| {
Self::decode(class, tag_number, reader)
})
}

/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
///
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
pub fn decode<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Self, T::Error> {
let header = Header::decode(reader)?;

if header.tag.class() == class && header.tag.is_constructed() {
Ok(Self {
value: reader.read_nested(header.length, |reader| T::decode(reader))?,
class,
tag_number,
})
} else {
let expected = expected_tag_constructed(class, tag_number, true);
Err(header.tag.unexpected_error(Some(expected)).into())
}
}
}

impl<'a, T> AnyCustomClassImplicit<T>
where
T: Tagged + DecodeValue<'a>,
{
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
///
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
///
/// Skips `CONTEXT-SPECIFIC` fields, lower than [`TagNumber`].
pub fn decode_skipping<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Option<Self>, T::Error> {
decode_peeking::<_, _, T::Error, _>(reader, class, tag_number, |reader| {
Self::decode(class, tag_number, reader)
})
}

/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
///
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
pub fn decode<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Self, T::Error> {
let header = Header::decode(reader)?;
let value = T::decode_value(reader, header)?;

if header.tag.is_constructed() != value.tag().is_constructed() {
return Err(header.tag.non_canonical_error().into());
}

Ok(Self {
value,
class,
tag_number,
constructed: header.tag.is_constructed(),
})
}
}

/// Attempt to decode a custom class-tagged field with the given
/// helper callback.
fn decode_peeking<'a, F, R: Reader<'a>, E, T>(
reader: &mut R,
expected_class: Class,
expected_number: TagNumber,
f: F,
) -> Result<Option<T>, E>
where
F: FnOnce(&mut R) -> Result<T, E>,
E: From<Error>,
{
while let Some(tag) = Tag::peek_optional(reader)? {
if is_unskippable_tag(tag, expected_class, expected_number) {
break;
} else if tag.number() == expected_number {
return Some(f(reader)).transpose();
} else {
AnyRef::decode(reader)?;
}
}

Ok(None)
}

/// Returns if this tag is of different class than eg. CONTEXT-SPECIFIC
/// or tag number is higher than expected
fn is_unskippable_tag(tag: Tag, expected_class: Class, expected_number: TagNumber) -> bool {
if expected_class != tag.class() {
return true;
}
match expected_class {
Class::Application => tag.number() > expected_number,
Class::ContextSpecific => tag.number() > expected_number,
Class::Private => tag.number() != expected_number,
Class::Universal => tag.number() != expected_number,
}
}

pub(crate) const fn expected_tag_constructed(
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,
}
}
12 changes: 11 additions & 1 deletion der/src/asn1/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@
use crate::tag::CLASS_APPLICATION;

use super::custom_class::{CustomClassExplicit, CustomClassImplicit};
use super::custom_class::{
CustomClassExplicit, CustomClassExplicitRef, CustomClassImplicit, CustomClassImplicitRef,
};

/// Application class, EXPLICIT
pub type ApplicationExplicit<const TAG: u16, T> = CustomClassExplicit<TAG, T, CLASS_APPLICATION>;

/// Application class, IMPLICIT
pub type ApplicationImplicit<const TAG: u16, T> = CustomClassImplicit<TAG, T, CLASS_APPLICATION>;

/// Application class, reference, EXPLICIT
pub type ApplicationExplicitRef<'a, const TAG: u16, T> =
CustomClassExplicitRef<'a, TAG, T, CLASS_APPLICATION>;

/// Application class, reference, IMPLICIT
pub type ApplicationImplicitRef<'a, const TAG: u16, T> =
CustomClassImplicitRef<'a, TAG, T, CLASS_APPLICATION>;
Loading

0 comments on commit a791614

Please sign in to comment.