Skip to content

Commit 863f59d

Browse files
authored
der: add TagNumber type, make Tag structured (#464)
Adds a `TagNumber` type for representing the lower 5 bits of a Tag, where bits 8/7 identify the class and bit 6 identifies primitive vs constructed forms. Changes `Tag` from being `repr(u8)` to a structured representation with `Application`, `ContextSpecific`, and `Private` variants, each of which includes a `TagNumber`. This enables support for all of these classes for tag numbers up to 30, which is the maximum value which can be represented with a single leading identifier octet. This change enables the `ContextSpecific` struct to have public members, as the tag number invariant can now be maintained by the `TagNumber` type instead.
1 parent 92fe26d commit 863f59d

File tree

5 files changed

+243
-260
lines changed

5 files changed

+243
-260
lines changed

der/derive/src/choice.rs

-4
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,6 @@ impl DeriveChoice {
224224
/// ASN.1 `CHOICE` alternative: one of the ASN.1 types comprising the `CHOICE`
225225
/// which maps to an enum variant.
226226
struct Alternative {
227-
/// ASN.1 type for this alternative.
228-
pub asn1_type: Asn1Type,
229-
230227
/// [`Ident`] for the corresponding enum variant.
231228
pub ident: Ident,
232229

@@ -246,7 +243,6 @@ impl Alternative {
246243
};
247244

248245
let alternative = Self {
249-
asn1_type,
250246
ident: variant.ident.clone(),
251247
field_type,
252248
};

der/src/asn1/context_specific.rs

+21-56
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,32 @@
22
33
use crate::{
44
Any, Choice, Decodable, Encodable, Encoder, Error, ErrorKind, Header, Length, Result, Tag,
5+
TagNumber,
56
};
67
use core::convert::TryFrom;
78

89
/// Context-specific field.
910
///
1011
/// This type encodes a field which is specific to a particular context,
11-
/// and has a special "context-specific tag" (presently 0-15 supported).
12+
/// and is identified by a [`TagNumber`].
1213
///
1314
/// Any context-specific field can be decoded/encoded with this type.
1415
/// The intended use is to dynamically dispatch off of the context-specific
15-
/// tag when decoding, which allows support for extensions, which are denoted
16-
/// in an ASN.1 schema using the `...` ellipsis extension marker.
17-
///
18-
///
16+
/// tag number when decoding, which allows support for extensions, which are
17+
/// denoted in an ASN.1 schema using the `...` ellipsis extension marker.
1918
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
2019
pub struct ContextSpecific<'a> {
21-
/// Context-specific tag value sans the leading `0b10000000` class
20+
/// Context-specific tag number sans the leading `0b10000000` class
2221
/// identifier bit and `0b100000` constructed flag.
23-
pub(crate) tag: u8,
22+
pub tag_number: TagNumber,
2423

2524
/// Value of the field.
26-
pub(crate) value: Any<'a>,
27-
}
28-
29-
impl<'a> ContextSpecific<'a> {
30-
/// Create a new context-specific field.
31-
///
32-
/// The tag value includes only lower 6-bits of the context specific tag,
33-
/// sans the leading `10` high bits identifying the context-specific tag
34-
/// class as well as the constructed flag.
35-
pub fn new(tag: u8, value: Any<'a>) -> Result<Self> {
36-
// Ensure we consider the context-specific tag valid
37-
Tag::context_specific(tag)?;
38-
39-
Ok(Self { tag, value })
40-
}
41-
42-
/// Get the context-specific tag for this field.
43-
///
44-
/// The tag value includes only lower 6-bits of the context specific tag,
45-
/// sans the leading `10` high bits identifying the context-specific tag
46-
/// class as well as the constructed flag.
47-
pub fn tag(self) -> u8 {
48-
self.tag
49-
}
50-
51-
/// Get the value of this context-specific tag.
52-
pub fn value(self) -> Any<'a> {
53-
self.value
54-
}
25+
pub value: Any<'a>,
5526
}
5627

5728
impl<'a> Choice<'a> for ContextSpecific<'a> {
5829
fn can_decode(tag: Tag) -> bool {
59-
tag.is_context_specific()
30+
matches!(tag, Tag::ContextSpecific(_))
6031
}
6132
}
6233

