Skip to content

Commit 93b81ca

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. Fixes #978 Fixes #984
1 parent 91e9607 commit 93b81ca

File tree

9 files changed

+159
-40
lines changed

9 files changed

+159
-40
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().map(|lt| lt.clone());
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/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ default = ["pem", "std"]
3131
std = ["const-oid/std", "der/std", "spki/std"]
3232

3333
arbitrary = ["dep:arbitrary", "std", "der/arbitrary", "spki/arbitrary"]
34+
hazmat = []
3435
pem = ["der/pem", "spki/pem"]
3536

3637
[package.metadata.docs.rs]

x509-cert/src/certificate.rs

+38-6
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,43 @@
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;
88
use der::{Decode, Enumerated, Error, ErrorKind, Sequence, ValueOrd};
99
use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
1010

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

14+
pub trait ParsingProfile: PartialEq + Debug + Eq + Clone {
15+
fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
16+
// See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
17+
// since some X.509 implementations interpret the limit of 20 bytes to refer
18+
// to the pre-encoded value.
19+
if serial.inner.len() > SerialNumber::<Self>::MAX_DECODE_LEN {
20+
Err(ErrorKind::Overlength.into())
21+
} else {
22+
Ok(())
23+
}
24+
}
25+
}
26+
27+
#[derive(Debug, PartialEq, Eq, Clone)]
28+
pub struct Rfc5280;
29+
30+
impl ParsingProfile for Rfc5280 {}
31+
32+
#[cfg(feature = "hazmat")]
33+
#[derive(Debug, PartialEq, Eq, Clone)]
34+
pub struct Raw;
35+
36+
#[cfg(feature = "hazmat")]
37+
impl ParsingProfile for Raw {
38+
fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
39+
Ok(())
40+
}
41+
}
42+
1443
/// Certificate `Version` as defined in [RFC 5280 Section 4.1].
1544
///
1645
/// ```text
@@ -73,7 +102,7 @@ impl Default for Version {
73102
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
74103
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
75104
#[allow(missing_docs)]
76-
pub struct TbsCertificate {
105+
pub struct TbsCertificate<PP: ParsingProfile = Rfc5280> {
77106
/// The certificate version
78107
///
79108
/// Note that this value defaults to Version 1 per the RFC. However,
@@ -83,7 +112,7 @@ pub struct TbsCertificate {
83112
#[asn1(context_specific = "0", default = "Default::default")]
84113
pub version: Version,
85114

86-
pub serial_number: SerialNumber,
115+
pub serial_number: SerialNumber<PP>,
87116
pub signature: AlgorithmIdentifierOwned,
88117
pub issuer: Name,
89118
pub validity: Validity,
@@ -98,6 +127,9 @@ pub struct TbsCertificate {
98127

99128
#[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
100129
pub extensions: Option<crate::ext::Extensions>,
130+
131+
#[asn1(skipped = "Default::default")]
132+
_profile: PhantomData<PP>,
101133
}
102134

103135
impl TbsCertificate {
@@ -146,14 +178,14 @@ impl TbsCertificate {
146178
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
147179
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
148180
#[allow(missing_docs)]
149-
pub struct Certificate {
150-
pub tbs_certificate: TbsCertificate,
181+
pub struct Certificate<PP: ParsingProfile = Rfc5280> {
182+
pub tbs_certificate: TbsCertificate<PP>,
151183
pub signature_algorithm: AlgorithmIdentifierOwned,
152184
pub signature: BitString,
153185
}
154186

155187
#[cfg(feature = "pem")]
156-
impl PemLabel for Certificate {
188+
impl<PP: ParsingProfile> PemLabel for Certificate<PP> {
157189
const PEM_LABEL: &'static str = "CERTIFICATE";
158190
}
159191

0 commit comments

Comments
 (0)