Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.*.swp
target
/.idea
tags
7 changes: 7 additions & 0 deletions assets/csr-custom-extension.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-----BEGIN CERTIFICATE REQUEST-----
MIHJMH0CAQAwLjEbMBkGA1UEAwwSY2xpZW50IGNlcnRpZmljYXRlMQ8wDQYDVQQK
DAZteSBvcmcwKjAFBgMrZXADIQC3Gw0vJiIXML8uYH86NEMtmiMuAdWn2WPEZVKu
mbsOTaAcMBoGCSqGSIb3DQEJDjENMAswCQYCKgMEAwECAzAFBgMrZXADQQDPcUvh
yrGGTBlgv4W3//5/h5AkEG2UVa1Bvfbi/UL4tkFYhkc5sAFZ37Igr2tgDNhofu7r
JVo5qjVH6T9QJ8gD
-----END CERTIFICATE REQUEST-----
49 changes: 28 additions & 21 deletions src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl<'a> DerParser<'a> for X509Extension<'a> {
let (rem, (_, value)) = <&[u8]>::parse_der_as_input(rem)
.map_err(|_| Err::Error(X509Error::InvalidExtensions))?;

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

let (_, parsed_extension) = if self.deep_parse_extensions {
parser::parse_extension(value.clone(), &oid)?
let parsed_extension = if self.deep_parse_extensions {
parser::parse_extension(value.clone(), &oid, critical)
} else {
(rem.take(rem.input_len()), ParsedExtension::Unparsed)
rem.take(rem.input_len());
ParsedExtension::Unparsed
};

let ext = X509Extension {
Expand Down Expand Up @@ -258,12 +259,21 @@ impl<'i> Parser<Input<'i>> for X509ExtensionParser {
}
}

/// A unsupported extension.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UnsupportedExtension<'a> {
/// The Object ID of the extension.
pub oid: Oid<'a>,
/// The unparsed value.
pub value: &'a [u8],
/// Whether the extension is critical.
pub critical: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub enum ParsedExtension<'a> {
/// Crate parser does not support this extension (yet)
UnsupportedExtension {
oid: Oid<'a>,
},
UnsupportedExtension(UnsupportedExtension<'a>),
ParseError {
error: Err<X509Error>,
},
Expand Down Expand Up @@ -422,30 +432,27 @@ pub(crate) mod parser {

// look into the parser map if the extension is known, and parse it
// otherwise, leave it as UnsupportedExtension
fn parse_extension0<'i>(
input: Input<'i>,
oid: &Oid,
) -> IResult<Input<'i>, ParsedExtension<'i>, X509Error> {
fn parse_extension0<'i>(input: Input<'i>, oid: &Oid, critical: bool) -> ParsedExtension<'i> {
if let Some(parser) = EXTENSION_PARSERS.get(oid) {
match parser(input.clone()) {
Ok((rem, ext)) => Ok((rem, ext)),
Err(error) => Ok((input, ParsedExtension::ParseError { error })),
Ok((_, ext)) => ext,
Err(error) => ParsedExtension::ParseError { error },
}
} else {
Ok((
input,
ParsedExtension::UnsupportedExtension {
oid: oid.to_owned(),
},
))
ParsedExtension::UnsupportedExtension(UnsupportedExtension {
oid: oid.to_owned(),
value: input.as_bytes2(),
critical,
})
}
}

pub(crate) fn parse_extension<'i>(
input: Input<'i>,
oid: &Oid,
) -> IResult<Input<'i>, ParsedExtension<'i>, X509Error> {
parse_extension0(input, oid)
critical: bool,
) -> ParsedExtension<'i> {
parse_extension0(input, oid, critical)
}

fn parse_basicconstraints_ext(input: Input) -> IResult<Input, ParsedExtension, X509Error> {
Expand Down
48 changes: 45 additions & 3 deletions tests/readcsr.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use asn1_rs::Set;
use asn1_rs::{oid, Oid, Set};
use oid_registry::{
OID_PKCS1_SHA256WITHRSA, OID_PKCS9_CHALLENGE_PASSWORD, OID_SIG_ECDSA_WITH_SHA256,
OID_X509_COMMON_NAME,
OID_PKCS1_SHA256WITHRSA, OID_PKCS9_CHALLENGE_PASSWORD, OID_PKCS9_EXTENSION_REQUEST,
OID_SIG_ECDSA_WITH_SHA256, OID_X509_COMMON_NAME,
};
use x509_parser::prelude::*;

const CSR_DATA_EMPTY_ATTRIB: &[u8] = include_bytes!("../assets/csr-empty-attributes.csr");
const CSR_DATA: &[u8] = include_bytes!("../assets/test.csr");
const CSR_CHALLENGE_PASSWORD: &[u8] = include_bytes!("../assets/csr-challenge-password.pem");
const CSR_CUSTOM_EXTENSION: &[u8] = include_bytes!("../assets/csr-custom-extension.pem");
const OID_CUSTOM_EXTENSION: Oid<'static> = oid!(1.2.3);
const VALUE_CUSTOM_EXTENSION: &[u8] = &[1, 2, 3];
#[test]
fn read_csr_empty_attrib() {
let (rem, csr) =
Expand Down Expand Up @@ -129,3 +132,42 @@ fn read_csr_verify() {
let (_, csr) = X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR");
csr.verify_signature().unwrap_err();
}

#[test]
fn read_csr_with_custom_extension() {
let der = pem::parse_x509_pem(CSR_CUSTOM_EXTENSION).unwrap().1;
let (rem, csr) = X509CertificationRequest::from_der(&der.contents)
.expect("Could not parse CSR with custom extension");

assert!(rem.is_empty());
dbg!(csr.certification_request_info.attributes());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
assert_eq!(cri.attributes().len(), 1);

let custom_attr = csr
.certification_request_info
.find_attribute(&OID_PKCS9_EXTENSION_REQUEST)
.expect("Custom extension not found in CSR");
for attr in custom_attr.parsed_attributes() {
match attr {
ParsedCriAttribute::ExtensionRequest(req) => {
assert_eq!(req.extensions.len(), 1);
let extension = req.extensions.first().unwrap();
assert_eq!(extension.oid, OID_CUSTOM_EXTENSION);
assert_eq!(extension.critical, false);
assert_eq!(extension.value.as_bytes2(), VALUE_CUSTOM_EXTENSION);
}
_ => unreachable!(),
}
}

let extensions = csr.requested_extensions();
for extension in extensions {
if let ParsedExtension::UnsupportedExtension(ext) = extension {
assert_eq!(ext.oid, OID_CUSTOM_EXTENSION);
assert_eq!(ext.value, VALUE_CUSTOM_EXTENSION);
assert_eq!(ext.critical, false);
}
}
}