@@ -66,7 +37,7 @@ impl<'a> Encodable for ContextSpecific<'a> {
6637
}
6738

6839
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
69-
let tag = Tag::context_specific(self.tag)?;
40+
let tag = Tag::ContextSpecific(self.tag_number);
7041
Header::new(tag, self.value.encoded_len()?)?.encode(encoder)?;
7142
self.value.encode(encoder)
7243
}
@@ -82,21 +53,17 @@ impl<'a> TryFrom<Any<'a>> for ContextSpecific<'a> {
8253
type Error = Error;
8354

8455
fn try_from(any: Any<'a>) -> Result<ContextSpecific<'a>> {
85-
let tag = if any.tag().is_context_specific() {
86-
(any.tag() as u8)
87-
.checked_sub(0xA0)
88-
.ok_or(ErrorKind::Overflow)?
89-
} else {
90-
return Err(ErrorKind::UnexpectedTag {
56+
match any.tag() {
57+
Tag::ContextSpecific(tag_number) => Ok(Self {
58+
tag_number,
59+
value: Any::from_der(any.as_bytes())?,
60+
}),
61+
actual => Err(ErrorKind::UnexpectedTag {
9162
expected: None,
92-
actual: any.tag(),
63+
actual,
9364
}
94-
.into());
95-
};
96-
97-
let value = Any::from_der(any.as_bytes())?;
98-
99-
Self::new(tag, value)
65+
.into()),
66+
}
10067
}
10168
}
10269

@@ -113,11 +80,9 @@ mod tests {
11380
#[test]
11481
fn round_trip() {
11582
let field = ContextSpecific::from_der(EXAMPLE_BYTES).unwrap();
116-
assert_eq!(field.tag(), 1);
117-
118-
let value = field.value();
119-
assert_eq!(value.tag(), Tag::BitString);
120-
assert_eq!(value.as_bytes(), &EXAMPLE_BYTES[5..]);
83+
assert_eq!(field.tag_number.value(), 1);
84+
assert_eq!(field.value.tag(), Tag::BitString);
85+
assert_eq!(field.value.as_bytes(), &EXAMPLE_BYTES[5..]);
12186

12287
let mut buf = [0u8; 128];
12388
let encoded = field.encode_to_slice(&mut buf).unwrap();

der/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
//!
66
//! This crate provides a `no_std`-friendly implementation of a subset of ASN.1
77
//! DER necessary for decoding/encoding various cryptography-related formats
8-
//! implemented as part of the [RustCrypto] project, e.g. the [`pkcs8`] crate.
8+
//! implemented as part of the [RustCrypto] project, e.g. the [`pkcs5`] and
9+
//! [`pkcs8`] crates.
910
//!
1011
//! The core implementation avoids any heap usage (with convenience methods
1112
//! that allocate gated under the off-by-default `alloc` feature).
@@ -315,6 +316,7 @@
315316
//!
316317
//! [X.690]: https://www.itu.int/rec/T-REC-X.690/
317318
//! [RustCrypto]: https://github.com/rustcrypto
319+
//! [`pkcs5`]: https://docs.rs/pkcs5/
318320
//! [`pkcs8`]: https://docs.rs/pkcs8/
319321
//! [RustCrypto/utils#370]: https://github.com/RustCrypto/utils/issues/370
320322
//! [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2
@@ -376,7 +378,7 @@ pub use crate::{
376378
header::Header,
377379
length::Length,
378380
message::Message,
379-
tag::{Class, Tag, Tagged},
381+
tag::{Class, Tag, TagNumber, Tagged},
380382
};
381383

382384
pub(crate) use crate::byte_slice::ByteSlice;

0 commit comments

Comments
 (0)