Skip to content

Commit 0aa605c

Browse files
committed
der: add ApplicationExplicit, ContextSpecificExplicit, PrivateExplicit wrappers
1 parent 7674d0c commit 0aa605c

File tree

5 files changed

+128
-10
lines changed

5 files changed

+128
-10
lines changed

der/src/asn1.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ mod videotex_string;
3535

3636
pub use self::{
3737
any::AnyRef,
38-
application::{Application, ApplicationRef},
38+
application::{Application, ApplicationExplicit, ApplicationRef},
3939
bit_string::{BitStringIter, BitStringRef},
4040
choice::Choice,
41-
context_specific::{ContextSpecific, ContextSpecificRef},
41+
context_specific::{ContextSpecific, ContextSpecificExplicit, ContextSpecificRef},
4242
general_string::GeneralStringRef,
4343
generalized_time::GeneralizedTime,
4444
ia5_string::Ia5StringRef,
4545
integer::{int::IntRef, uint::UintRef},
4646
null::Null,
4747
octet_string::OctetStringRef,
4848
printable_string::PrintableStringRef,
49-
private::{Private, PrivateRef},
49+
private::{Private, PrivateExplicit, PrivateRef},
5050
sequence::{Sequence, SequenceRef},
5151
sequence_of::{SequenceOf, SequenceOfIter},
5252
set_of::{SetOf, SetOfIter},

der/src/asn1/application.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
33
use crate::{
44
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
5-
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6-
tag::IsConstructed,
5+
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6+
asn1::AnyRef, tag::IsConstructed,
77
};
88
use core::cmp::Ordering;
99

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

1313
impl_custom_class!(Application, Application, "APPLICATION", "0b01000000");
1414
impl_custom_class_ref!(ApplicationRef, Application, "APPLICATION", "0b01000000");
15+
16+
impl_custom_class_explicit!(
17+
ApplicationExplicit,
18+
Application,
19+
"APPLICATION",
20+
"0b01000000"
21+
);

der/src/asn1/context_specific.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
33
use crate::{
44
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
5-
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6-
tag::IsConstructed,
5+
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6+
asn1::AnyRef, tag::IsConstructed,
77
};
88
use core::cmp::Ordering;
99

@@ -23,13 +23,23 @@ impl_custom_class_ref!(
2323
"0b10000000"
2424
);
2525

26+
impl_custom_class_explicit!(
27+
ContextSpecificExplicit,
28+
ContextSpecific,
29+
"CONTEXT-SPECIFIC",
30+
"0b10000000"
31+
);
32+
2633
#[cfg(test)]
2734
#[allow(clippy::unwrap_used)]
2835
mod tests {
2936
use super::ContextSpecific;
3037
use crate::{
3138
Decode, Encode, SliceReader, TagMode, TagNumber,
32-
asn1::{BitStringRef, ContextSpecificRef, SetOf, Utf8StringRef},
39+
asn1::{
40+
BitStringRef, ContextSpecificRef, SetOf, Utf8StringRef,
41+
context_specific::ContextSpecificExplicit,
42+
},
3343
};
3444
use hex_literal::hex;
3545

@@ -194,4 +204,34 @@ mod tests {
194204
assert_eq!(field.value.get(0).cloned(), Some(hello));
195205
assert_eq!(field.value.get(1).cloned(), Some(world));
196206
}
207+
208+
#[test]
209+
fn round_trip_explicit() {
210+
let field =
211+
ContextSpecificExplicit::<1, BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
212+
assert_eq!(
213+
field.value,
214+
BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
215+
);
216+
assert_eq!(
217+
ContextSpecificExplicit::<1, BitStringRef<'_>>::tag_mode(),
218+
TagMode::Explicit
219+
);
220+
assert_eq!(
221+
ContextSpecificExplicit::<1, BitStringRef<'_>>::tag_number(),
222+
TagNumber(1)
223+
);
224+
225+
let mut buf = [0u8; 128];
226+
let encoded = field.encode_to_slice(&mut buf).unwrap();
227+
assert_eq!(encoded, EXAMPLE_BYTES);
228+
229+
// should not decode as tag CONTEXT-SPECIFIC [2]
230+
assert!(ContextSpecificExplicit::<2, BitStringRef<'_>>::from_der(EXAMPLE_BYTES).is_err());
231+
232+
// should be different than CONTEXT-SPECIFIC [1]
233+
let invalid_field = ContextSpecificExplicit::<2, BitStringRef<'_>> { value: field.value };
234+
let invalid_encoded = invalid_field.encode_to_slice(&mut buf).unwrap();
235+
assert_ne!(invalid_encoded, EXAMPLE_BYTES);
236+
}
197237
}

der/src/asn1/internal_macros.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,73 @@ macro_rules! impl_custom_class_ref {
335335
}
336336
};
337337
}
338+
339+
macro_rules! impl_custom_class_explicit {
340+
($explicit_class_type_name: ident, $class_enum_name: ident, $asn1_class_name: literal, $class_bits_str: literal) => {
341+
#[doc = concat!("`", $asn1_class_name, "` field reference, with const `EXPLICIT` encoding.")]
342+
///
343+
///
344+
/// This type encodes a field which is specific to a particular context
345+
/// and is identified by a [`TagNumber`].
346+
///
347+
/// Inner value might implement [`Encode`], [`Decode`] or both.
348+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
349+
pub struct $explicit_class_type_name<const NUMBER: u32, T> {
350+
/// Inner value might implement [`Encode`], [`Decode`] or both.
351+
pub value: T,
352+
}
353+
354+
impl<const NUMBER: u32, T> $explicit_class_type_name<NUMBER, T> {
355+
#[doc = concat!("Returns const [`TagNumber`], associated with this `EXPLICIT` `", $asn1_class_name, "` wrapper.")]
356+
pub const fn tag_number() -> TagNumber {
357+
TagNumber(NUMBER)
358+
}
359+
360+
#[doc = concat!("Returns const [`TagMode::Explicit`], associated with this `EXPLICIT` `", $asn1_class_name, "` wrapper.")]
361+
pub const fn tag_mode() -> TagMode {
362+
TagMode::Explicit
363+
}
364+
}
365+
366+
impl<const NUMBER: u32, T> EncodeValue for $explicit_class_type_name<NUMBER, T>
367+
where
368+
T: Encode,
369+
{
370+
fn value_len(&self) -> Result<Length, Error> {
371+
self.value.encoded_len()
372+
}
373+
374+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
375+
// Encode EXPLICIT value (with tag and length)
376+
self.value.encode(writer)
377+
}
378+
}
379+
380+
impl<'a, const NUMBER: u32, T> DecodeValue<'a> for $explicit_class_type_name<NUMBER, T>
381+
where
382+
T: Decode<'a>,
383+
{
384+
type Error = T::Error;
385+
386+
fn decode_value<R: Reader<'a>>(
387+
reader: &mut R,
388+
header: Header,
389+
) -> Result<Self, Self::Error> {
390+
// encoding shall be constructed
391+
if !header.tag.is_constructed() {
392+
return Err(reader.error(header.tag.non_canonical_error()).into());
393+
}
394+
Ok(Self {
395+
value: T::decode(reader)?,
396+
})
397+
}
398+
}
399+
400+
impl<const NUMBER: u32, T> FixedTag for $explicit_class_type_name<NUMBER, T> {
401+
const TAG: Tag = Tag::$class_enum_name {
402+
constructed: true,
403+
number: Self::tag_number(),
404+
};
405+
}
406+
};
407+
}

der/src/asn1/private.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
33
use crate::{
44
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, EncodingRules,
5-
Error, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6-
tag::IsConstructed,
5+
Error, FixedTag, Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6+
asn1::AnyRef, tag::IsConstructed,
77
};
88
use core::cmp::Ordering;
99

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

1313
impl_custom_class!(Private, Private, "PRIVATE", "0b11000000");
1414
impl_custom_class_ref!(PrivateRef, Private, "PRIVATE", "0b11000000");
15+
impl_custom_class_explicit!(PrivateExplicit, Private, "PRIVATE", "0b11000000");

0 commit comments

Comments
 (0)