Skip to content

Commit a9b80c6

Browse files
authored
der: add macro for APPLICATION, CONTEXT-SPECIFIC and PRIVATE tags (#1819)
1 parent 51f670e commit a9b80c6

File tree

5 files changed

+309
-252
lines changed

5 files changed

+309
-252
lines changed

der/src/asn1.rs

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
mod internal_macros;
66

77
mod any;
8+
mod application;
89
pub(crate) mod bit_string;
910
#[cfg(feature = "alloc")]
1011
mod bmp_string;
@@ -21,6 +22,7 @@ mod octet_string;
2122
mod oid;
2223
mod optional;
2324
mod printable_string;
25+
mod private;
2426
#[cfg(feature = "real")]
2527
mod real;
2628
mod sequence;
@@ -33,6 +35,7 @@ mod videotex_string;
3335

3436
pub use self::{
3537
any::AnyRef,
38+
application::{Application, ApplicationRef},
3639
bit_string::{BitStringIter, BitStringRef},
3740
choice::Choice,
3841
context_specific::{ContextSpecific, ContextSpecificRef},
@@ -43,6 +46,7 @@ pub use self::{
4346
null::Null,
4447
octet_string::OctetStringRef,
4548
printable_string::PrintableStringRef,
49+
private::{Private, PrivateRef},
4650
sequence::{Sequence, SequenceRef},
4751
sequence_of::{SequenceOf, SequenceOfIter},
4852
set_of::{SetOf, SetOfIter},

der/src/asn1/application.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Application class field.
2+
3+
use crate::{
4+
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
5+
Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6+
tag::IsConstructed,
7+
};
8+
use core::cmp::Ordering;
9+
10+
#[cfg(doc)]
11+
use crate::ErrorKind;
12+
13+
impl_custom_class!(Application, Application, "APPLICATION", "0b01000000");
14+
impl_custom_class_ref!(ApplicationRef, Application, "APPLICATION", "0b01000000");

der/src/asn1/context_specific.rs

+15-252
Original file line numberDiff line numberDiff line change
@@ -7,258 +7,21 @@ use crate::{
77
};
88
use core::cmp::Ordering;
99

10-
/// Context-specific field which wraps an owned inner value.
11-
///
12-
/// This type decodes/encodes a field which is specific to a particular context
13-
/// and is identified by a [`TagNumber`].
14-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
15-
pub struct ContextSpecific<T> {
16-
/// Context-specific tag number sans the leading `0b10000000` class
17-
/// identifier bit and `0b100000` constructed flag.
18-
pub tag_number: TagNumber,
19-
20-
/// Tag mode: `EXPLICIT` VS `IMPLICIT`.
21-
pub tag_mode: TagMode,
22-
23-
/// Value of the field.
24-
pub value: T,
25-
}
26-
27-
impl<T> ContextSpecific<T> {
28-
/// Attempt to decode an `EXPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
29-
/// provided [`TagNumber`].
30-
///
31-
/// This method has the following behavior which decodes tag numbers one by one
32-
/// in extension fields, which are denoted in an ASN.1 schema using
33-
/// the `...` ellipsis extension marker:
34-
///
35-
/// - Returns `Ok(None)` if anything other than a [`ContextSpecific`] field
36-
/// is encountered.
37-
/// - Returns `Ok(None)` if a [`ContextSpecific`] field with a different tag
38-
/// number is encountered. These fields are not consumed in this case.
39-
/// - Returns `Err(ErrorKind::Noncanonical)` if constructed bit is primitive.
40-
/// - Returns `Ok(Some(..))` if tag number matches.
41-
pub fn decode_explicit<'a, R: Reader<'a>>(
42-
reader: &mut R,
43-
tag_number: TagNumber,
44-
) -> Result<Option<Self>, T::Error>
45-
where
46-
T: Decode<'a>,
47-
{
48-
if !Tag::peek_matches(reader, Class::ContextSpecific, tag_number)? {
49-
return Ok(None);
50-
}
51-
Ok(Some(Self::decode(reader)?))
52-
}
53-
54-
/// Attempt to decode an `IMPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
55-
/// provided [`TagNumber`].
56-
///
57-
/// This method otherwise behaves the same as `decode_explicit`,
58-
/// but should be used in cases where the particular fields are `IMPLICIT`
59-
/// as opposed to `EXPLICIT`.
60-
///
61-
/// Differences from `EXPLICIT`:
62-
/// - Returns `Err(ErrorKind::Noncanonical)` if constructed bit
63-
/// does not match constructed bit of the base encoding.
64-
pub fn decode_implicit<'a, R: Reader<'a>>(
65-
reader: &mut R,
66-
tag_number: TagNumber,
67-
) -> Result<Option<Self>, T::Error>
68-
where
69-
T: DecodeValue<'a> + IsConstructed,
70-
{
71-
// Peek tag number
72-
if !Tag::peek_matches(reader, Class::ContextSpecific, tag_number)? {
73-
return Ok(None);
74-
}
75-
// Decode IMPLICIT header
76-
let header = Header::decode(reader)?;
77-
78-
// read_nested checks if header matches decoded length
79-
let value = reader.read_nested(header.length, |reader| {
80-
// Decode inner IMPLICIT value
81-
T::decode_value(reader, header)
82-
})?;
83-
84-
// the encoding shall be constructed if the base encoding is constructed
85-
if header.tag.is_constructed() != T::CONSTRUCTED {
86-
return Err(header.tag.non_canonical_error().into());
87-
}
88-
89-
Ok(Some(Self {
90-
tag_number,
91-
tag_mode: TagMode::Implicit,
92-
value,
93-
}))
94-
}
95-
}
96-
97-
impl<'a, T> Choice<'a> for ContextSpecific<T>
98-
where
99-
T: Decode<'a> + Tagged,
100-
{
101-
fn can_decode(tag: Tag) -> bool {
102-
tag.is_context_specific()
103-
}
104-
}
105-
106-
impl<'a, T> Decode<'a> for ContextSpecific<T>
107-
where
108-
T: Decode<'a>,
109-
{
110-
type Error = T::Error;
111-
112-
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
113-
// Decode EXPLICIT header
114-
let header = Header::decode(reader)?;
115-
116-
// encoding shall be constructed
117-
if !header.tag.is_constructed() {
118-
return Err(header.tag.non_canonical_error().into());
119-
}
120-
match header.tag {
121-
Tag::ContextSpecific { number, .. } => Ok(Self {
122-
tag_number: number,
123-
tag_mode: TagMode::default(),
124-
value: reader.read_nested(header.length, |reader| {
125-
// Decode inner tag-length-value of EXPLICIT
126-
T::decode(reader)
127-
})?,
128-
}),
129-
tag => Err(tag.unexpected_error(None).into()),
130-
}
131-
}
132-
}
133-
134-
impl<T> EncodeValue for ContextSpecific<T>
135-
where
136-
T: EncodeValue + Tagged,
137-
{
138-
fn value_len(&self) -> Result<Length, Error> {
139-
match self.tag_mode {
140-
TagMode::Explicit => self.value.encoded_len(),
141-
TagMode::Implicit => self.value.value_len(),
142-
}
143-
}
144-
145-
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
146-
match self.tag_mode {
147-
TagMode::Explicit => self.value.encode(writer),
148-
TagMode::Implicit => self.value.encode_value(writer),
149-
}
150-
}
151-
}
152-
153-
impl<T> Tagged for ContextSpecific<T>
154-
where
155-
T: Tagged,
156-
{
157-
fn tag(&self) -> Tag {
158-
let constructed = match self.tag_mode {
159-
// ISO/IEC 8825-1:2021
160-
// 8.14.3 If implicit tagging (see Rec. ITU-T X.680 | ISO/IEC 8824-1, 31.2.7) was not used in the definition of the type, the
161-
// encoding shall be constructed and the contents octets shall be the complete base encoding [Encode].
162-
TagMode::Explicit => true,
163-
164-
// ISO/IEC 8825-1:2021
165-
// 8.14.4 If implicit tagging was used in the definition of the type, then:
166-
// a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise; and
167-
// b) the contents octets shall be the same as the contents octets [EncodeValue] of the base encoding.
168-
//
169-
// TODO(dishmaker): use IsConstructed trait for IMPLICIT
170-
TagMode::Implicit => self.value.tag().is_constructed(),
171-
};
172-
173-
Tag::ContextSpecific {
174-
number: self.tag_number,
175-
constructed,
176-
}
177-
}
178-
}
179-
180-
impl<'a, T> TryFrom<AnyRef<'a>> for ContextSpecific<T>
181-
where
182-
T: Decode<'a>,
183-
{
184-
type Error = T::Error;
185-
186-
fn try_from(any: AnyRef<'a>) -> Result<ContextSpecific<T>, Self::Error> {
187-
match any.tag() {
188-
Tag::ContextSpecific {
189-
number,
190-
constructed: true,
191-
} => Ok(Self {
192-
tag_number: number,
193-
tag_mode: TagMode::default(),
194-
value: T::from_der(any.value())?,
195-
}),
196-
tag => Err(tag.unexpected_error(None).into()),
197-
}
198-
}
199-
}
200-
201-
impl<T> ValueOrd for ContextSpecific<T>
202-
where
203-
T: EncodeValue + ValueOrd + Tagged,
204-
{
205-
fn value_cmp(&self, other: &Self) -> Result<Ordering, Error> {
206-
match self.tag_mode {
207-
TagMode::Explicit => self.der_cmp(other),
208-
TagMode::Implicit => self.value_cmp(other),
209-
}
210-
}
211-
}
212-
213-
/// Context-specific field reference.
214-
///
215-
/// This type encodes a field which is specific to a particular context
216-
/// and is identified by a [`TagNumber`].
217-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
218-
pub struct ContextSpecificRef<'a, T> {
219-
/// Context-specific tag number sans the leading `0b10000000` class
220-
/// identifier bit and `0b100000` constructed flag.
221-
pub tag_number: TagNumber,
222-
223-
/// Tag mode: `EXPLICIT` VS `IMPLICIT`.
224-
pub tag_mode: TagMode,
225-
226-
/// Value of the field.
227-
pub value: &'a T,
228-
}
229-
230-
impl<'a, T> ContextSpecificRef<'a, T> {
231-
/// Convert to a [`ContextSpecific`].
232-
fn encoder(&self) -> ContextSpecific<EncodeValueRef<'a, T>> {
233-
ContextSpecific {
234-
tag_number: self.tag_number,
235-
tag_mode: self.tag_mode,
236-
value: EncodeValueRef(self.value),
237-
}
238-
}
239-
}
240-
241-
impl<T> EncodeValue for ContextSpecificRef<'_, T>
242-
where
243-
T: EncodeValue + Tagged,
244-
{
245-
fn value_len(&self) -> Result<Length, Error> {
246-
self.encoder().value_len()
247-
}
248-
249-
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
250-
self.encoder().encode_value(writer)
251-
}
252-
}
253-
254-
impl<T> Tagged for ContextSpecificRef<'_, T>
255-
where
256-
T: Tagged,
257-
{
258-
fn tag(&self) -> Tag {
259-
self.encoder().tag()
260-
}
261-
}
10+
#[cfg(doc)]
11+
use crate::ErrorKind;
12+
13+
impl_custom_class!(
14+
ContextSpecific,
15+
ContextSpecific,
16+
"CONTEXT-SPECIFIC",
17+
"0b10000000"
18+
);
19+
impl_custom_class_ref!(
20+
ContextSpecificRef,
21+
ContextSpecific,
22+
"CONTEXT-SPECIFIC",
23+
"0b10000000"
24+
);
26225

26326
#[cfg(test)]
26427
#[allow(clippy::unwrap_used)]

0 commit comments

Comments
 (0)