Skip to content

Commit a699cb1

Browse files
Harris Kaufmanndishmaker
authored andcommitted
support private tags and tag numbers >30 that are stored in long form
1 parent 61db930 commit a699cb1

File tree

20 files changed

+734
-293
lines changed

20 files changed

+734
-293
lines changed

der/src/asn1.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod octet_string;
2020
mod oid;
2121
mod optional;
2222
mod printable_string;
23+
mod private;
2324
#[cfg(feature = "real")]
2425
mod real;
2526
mod sequence;
@@ -41,6 +42,7 @@ pub use self::{
4142
null::Null,
4243
octet_string::OctetStringRef,
4344
printable_string::PrintableStringRef,
45+
private::{Private, PrivateRef},
4446
sequence::{Sequence, SequenceRef},
4547
sequence_of::{SequenceOf, SequenceOfIter},
4648
set_of::{SetOf, SetOfIter},

der/src/asn1/context_specific.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ impl<T> ContextSpecific<T> {
8989
F: FnOnce(&mut R) -> Result<Self, E>,
9090
E: From<Error>,
9191
{
92-
while let Some(octet) = reader.peek_byte() {
93-
let tag = Tag::try_from(octet)?;
94-
92+
while let Some(tag) = Tag::peek_optional(reader)? {
9593
if !tag.is_context_specific() || (tag.number() > tag_number) {
9694
break;
9795
} else if tag.number() == tag_number {

der/src/asn1/optional.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ where
1010
type Error = T::Error;
1111

1212
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Option<T>, Self::Error> {
13-
if let Some(byte) = reader.peek_byte() {
14-
if T::can_decode(Tag::try_from(byte)?) {
13+
if let Some(tag) = Tag::peek_optional(reader)? {
14+
if T::can_decode(tag) {
1515
return T::decode(reader).map(Some);
1616
}
1717
}

der/src/asn1/private.rs

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
//! Private field.
2+
3+
use crate::{
4+
asn1::AnyRef, Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error,
5+
Header, Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6+
};
7+
use core::cmp::Ordering;
8+
9+
/// Private field which wraps an owned inner value.
10+
///
11+
/// This type encodes a field which is whose meaning is specific to a given
12+
/// enterprise and is identified by a [`TagNumber`].
13+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
14+
pub struct Private<T> {
15+
/// Private tag number sans the leading `0b10000000` class
16+
/// identifier bit and `0b100000` constructed flag.
17+
pub tag_number: TagNumber,
18+
19+
/// Tag mode: `EXPLICIT` VS `IMPLICIT`.
20+
pub tag_mode: TagMode,
21+
22+
/// Value of the field.
23+
pub value: T,
24+
}
25+
26+
impl<T> Private<T> {
27+
/// Attempt to decode an `EXPLICIT` ASN.1 `PRIVATE` field with the
28+
/// provided [`TagNumber`].
29+
///
30+
/// This method has the following behavior:
31+
///
32+
/// - Returns `Ok(None)` if a [`Private`] field with a different tag
33+
/// number is encountered. These fields are not consumed in this case,
34+
/// allowing a field with a different tag number to be omitted, then the
35+
/// matching field consumed as a follow-up.
36+
/// - Returns `Ok(None)` if anything other than a [`Private`] field
37+
/// is encountered.
38+
pub fn decode_explicit<'a, R: Reader<'a>>(
39+
reader: &mut R,
40+
tag_number: TagNumber,
41+
) -> Result<Option<Self>, T::Error>
42+
where
43+
T: Decode<'a>,
44+
{
45+
Self::decode_with(reader, tag_number, |reader| Self::decode(reader))
46+
}
47+
48+
/// Attempt to decode an `IMPLICIT` ASN.1 `PRIVATE` field with the
49+
/// provided [`TagNumber`].
50+
///
51+
/// This method otherwise behaves the same as `decode_explicit`,
52+
/// but should be used in cases where the particular fields are `IMPLICIT`
53+
/// as opposed to `EXPLICIT`.
54+
pub fn decode_implicit<'a, R: Reader<'a>>(
55+
reader: &mut R,
56+
tag_number: TagNumber,
57+
) -> Result<Option<Self>, T::Error>
58+
where
59+
T: DecodeValue<'a> + Tagged,
60+
{
61+
Self::decode_with::<_, _, T::Error>(reader, tag_number, |reader| {
62+
let header = Header::decode(reader)?;
63+
let value = T::decode_value(reader, header)?;
64+
65+
if header.tag.is_constructed() != value.tag().is_constructed() {
66+
return Err(header.tag.non_canonical_error().into());
67+
}
68+
69+
Ok(Self {
70+
tag_number,
71+
tag_mode: TagMode::Implicit,
72+
value,
73+
})
74+
})
75+
}
76+
77+
/// Attempt to decode a private field with the given
78+
/// helper callback.
79+
fn decode_with<'a, F, R: Reader<'a>, E>(
80+
reader: &mut R,
81+
tag_number: TagNumber,
82+
f: F,
83+
) -> Result<Option<Self>, E>
84+
where
85+
F: FnOnce(&mut R) -> Result<Self, E>,
86+
E: From<Error>,
87+
{
88+
while let Some(tag) = Tag::peek_optional(reader)? {
89+
if !tag.is_private() || (tag.number() != tag_number) {
90+
break;
91+
} else {
92+
return Some(f(reader)).transpose();
93+
}
94+
}
95+
96+
Ok(None)
97+
}
98+
}
99+
100+
impl<'a, T> Choice<'a> for Private<T>
101+
where
102+
T: Decode<'a> + Tagged,
103+
{
104+
fn can_decode(tag: Tag) -> bool {
105+
tag.is_private()
106+
}
107+
}
108+
109+
impl<'a, T> Decode<'a> for Private<T>
110+
where
111+
T: Decode<'a>,
112+
{
113+
type Error = T::Error;
114+
115+
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
116+
let header = Header::decode(reader)?;
117+
118+
match header.tag {
119+
Tag::Private {
120+
number,
121+
constructed: true,
122+
} => Ok(Self {
123+
tag_number: number,
124+
tag_mode: TagMode::default(),
125+
value: reader.read_nested(header.length, |reader| T::decode(reader))?,
126+
}),
127+
tag => Err(tag.unexpected_error(None).into()),
128+
}
129+
}
130+
}
131+
132+
impl<T> EncodeValue for Private<T>
133+
where
134+
T: EncodeValue + Tagged,
135+
{
136+
fn value_len(&self) -> Result<Length, Error> {
137+
match self.tag_mode {
138+
TagMode::Explicit => self.value.encoded_len(),
139+
TagMode::Implicit => self.value.value_len(),
140+
}
141+
}
142+
143+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
144+
match self.tag_mode {
145+
TagMode::Explicit => self.value.encode(writer),
146+
TagMode::Implicit => self.value.encode_value(writer),
147+
}
148+
}
149+
}
150+
151+
impl<T> Tagged for Private<T>
152+
where
153+
T: Tagged,
154+
{
155+
fn tag(&self) -> Tag {
156+
let constructed = match self.tag_mode {
157+
TagMode::Explicit => true,
158+
TagMode::Implicit => self.value.tag().is_constructed(),
159+
};
160+
161+
Tag::Private {
162+
number: self.tag_number,
163+
constructed,
164+
}
165+
}
166+
}
167+
168+
impl<'a, T> TryFrom<AnyRef<'a>> for Private<T>
169+
where
170+
T: Decode<'a>,
171+
{
172+
type Error = T::Error;
173+
174+
fn try_from(any: AnyRef<'a>) -> Result<Private<T>, Self::Error> {
175+
match any.tag() {
176+
Tag::Private {
177+
number,
178+
constructed: true,
179+
} => Ok(Self {
180+
tag_number: number,
181+
tag_mode: TagMode::default(),
182+
value: T::from_der(any.value())?,
183+
}),
184+
tag => Err(tag.unexpected_error(None).into()),
185+
}
186+
}
187+
}
188+
189+
impl<T> ValueOrd for Private<T>
190+
where
191+
T: EncodeValue + ValueOrd + Tagged,
192+
{
193+
fn value_cmp(&self, other: &Self) -> Result<Ordering, Error> {
194+
match self.tag_mode {
195+
TagMode::Explicit => self.der_cmp(other),
196+
TagMode::Implicit => self.value_cmp(other),
197+
}
198+
}
199+
}
200+
201+
/// Private field reference.
202+
///
203+
/// This type encodes a field which is whose meaning is specific to a given
204+
/// enterprise and is identified by a [`TagNumber`].
205+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
206+
pub struct PrivateRef<'a, T> {
207+
/// Private tag number sans the leading `0b11000000` class
208+
/// identifier bit and `0b100000` constructed flag.
209+
pub tag_number: TagNumber,
210+
211+
/// Tag mode: `EXPLICIT` VS `IMPLICIT`.
212+
pub tag_mode: TagMode,
213+
214+
/// Value of the field.
215+
pub value: &'a T,
216+
}
217+
218+
impl<'a, T> PrivateRef<'a, T> {
219+
/// Convert to a [`Private`].
220+
fn encoder(&self) -> Private<EncodeValueRef<'a, T>> {
221+
Private {
222+
tag_number: self.tag_number,
223+
tag_mode: self.tag_mode,
224+
value: EncodeValueRef(self.value),
225+
}
226+
}
227+
}
228+
229+
impl<'a, T> EncodeValue for PrivateRef<'a, T>
230+
where
231+
T: EncodeValue + Tagged,
232+
{
233+
fn value_len(&self) -> Result<Length, Error> {
234+
self.encoder().value_len()
235+
}
236+
237+
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
238+
self.encoder().encode_value(writer)
239+
}
240+
}
241+
242+
impl<'a, T> Tagged for PrivateRef<'a, T>
243+
where
244+
T: Tagged,
245+
{
246+
fn tag(&self) -> Tag {
247+
self.encoder().tag()
248+
}
249+
}
250+
251+
#[cfg(test)]
252+
#[allow(clippy::unwrap_used)]
253+
mod tests {
254+
use super::Private;
255+
use crate::{asn1::BitStringRef, Decode, Encode, SliceReader, TagMode, TagNumber};
256+
use hex_literal::hex;
257+
258+
// Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
259+
const EXAMPLE_BYTES: &[u8] =
260+
&hex!("A123032100A3A7EAE3A8373830BC47E1167BC50E1DB551999651E0E2DC587623438EAC3F31");
261+
262+
#[test]
263+
fn round_trip() {
264+
let field = Private::<BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
265+
assert_eq!(field.tag_number.value(), 1);
266+
assert_eq!(
267+
field.value,
268+
BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
269+
);
270+
271+
let mut buf = [0u8; 128];
272+
let encoded = field.encode_to_slice(&mut buf).unwrap();
273+
assert_eq!(encoded, EXAMPLE_BYTES);
274+
}
275+
276+
#[test]
277+
fn private_with_explicit_field() {
278+
let tag_number = TagNumber::new(0);
279+
280+
// Empty message
281+
let mut reader = SliceReader::new(&[]).unwrap();
282+
assert_eq!(
283+
Private::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
284+
None
285+
);
286+
287+
// Message containing a non-private type
288+
let mut reader = SliceReader::new(&hex!("020100")).unwrap();
289+
assert_eq!(
290+
Private::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
291+
None
292+
);
293+
294+
// Message containing an EXPLICIT private field
295+
let mut reader = SliceReader::new(&hex!("A003020100")).unwrap();
296+
let field = Private::<u8>::decode_explicit(&mut reader, tag_number)
297+
.unwrap()
298+
.unwrap();
299+
300+
assert_eq!(field.tag_number, tag_number);
301+
assert_eq!(field.tag_mode, TagMode::Explicit);
302+
assert_eq!(field.value, 0);
303+
}
304+
305+
#[test]
306+
fn private_with_implicit_field() {
307+
// From RFC8410 Section 10.3:
308+
// <https://datatracker.ietf.org/doc/html/rfc8410#section-10.3>
309+
//
310+
// 81 33: [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B
311+
// 96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66
312+
// E1
313+
let private_implicit_bytes =
314+
hex!("81210019BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
315+
316+
let tag_number = TagNumber::new(1);
317+
318+
let mut reader = SliceReader::new(&private_implicit_bytes).unwrap();
319+
let field = Private::<BitStringRef<'_>>::decode_implicit(&mut reader, tag_number)
320+
.unwrap()
321+
.unwrap();
322+
323+
assert_eq!(field.tag_number, tag_number);
324+
assert_eq!(field.tag_mode, TagMode::Implicit);
325+
assert_eq!(
326+
field.value.as_bytes().unwrap(),
327+
&private_implicit_bytes[3..]
328+
);
329+
}
330+
331+
#[test]
332+
fn private_skipping_unknown_field() {
333+
let tag = TagNumber::new(1);
334+
let mut reader = SliceReader::new(&hex!("A003020100A103020101")).unwrap();
335+
let field = Private::<u8>::decode_explicit(&mut reader, tag)
336+
.unwrap()
337+
.unwrap();
338+
assert_eq!(field.value, 1);
339+
}
340+
341+
#[test]
342+
fn private_returns_none_on_greater_tag_number() {
343+
let tag = TagNumber::new(0);
344+
let mut reader = SliceReader::new(&hex!("A103020101")).unwrap();
345+
assert_eq!(
346+
Private::<u8>::decode_explicit(&mut reader, tag).unwrap(),
347+
None
348+
);
349+
}
350+
}

der/src/encode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ where
7373
{
7474
/// Compute the length of this value in bytes when encoded as ASN.1 DER.
7575
fn encoded_len(&self) -> Result<Length> {
76-
self.value_len().and_then(|len| len.for_tlv())
76+
self.value_len().and_then(|len| len.for_tlv(self.tag()))
7777
}
7878

7979
/// Encode this value as ASN.1 DER using the provided [`Writer`].

0 commit comments

Comments
 (0)