Skip to content

Commit 9467aba

Browse files
committed
x509-cert: provide parsing profiles
This allow the user to relax checks when parsing certificate and cover for non rfc5280 compliant x509 certificates.
1 parent 8c29e12 commit 9467aba

File tree

8 files changed

+162
-39
lines changed

8 files changed

+162
-39
lines changed

der/derive/src/attributes.rs

+14
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub(crate) struct FieldAttrs {
7979

8080
/// Is the inner type constructed?
8181
pub constructed: bool,
82+
83+
/// Is the type skipped?
84+
pub skipped: Option<Path>,
8285
}
8386

8487
impl FieldAttrs {
@@ -98,6 +101,7 @@ impl FieldAttrs {
98101
let mut optional = None;
99102
let mut tag_mode = None;
100103
let mut constructed = None;
104+
let mut skipped = None;
101105

102106
let mut parsed_attrs = Vec::new();
103107
AttrNameValue::from_attributes(attrs, &mut parsed_attrs);
@@ -154,6 +158,15 @@ impl FieldAttrs {
154158
}
155159

156160
constructed = Some(ty);
161+
// `skipped` attribute
162+
} else if attr.parse_value::<String>("skipped").is_some() {
163+
if skipped.is_some() {
164+
abort!(attr.name, "duplicate ASN.1 `skipped` attribute");
165+
}
166+
167+
skipped = Some(attr.value.parse().unwrap_or_else(|e| {
168+
abort!(attr.value, "error parsing ASN.1 `skipped` attribute: {}", e)
169+
}));
157170
} else {
158171
abort!(
159172
attr.name,
@@ -171,6 +184,7 @@ impl FieldAttrs {
171184
optional: optional.unwrap_or_default(),
172185
tag_mode: tag_mode.unwrap_or(type_attrs.tag_mode),
173186
constructed: constructed.unwrap_or_default(),
187+
skipped,
174188
}
175189
}
176190

der/derive/src/sequence.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use field::SequenceField;
88
use proc_macro2::TokenStream;
99
use proc_macro_error::abort;
1010
use quote::quote;
11-
use syn::{DeriveInput, Ident, Lifetime};
11+
use syn::{DeriveInput, Ident, Lifetime, TypeParam};
1212

1313
/// Derive the `Sequence` trait for a struct
1414
pub(crate) struct DeriveSequence {
@@ -18,6 +18,9 @@ pub(crate) struct DeriveSequence {
1818
/// Lifetime of the struct.
1919
lifetime: Option<Lifetime>,
2020

21+
/// Type param of the struct.
22+
type_param: Option<TypeParam>,
23+
2124
/// Fields of the struct.
2225
fields: Vec<SequenceField>,
2326
}
@@ -40,6 +43,8 @@ impl DeriveSequence {
4043
.next()
4144
.map(|lt| lt.lifetime.clone());
4245

46+
let type_param = input.generics.type_params().next().cloned();
47+
4348
let type_attrs = TypeAttrs::parse(&input.attrs);
4449

4550
let fields = data
@@ -51,6 +56,7 @@ impl DeriveSequence {
5156
Self {
5257
ident: input.ident,
5358
lifetime,
59+
type_param,
5460
fields,
5561
}
5662
}
@@ -72,6 +78,13 @@ impl DeriveSequence {
7278
.map(|_| lifetime.clone())
7379
.unwrap_or_default();
7480

81+
let type_params = self.type_param.as_ref().map(|t| {
82+
let mut t = t.clone();
83+
t.default = None;
84+
t
85+
});
86+
let type_params_names = self.type_param.as_ref().map(|t| t.ident.clone());
87+
7588
let mut decode_body = Vec::new();
7689
let mut decode_result = Vec::new();
7790
let mut encoded_lengths = Vec::new();
@@ -81,13 +94,14 @@ impl DeriveSequence {
8194
decode_body.push(field.to_decode_tokens());
8295
decode_result.push(&field.ident);
8396

84-
let field = field.to_encode_tokens();
85-
encoded_lengths.push(quote!(#field.encoded_len()?));
86-
encode_fields.push(quote!(#field.encode(writer)?;));
97+
if let Some(field) = field.to_encode_tokens() {
98+
encoded_lengths.push(quote!(#field.encoded_len()?));
99+
encode_fields.push(quote!(#field.encode(writer)?;));
100+
}
87101
}
88102

89103
quote! {
90-
impl<#lifetime> ::der::DecodeValue<#lifetime> for #ident<#lt_params> {
104+
impl<#lifetime, #type_params> ::der::DecodeValue<#lifetime> for #ident<#lt_params #type_params_names> {
91105
fn decode_value<R: ::der::Reader<#lifetime>>(
92106
reader: &mut R,
93107
header: ::der::Header,
@@ -104,7 +118,7 @@ impl DeriveSequence {
104118
}
105119
}
106120

107-
impl<#lifetime> ::der::EncodeValue for #ident<#lt_params> {
121+
impl<#lifetime, #type_params> ::der::EncodeValue for #ident<#lt_params #type_params_names> {
108122
fn value_len(&self) -> ::der::Result<::der::Length> {
109123
use ::der::Encode as _;
110124

@@ -122,7 +136,7 @@ impl DeriveSequence {
122136
}
123137
}
124138

125-
impl<#lifetime> ::der::Sequence<#lifetime> for #ident<#lt_params> {}
139+
impl<#lifetime, #type_params> ::der::Sequence<#lifetime> for #ident<#lt_params #type_params_names> {}
126140
}
127141
}
128142
}

der/derive/src/sequence/field.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,22 @@ impl SequenceField {
7272
}
7373
}
7474

75+
if let Some(default) = &self.attrs.skipped {
76+
let ident = &self.ident;
77+
return quote! {
78+
let #ident = #default();
79+
};
80+
}
81+
7582
lowerer.into_tokens(&self.ident)
7683
}
7784

7885
/// Derive code for encoding a field of a sequence.
79-
pub(super) fn to_encode_tokens(&self) -> TokenStream {
86+
pub(super) fn to_encode_tokens(&self) -> Option<TokenStream> {
87+
if self.attrs.skipped.is_some() {
88+
return None;
89+
}
90+
8091
let mut lowerer = LowerFieldEncoder::new(&self.ident);
8192
let attrs = &self.attrs;
8293

@@ -101,7 +112,7 @@ impl SequenceField {
101112
lowerer.apply_default(&self.ident, default);
102113
}
103114

104-
lowerer.into_tokens()
115+
Some(lowerer.into_tokens())
105116
}
106117
}
107118

der/derive/src/value_ord.rs

+4
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ impl ValueField {
171171

172172
/// Lower to [`TokenStream`].
173173
fn to_tokens(&self) -> TokenStream {
174+
if self.attrs.skipped.is_some() {
175+
return TokenStream::default();
176+
}
177+
174178
let ident = &self.ident;
175179

176180
if self.is_enum {

x509-cert/src/certificate.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,49 @@
33
use crate::{name::Name, serial_number::SerialNumber, time::Validity};
44
use alloc::vec::Vec;
55
use const_oid::AssociatedOid;
6-
use core::cmp::Ordering;
6+
use core::{cmp::Ordering, fmt::Debug, marker::PhantomData};
77
use der::asn1::BitString;
8-
use der::{Decode, Enumerated, Error, ErrorKind, Sequence, ValueOrd};
8+
use der::{Decode, Enumerated, Error, ErrorKind, Sequence, Tag, ValueOrd};
99
use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
1010

1111
#[cfg(feature = "pem")]
1212
use der::pem::PemLabel;
1313

14+
/// [`Profile`] allows the consumer of this crate to customize the behavior when parsing
15+
/// certificates.
16+
/// By default, parsing will be made in a rfc5280-compliant manner.
17+
pub trait Profile: PartialEq + Debug + Eq + Clone {
18+
/// Checks to run when parsing serial numbers
19+
fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
20+
// See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
21+
// since some X.509 implementations interpret the limit of 20 bytes to refer
22+
// to the pre-encoded value.
23+
if serial.inner.len() > SerialNumber::<Self>::MAX_DECODE_LEN {
24+
Err(Tag::Integer.value_error())
25+
} else {
26+
Ok(())
27+
}
28+
}
29+
}
30+
31+
#[derive(Debug, PartialEq, Eq, Clone)]
32+
/// Parse certificates with rfc5280-compliant checks
33+
pub struct Rfc5280;
34+
35+
impl Profile for Rfc5280 {}
36+
37+
#[cfg(feature = "hazmat")]
38+
#[derive(Debug, PartialEq, Eq, Clone)]
39+
/// Parse raw x509 certificate and disable all the checks
40+
pub struct Raw;
41+
42+
#[cfg(feature = "hazmat")]
43+
impl Profile for Raw {
44+
fn check_serial_number(_serial: &SerialNumber<Self>) -> der::Result<()> {
45+
Ok(())
46+
}
47+
}
48+
1449
/// Certificate `Version` as defined in [RFC 5280 Section 4.1].
1550
///
1651
/// ```text
@@ -73,7 +108,7 @@ impl Default for Version {
73108
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
74109
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
75110
#[allow(missing_docs)]
76-
pub struct TbsCertificate {
111+
pub struct TbsCertificate<P: Profile = Rfc5280> {
77112
/// The certificate version
78113
///
79114
/// Note that this value defaults to Version 1 per the RFC. However,
@@ -83,7 +118,7 @@ pub struct TbsCertificate {
83118
#[asn1(context_specific = "0", default = "Default::default")]
84119
pub version: Version,
85120

86-
pub serial_number: SerialNumber,
121+
pub serial_number: SerialNumber<P>,
87122
pub signature: AlgorithmIdentifierOwned,
88123
pub issuer: Name,
89124
pub validity: Validity,
@@ -98,9 +133,12 @@ pub struct TbsCertificate {
98133

99134
#[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
100135
pub extensions: Option<crate::ext::Extensions>,
136+
137+
#[asn1(skipped = "Default::default")]
138+
_profile: PhantomData<P>,
101139
}
102140

103-
impl TbsCertificate {
141+
impl<P: Profile> TbsCertificate<P> {
104142
/// Decodes a single extension
105143
///
106144
/// Returns an error if multiple of these extensions is present. Returns
@@ -146,14 +184,14 @@ impl TbsCertificate {
146184
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
147185
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
148186
#[allow(missing_docs)]
149-
pub struct Certificate {
150-
pub tbs_certificate: TbsCertificate,
187+
pub struct Certificate<P: Profile = Rfc5280> {
188+
pub tbs_certificate: TbsCertificate<P>,
151189
pub signature_algorithm: AlgorithmIdentifierOwned,
152190
pub signature: BitString,
153191
}
154192

155193
#[cfg(feature = "pem")]
156-
impl PemLabel for Certificate {
194+
impl<P: Profile> PemLabel for Certificate<P> {
157195
const PEM_LABEL: &'static str = "CERTIFICATE";
158196
}
159197

x509-cert/src/serial_number.rs

+23-19
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//! X.509 serial number
22
3-
use core::fmt::Display;
3+
use core::{fmt::Display, marker::PhantomData};
44

55
use der::{
66
asn1::{self, Int},
77
DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
88
Writer,
99
};
1010

11+
use crate::certificate::{Profile, Rfc5280};
12+
1113
/// [RFC 5280 Section 4.1.2.2.] Serial Number
1214
///
1315
/// The serial number MUST be a positive integer assigned by the CA to
@@ -25,16 +27,18 @@ use der::{
2527
/// that are negative or zero. Certificate users SHOULD be prepared to
2628
/// gracefully handle such certificates.
2729
#[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
28-
pub struct SerialNumber {
29-
inner: Int,
30+
pub struct SerialNumber<P: Profile = Rfc5280> {
31+
pub(crate) inner: Int,
32+
#[asn1(skipped = "Default::default")]
33+
_profile: PhantomData<P>,
3034
}
3135

32-
impl SerialNumber {
36+
impl<P: Profile> SerialNumber<P> {
3337
/// Maximum length in bytes for a [`SerialNumber`]
3438
pub const MAX_LEN: Length = Length::new(20);
3539

3640
/// See notes in `SerialNumber::new` and `SerialNumber::decode_value`.
37-
const MAX_DECODE_LEN: Length = Length::new(21);
41+
pub(crate) const MAX_DECODE_LEN: Length = Length::new(21);
3842

3943
/// Create a new [`SerialNumber`] from a byte slice.
4044
///
@@ -47,12 +51,13 @@ impl SerialNumber {
4751
// RFC 5280 is ambiguous about whether this is valid, so we limit
4852
// `SerialNumber` *encodings* to 20 bytes or fewer while permitting
4953
// `SerialNumber` *decodings* to have up to 21 bytes below.
50-
if inner.value_len()? > SerialNumber::MAX_LEN {
54+
if inner.value_len()? > Self::MAX_LEN {
5155
return Err(ErrorKind::Overlength.into());
5256
}
5357

5458
Ok(Self {
5559
inner: inner.into(),
60+
_profile: PhantomData,
5661
})
5762
}
5863

@@ -63,7 +68,7 @@ impl SerialNumber {
6368
}
6469
}
6570

66-
impl EncodeValue for SerialNumber {
71+
impl<P: Profile> EncodeValue for SerialNumber<P> {
6772
fn value_len(&self) -> Result<Length> {
6873
self.inner.value_len()
6974
}
@@ -73,22 +78,21 @@ impl EncodeValue for SerialNumber {
7378
}
7479
}
7580

76-
impl<'a> DecodeValue<'a> for SerialNumber {
81+
impl<'a, P: Profile> DecodeValue<'a> for SerialNumber<P> {
7782
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
7883
let inner = Int::decode_value(reader, header)?;
84+
let serial = Self {
85+
inner,
86+
_profile: PhantomData,
87+
};
7988

80-
// See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
81-
// since some X.509 implementations interpret the limit of 20 bytes to refer
82-
// to the pre-encoded value.
83-
if inner.len() > SerialNumber::MAX_DECODE_LEN {
84-
return Err(Tag::Integer.value_error());
85-
}
89+
P::check_serial_number(&serial)?;
8690

87-
Ok(Self { inner })
91+
Ok(serial)
8892
}
8993
}
9094

91-
impl FixedTag for SerialNumber {
95+
impl<P: Profile> FixedTag for SerialNumber<P> {
9296
const TAG: Tag = <Int as FixedTag>::TAG;
9397
}
9498

@@ -131,7 +135,7 @@ impl_from!(usize);
131135
// Implement by hand because the derive would create invalid values.
132136
// Use the constructor to create a valid value.
133137
#[cfg(feature = "arbitrary")]
134-
impl<'a> arbitrary::Arbitrary<'a> for SerialNumber {
138+
impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
135139
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
136140
let len = u.int_in_range(0u32..=Self::MAX_LEN.into())?;
137141

@@ -154,7 +158,7 @@ mod tests {
154158
// Creating a new serial with an oversized encoding (due to high MSB) fails.
155159
{
156160
let too_big = [0x80; 20];
157-
assert!(SerialNumber::new(&too_big).is_err());
161+
assert!(SerialNumber::<Rfc5280>::new(&too_big).is_err());
158162
}
159163

160164
// Creating a new serial with the maximum encoding succeeds.
@@ -163,7 +167,7 @@ mod tests {
163167
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
164168
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
165169
];
166-
assert!(SerialNumber::new(&just_enough).is_ok());
170+
assert!(SerialNumber::<Rfc5280>::new(&just_enough).is_ok());
167171
}
168172
}
169173

0 commit comments

Comments
 (0)