Skip to content

Commit 6f214d3

Browse files
jean-airoldiechifflier
authored andcommitted
Add value field to ParsedExtension::UnsupportedExtension
* This is a breaking change. * Allows a user to manually parse the value of a custom extension. * Add tests. * Fix clippy & run rustfmt
1 parent 4c4b831 commit 6f214d3

File tree

4 files changed

+81
-24
lines changed

4 files changed

+81
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.*.swp
22
target
33
/.idea
4+
tags

assets/csr-custom-extension.pem

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-----BEGIN CERTIFICATE REQUEST-----
2+
MIHJMH0CAQAwLjEbMBkGA1UEAwwSY2xpZW50IGNlcnRpZmljYXRlMQ8wDQYDVQQK
3+
DAZteSBvcmcwKjAFBgMrZXADIQC3Gw0vJiIXML8uYH86NEMtmiMuAdWn2WPEZVKu
4+
mbsOTaAcMBoGCSqGSIb3DQEJDjENMAswCQYCKgMEAwECAzAFBgMrZXADQQDPcUvh
5+
yrGGTBlgv4W3//5/h5AkEG2UVa1Bvfbi/UL4tkFYhkc5sAFZ37Igr2tgDNhofu7r
6+
JVo5qjVH6T9QJ8gD
7+
-----END CERTIFICATE REQUEST-----

src/extensions/mod.rs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'a> DerParser<'a> for X509Extension<'a> {
175175
let (rem, (_, value)) = <&[u8]>::parse_der_as_input(rem)
176176
.map_err(|_| Err::Error(X509Error::InvalidExtensions))?;
177177

178-
let (_, parsed_extension) = parser::parse_extension(value.clone(), &oid)?;
178+
let parsed_extension = parser::parse_extension(value.clone(), &oid, critical);
179179
let ext = X509Extension {
180180
oid,
181181
critical,
@@ -226,10 +226,11 @@ impl<'i> Parser<Input<'i>> for X509ExtensionParser {
226226
let (rem, (_, value)) = <&[u8]>::parse_der_as_input(rem)
227227
.map_err(|_| Err::Error(X509Error::InvalidExtensions))?;
228228

229-
let (_, parsed_extension) = if self.deep_parse_extensions {
230-
parser::parse_extension(value.clone(), &oid)?
229+
let parsed_extension = if self.deep_parse_extensions {
230+
parser::parse_extension(value.clone(), &oid, critical)
231231
} else {
232-
(rem.take(rem.input_len()), ParsedExtension::Unparsed)
232+
rem.take(rem.input_len());
233+
ParsedExtension::Unparsed
233234
};
234235

235236
let ext = X509Extension {
@@ -258,12 +259,21 @@ impl<'i> Parser<Input<'i>> for X509ExtensionParser {
258259
}
259260
}
260261

262+
/// A unsupported extension.
263+
#[derive(Debug, Clone, Eq, PartialEq)]
264+
pub struct UnsupportedExtension<'a> {
265+
/// The Object ID of the extension.
266+
pub oid: Oid<'a>,
267+
/// The unparsed value.
268+
pub value: &'a [u8],
269+
/// Whether the extension is critical.
270+
pub critical: bool,
271+
}
272+
261273
#[derive(Clone, Debug, PartialEq)]
262274
pub enum ParsedExtension<'a> {
263275
/// Crate parser does not support this extension (yet)
264-
UnsupportedExtension {
265-
oid: Oid<'a>,
266-
},
276+
UnsupportedExtension(UnsupportedExtension<'a>),
267277
ParseError {
268278
error: Err<X509Error>,
269279
},
@@ -422,30 +432,27 @@ pub(crate) mod parser {
422432

423433
// look into the parser map if the extension is known, and parse it
424434
// otherwise, leave it as UnsupportedExtension
425-
fn parse_extension0<'i>(
426-
input: Input<'i>,
427-
oid: &Oid,
428-
) -> IResult<Input<'i>, ParsedExtension<'i>, X509Error> {
435+
fn parse_extension0<'i>(input: Input<'i>, oid: &Oid, critical: bool) -> ParsedExtension<'i> {
429436
if let Some(parser) = EXTENSION_PARSERS.get(oid) {
430437
match parser(input.clone()) {
431-
Ok((rem, ext)) => Ok((rem, ext)),
432-
Err(error) => Ok((input, ParsedExtension::ParseError { error })),
438+
Ok((_, ext)) => ext,
439+
Err(error) => ParsedExtension::ParseError { error },
433440
}
434441
} else {
435-
Ok((
436-
input,
437-
ParsedExtension::UnsupportedExtension {
438-
oid: oid.to_owned(),
439-
},
440-
))
442+
ParsedExtension::UnsupportedExtension(UnsupportedExtension {
443+
oid: oid.to_owned(),
444+
value: input.as_bytes2(),
445+
critical,
446+
})
441447
}
442448
}
443449

444450
pub(crate) fn parse_extension<'i>(
445451
input: Input<'i>,
446452
oid: &Oid,
447-
) -> IResult<Input<'i>, ParsedExtension<'i>, X509Error> {
448-
parse_extension0(input, oid)
453+
critical: bool,
454+
) -> ParsedExtension<'i> {
455+
parse_extension0(input, oid, critical)
449456
}
450457

