Skip to content

Commit 5d8f3eb

Browse files
Harris Kaufmanndishmaker
authored andcommitted
(edited by dishmaker) support private tags and tag numbers >30 that are 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
1 parent 94f0b86 commit 5d8f3eb

34 files changed

+1855
-829
lines changed

der/src/asn1.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
mod internal_macros;
66

77
mod any;
8+
mod any_custom_class;
9+
mod application;
810
mod bit_string;
911
#[cfg(feature = "alloc")]
1012
mod bmp_string;
1113
mod boolean;
1214
mod choice;
1315
mod context_specific;
16+
mod custom_class;
1417
mod generalized_time;
1518
mod ia5_string;
1619
mod integer;
@@ -20,6 +23,7 @@ mod octet_string;
2023
mod oid;
2124
mod optional;
2225
mod printable_string;
26+
mod private;
2327
#[cfg(feature = "real")]
2428
mod real;
2529
mod sequence;
@@ -32,15 +36,24 @@ mod videotex_string;
3236

3337
pub use self::{
3438
any::AnyRef,
39+
any_custom_class::{AnyCustomClassExplicit, AnyCustomClassImplicit},
40+
application::{
41+
ApplicationExplicit, ApplicationExplicitRef, ApplicationImplicit, ApplicationImplicitRef,
42+
},
3543
bit_string::{BitStringIter, BitStringRef},
3644
choice::Choice,
37-
context_specific::{ContextSpecific, ContextSpecificRef},
45+
//context_specific::{ContextSpecific, ContextSpecificRef},
46+
context_specific::{
47+
ContextSpecificExplicit, ContextSpecificExplicitRef, ContextSpecificImplicit,
48+
ContextSpecificImplicitRef,
49+
},
3850
generalized_time::GeneralizedTime,
3951
ia5_string::Ia5StringRef,
4052
integer::{int::IntRef, uint::UintRef},
4153
null::Null,
4254
octet_string::OctetStringRef,
4355
printable_string::PrintableStringRef,
56+
private::{PrivateExplicit, PrivateExplicitRef, PrivateImplicit, PrivateImplicitRef},
4457
sequence::{Sequence, SequenceRef},
4558
sequence_of::{SequenceOf, SequenceOfIter},
4659
set_of::{SetOf, SetOfIter},

der/src/asn1/any_custom_class.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
use crate::{
2+
Class, Decode, DecodeValue, Encode, EncodeValue, Error, Header, Length, Reader, Tag, TagNumber,
3+
Tagged, Writer,
4+
};
5+
6+
use super::AnyRef;
7+
8+
/// `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
9+
///
10+
/// `EXPLICIT` encoding - always constructed.
11+
pub struct AnyCustomClassExplicit<T> {
12+
/// Value of the field. Should implement [`Decode`]
13+
pub value: T,
14+
15+
/// Class of the field.
16+
///
17+
/// Supported classes: [`Class::Application`], [`Class::ContextSpecific`], [`Class::Private`]
18+
pub class: Class,
19+
20+
/// Tag number without the leading class bits `0b11000000`
21+
/// and without constructed `0b00100000` flag.
22+
pub tag_number: TagNumber,
23+
}
24+
25+
/// `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
26+
///
27+
/// `IMPLICIT` encoding - constructed bit should match inner value's tag.
28+
pub struct AnyCustomClassImplicit<T> {
29+
/// Value of the field. Should implement [`DecodeValue`]
30+
pub value: T,
31+
32+
/// Class of the field.
33+
///
34+
/// Supported classes: [`Class::Application`], [`Class::ContextSpecific`], [`Class::Private`]
35+
pub class: Class,
36+
37+
/// Tag number without the leading class bits `0b11000000`
38+
/// and without constructed `0b00100000` flag.
39+
pub tag_number: TagNumber,
40+
41+
/// Constructed flag. Should match value's tag constructed flag.
42+
pub constructed: bool,
43+
}
44+
45+
impl<'a, T> AnyCustomClassExplicit<T>
46+
where
47+
T: Decode<'a>,
48+
{
49+
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
50+
///
51+
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
52+
///
53+
/// Skips `CONTEXT-SPECIFIC` fields, lower than [`TagNumber`].
54+
pub fn decode_skipping<R: Reader<'a>>(
55+
class: Class,
56+
tag_number: TagNumber,
57+
reader: &mut R,
58+
) -> Result<Option<Self>, T::Error> {
59+
decode_peeking(reader, class, tag_number, |reader| {
60+
Self::decode_checked(class, tag_number, reader)
61+
})
62+
}
63+
64+
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
65+
///
66+
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
67+
pub fn decode_checked<R: Reader<'a>>(
68+
class: Class,
69+
tag_number: TagNumber,
70+
reader: &mut R,
71+
) -> Result<Self, T::Error> {
72+
let any_explicit = Self::decode(reader)?;
73+
74+
if any_explicit.class == class && any_explicit.tag_number == tag_number {
75+
Ok(any_explicit)
76+
} else {
77+
let expected = expected_tag_constructed(class, tag_number, true);
78+
Err(any_explicit.tag().unexpected_error(Some(expected)).into())
79+
}
80+
}
81+
}
82+
83+
impl<'a, T> AnyCustomClassImplicit<T>
84+
where
85+
T: Tagged + DecodeValue<'a> + 'a,
86+
{
87+
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
88+
///
89+
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
90+
///
91+
/// Skips `CONTEXT-SPECIFIC` fields, lower than [`TagNumber`].
92+
pub fn decode_skipping<R: Reader<'a>>(
93+
class: Class,
94+
tag_number: TagNumber,
95+
reader: &mut R,
96+
) -> Result<Option<Self>, T::Error> {
97+
decode_peeking::<_, _, T::Error, _>(reader, class, tag_number, |reader| {
98+
Self::decode_checked(class, tag_number, reader)
99+
})
100+
}
101+
102+
/// Decodes `APPLICATION`, `CONTEXT-SPECIFIC` or `PRIVATE` tagged value.
103+
///
104+
/// Returns Ok only if both [`Class`] and [`TagNumber`] match the decoded tag.
105+
pub fn decode_checked<R: Reader<'a>>(
106+
class: Class,
107+
tag_number: TagNumber,
108+
reader: &mut R,
109+
) -> Result<Self, T::Error> {
110+
let any_implicit = Self::decode(reader)?;
111+
if any_implicit.class == class && any_implicit.tag_number == tag_number {
112+
Ok(any_implicit)
113+
} else {
114+
let expected = expected_tag_constructed(class, tag_number, true);
115+
Err(any_implicit.tag().unexpected_error(Some(expected)).into())
116+
}
117+
}
118+
}
119+
120+
impl<'a, T> Decode<'a> for AnyCustomClassExplicit<T>
121+
where
122+
T: Decode<'a>,
123+
{
124+
type Error = T::Error;
125+
126+
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
127+
let header = Header::decode(reader)?;
128+
129+
if !header.tag.is_constructed() {
130+
return Err(header.tag.non_canonical_error().into());
131+
}
132+
133+
Ok(Self {
134+
value: reader.read_nested(header.length, |reader| T::decode(reader))?,
135+
class: header.tag.class(),
136+
tag_number: header.tag.number(),
137+
})
138+
}
139+
}
140+
141+
impl<'a, T> Decode<'a> for AnyCustomClassImplicit<T>
142+
where
143+
T: Tagged + DecodeValue<'a> + 'a,
144+
{
145+
type Error = T::Error;
146+
147+
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
148+
let header = Header::decode(reader)?;
149+
150+
let value = reader.read_nested(header.length, |reader| T::decode_value(reader, header))?;
151+
152+
if header.tag.is_constructed() != value.tag().is_constructed() {
153+
return Err(header.tag.non_canonical_error().into());
154+
}
155+
Ok(Self {
156+
value,
157+
class: header.tag.class(),
158+
tag_number: header.tag.number(),
159+
constructed: header.tag.is_constructed(),
160+
})
161+
}
162+
}
163+
164+
impl<T> EncodeValue for AnyCustomClassExplicit<T>
165+
where
166+
T: EncodeValue + Tagged,
167+
{
168+
fn value_len(&self) -> Result<Length, Error> {
169+
self.value.encoded_len()
170+
}
171+
172+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
173+
self.value.encode(writer)
174+
}
175+
}
176+
177+
impl<T> EncodeValue for AnyCustomClassImplicit<T>
178+
where
179+
T: EncodeValue + Tagged,
180+
{
181+
fn value_len(&self) -> Result<Length, Error> {
182+
self.value.value_len()
183+
}
184+
185+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
186+
self.value.encode_value(writer)
187+
}
188+
}
189+
190+
impl<T> Tagged for AnyCustomClassExplicit<T> {
191+
fn tag(&self) -> Tag {
192+
expected_tag_constructed(self.class, self.tag_number, true)
193+
}
194+
}
195+
196+
impl<T> Tagged for AnyCustomClassImplicit<T> {
197+
fn tag(&self) -> Tag {
198+
expected_tag_constructed(self.class, self.tag_number, self.constructed)
199+
}
200+
}
201+
202+
/// Attempt to decode a custom class-tagged field with the given
203+
/// helper callback.
204+
fn decode_peeking<'a, F, R: Reader<'a>, E, T>(
205+
reader: &mut R,
206+
expected_class: Class,
207+
expected_number: TagNumber,
208+
f: F,
209+
) -> Result<Option<T>, E>
210+
where
211+
F: FnOnce(&mut R) -> Result<T, E>,
212+
E: From<Error>,
213+
{
214+
while let Some(tag) = Tag::peek_optional(reader)? {
215+
if is_unskippable_tag(tag, expected_class, expected_number) {
216+
break;
217+
} else if tag.number() == expected_number {
218+
return Some(f(reader)).transpose();
219+
} else {
220+
AnyRef::decode(reader)?;
221+
}
222+
}
223+
224+
Ok(None)
225+
}
226+
227+
/// Returns if this tag is of different class than eg. CONTEXT-SPECIFIC
228+
/// or tag number is higher than expected
229+
fn is_unskippable_tag(tag: Tag, expected_class: Class, expected_number: TagNumber) -> bool {
230+
if expected_class != tag.class() {
231+
return true;
232+
}
233+
match expected_class {
234+
Class::Application => tag.number() > expected_number,
235+
Class::ContextSpecific => tag.number() > expected_number,
236+
Class::Private => tag.number() != expected_number,
237+
Class::Universal => tag.number() != expected_number,
238+
}
239+
}
240+
241+
pub(crate) const fn expected_tag_constructed(
242+
class: Class,
243+
number: TagNumber,
244+
constructed: bool,
245+
) -> Tag {
246+
match class {
247+
Class::Application => Tag::Application {
248+
constructed,
249+
number,
250+
},
251+
Class::ContextSpecific => Tag::ContextSpecific {
252+
constructed,
253+
number,
254+
},
255+
Class::Private => Tag::Private {
256+
constructed,
257+
number,
258+
},
259+
Class::Universal => Tag::Null,
260+
}
261+
}

der/src/asn1/application.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! Application field.
2+
3+
use crate::tag::CLASS_APPLICATION;
4+
5+
use super::custom_class::{
6+
CustomClassExplicit, CustomClassExplicitRef, CustomClassImplicit, CustomClassImplicitRef,
7+
};
8+
9+
/// Application class, EXPLICIT
10+
pub type ApplicationExplicit<const TAG: u16, T> = CustomClassExplicit<TAG, T, CLASS_APPLICATION>;
11+
12+
/// Application class, IMPLICIT
13+
pub type ApplicationImplicit<const TAG: u16, T> = CustomClassImplicit<TAG, T, CLASS_APPLICATION>;
14+
15+
/// Application class, reference, EXPLICIT
16+
pub type ApplicationExplicitRef<'a, const TAG: u16, T> =
17+
CustomClassExplicitRef<'a, TAG, T, CLASS_APPLICATION>;
18+
19+
/// Application class, reference, IMPLICIT
20+
pub type ApplicationImplicitRef<'a, const TAG: u16, T> =
21+
CustomClassImplicitRef<'a, TAG, T, CLASS_APPLICATION>;

0 commit comments

Comments
 (0)