Skip to content

Commit

Permalink
(edited by dishmaker) support private tags and tag numbers >30 that a…
Browse files Browse the repository at this point in the history
…re stored in long form

draft: CustomClass generic type

feat: add decode_implicit and decode_explicit back

fix: cargo test -p der now pass

refactor: raname to Explicit struct

refactor: TAG, T order

docs: CustomClass

feat: add ContextSpecificRef back (ContextSpecificExplicitRef)

update der-derive to current der implementation

feat: add tests for context-specific

fix: dependency crates

fixed for x509-cert

cargo fmt

fix: crates depending on context-specific and long TagNumber

fix x509-cert
  • Loading branch information
Harris Kaufmann authored and dishmaker committed Oct 9, 2024
1 parent 7e399e9 commit a126b9f
Show file tree
Hide file tree
Showing 34 changed files with 1,873 additions and 829 deletions.
15 changes: 14 additions & 1 deletion der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
mod internal_macros;

mod any;
mod any_custom_class;
mod application;
mod bit_string;
#[cfg(feature = "alloc")]
mod bmp_string;
mod boolean;
mod choice;
mod context_specific;
mod custom_class;
mod generalized_time;
mod ia5_string;
mod integer;
Expand All @@ -20,6 +23,7 @@ mod octet_string;
mod oid;
mod optional;
mod printable_string;
mod private;
#[cfg(feature = "real")]
mod real;
mod sequence;
Expand All @@ -32,15 +36,24 @@ mod videotex_string;

pub use self::{
any::AnyRef,
any_custom_class::{AnyCustomClassExplicit, AnyCustomClassImplicit},
application::{
ApplicationExplicit, ApplicationExplicitRef, ApplicationImplicit, ApplicationImplicitRef,
},
bit_string::{BitStringIter, BitStringRef},
choice::Choice,
context_specific::{ContextSpecific, ContextSpecificRef},
//context_specific::{ContextSpecific, ContextSpecificRef},
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::{PrivateExplicit, PrivateExplicitRef, PrivateImplicit, PrivateImplicitRef},
sequence::{Sequence, SequenceRef},
sequence_of::{SequenceOf, SequenceOfIter},
set_of::{SetOf, SetOfIter},
Expand Down
261 changes: 261 additions & 0 deletions der/src/asn1/any_custom_class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
use crate::{
Class, Decode, DecodeValue, Encode, EncodeValue, Error, Header, Length, Reader, Tag, TagNumber,
Tagged, Writer,
};

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_checked(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_checked<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Self, T::Error> {
let any_explicit = Self::decode(reader)?;

if any_explicit.class == class && any_explicit.tag_number == tag_number {
Ok(any_explicit)
} else {
let expected = expected_tag_constructed(class, tag_number, true);
Err(any_explicit.tag().unexpected_error(Some(expected)).into())
}
}
}

impl<'a, T> AnyCustomClassImplicit<T>
where
T: Tagged + DecodeValue<'a> + '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_checked(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_checked<R: Reader<'a>>(
class: Class,
tag_number: TagNumber,
reader: &mut R,
) -> Result<Self, T::Error> {
let any_implicit = Self::decode(reader)?;
if any_implicit.class == class && any_implicit.tag_number == tag_number {
Ok(any_implicit)
} else {
let expected = expected_tag_constructed(class, tag_number, true);
Err(any_implicit.tag().unexpected_error(Some(expected)).into())
}
}
}

impl<'a, T> Decode<'a> for AnyCustomClassExplicit<T>
where
T: Decode<'a>,
{
type Error = T::Error;

fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
let header = Header::decode(reader)?;

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

Ok(Self {
value: reader.read_nested(header.length, |reader| T::decode(reader))?,
class: header.tag.class(),
tag_number: header.tag.number(),
})
}
}

impl<'a, T> Decode<'a> for AnyCustomClassImplicit<T>
where
T: Tagged + DecodeValue<'a> + 'a,
{
type Error = T::Error;

fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
let header = Header::decode(reader)?;

let value = reader.read_nested(header.length, |reader| 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: header.tag.class(),
tag_number: header.tag.number(),
constructed: header.tag.is_constructed(),
})
}
}

impl<T> EncodeValue for AnyCustomClassExplicit<T>
where
T: EncodeValue + Tagged,
{
fn value_len(&self) -> Result<Length, Error> {
self.value.encoded_len()
}

fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.value.encode(writer)
}
}

impl<T> EncodeValue for AnyCustomClassImplicit<T>
where
T: EncodeValue + Tagged,
{
fn value_len(&self) -> Result<Length, Error> {
self.value.value_len()
}

fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.value.encode_value(writer)
}
}

impl<T> Tagged for AnyCustomClassExplicit<T> {
fn tag(&self) -> Tag {
expected_tag_constructed(self.class, self.tag_number, true)
}
}

impl<T> Tagged for AnyCustomClassImplicit<T> {
fn tag(&self) -> Tag {
expected_tag_constructed(self.class, self.tag_number, self.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,
}
}
21 changes: 21 additions & 0 deletions der/src/asn1/application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! Application field.

use crate::tag::CLASS_APPLICATION;

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 a126b9f

Please sign in to comment.