451458
fn parse_basicconstraints_ext(input: Input) -> IResult<Input, ParsedExtension, X509Error> {

tests/readcsr.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
use asn1_rs::Set;
1+
use asn1_rs::{oid, Oid, Set};
22
use oid_registry::{
3-
OID_PKCS1_SHA256WITHRSA, OID_PKCS9_CHALLENGE_PASSWORD, OID_SIG_ECDSA_WITH_SHA256,
4-
OID_X509_COMMON_NAME,
3+
OID_PKCS1_SHA256WITHRSA, OID_PKCS9_CHALLENGE_PASSWORD, OID_PKCS9_EXTENSION_REQUEST,
4+
OID_SIG_ECDSA_WITH_SHA256, OID_X509_COMMON_NAME,
55
};
66
use x509_parser::prelude::*;
77

88
const CSR_DATA_EMPTY_ATTRIB: &[u8] = include_bytes!("../assets/csr-empty-attributes.csr");
99
const CSR_DATA: &[u8] = include_bytes!("../assets/test.csr");
1010
const CSR_CHALLENGE_PASSWORD: &[u8] = include_bytes!("../assets/csr-challenge-password.pem");
11+
const CSR_CUSTOM_EXTENSION: &[u8] = include_bytes!("../assets/csr-custom-extension.pem");
12+
const OID_CUSTOM_EXTENSION: Oid<'static> = oid!(1.2.3);
13+
const VALUE_CUSTOM_EXTENSION: &[u8] = &[1, 2, 3];
1114
#[test]
1215
fn read_csr_empty_attrib() {
1316
let (rem, csr) =
@@ -129,3 +132,42 @@ fn read_csr_verify() {
129132
let (_, csr) = X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR");
130133
csr.verify_signature().unwrap_err();
131134
}
135+
136+
#[test]
137+
fn read_csr_with_custom_extension() {
138+
let der = pem::parse_x509_pem(CSR_CUSTOM_EXTENSION).unwrap().1;
139+
let (rem, csr) = X509CertificationRequest::from_der(&der.contents)
140+
.expect("Could not parse CSR with custom extension");
141+
142+
assert!(rem.is_empty());
143+
dbg!(csr.certification_request_info.attributes());
144+
let cri = &csr.certification_request_info;
145+
assert_eq!(cri.version, X509Version(0));
146+
assert_eq!(cri.attributes().len(), 1);
147+
148+
let custom_attr = csr
149+
.certification_request_info
150+
.find_attribute(&OID_PKCS9_EXTENSION_REQUEST)
151+
.expect("Custom extension not found in CSR");
152+
for attr in custom_attr.parsed_attributes() {
153+
match attr {
154+
ParsedCriAttribute::ExtensionRequest(req) => {
155+
assert_eq!(req.extensions.len(), 1);
156+
let extension = req.extensions.first().unwrap();
157+
assert_eq!(extension.oid, OID_CUSTOM_EXTENSION);
158+
assert_eq!(extension.critical, false);
159+
assert_eq!(extension.value.as_bytes2(), VALUE_CUSTOM_EXTENSION);
160+
}
161+
_ => unreachable!(),
162+
}
163+
}
164+
165+
let extensions = csr.requested_extensions();
166+
for extension in extensions {
167+
if let ParsedExtension::UnsupportedExtension(ext) = extension {
168+
assert_eq!(ext.oid, OID_CUSTOM_EXTENSION);
169+
assert_eq!(ext.value, VALUE_CUSTOM_EXTENSION);
170+
assert_eq!(ext.critical, false);
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)