Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accept x.509 v1 certificates #234

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
33 changes: 25 additions & 8 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ pub(crate) fn parse_cert_internal<'a>(
})?;

tbs.read_all(Error::BadDER, |tbs| {
version3(tbs)?;
let cert_version = version(tbs)?;

serial_number(tbs)?;

let signature = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
Expand Down Expand Up @@ -95,6 +96,11 @@ pub(crate) fn parse_cert_internal<'a>(
subject_alt_name: None,
};

// V1 certificates are expected to have no extensions
if cert_version == CertVersion::V1 {
return Ok(cert);
}

// mozilla::pkix allows the extensions to be omitted. However, since
// the subjectAltName extension is mandatory, the extensions are
// mandatory too, and we enforce that. Also, mozilla::pkix includes
Expand Down Expand Up @@ -129,20 +135,31 @@ pub(crate) fn parse_cert_internal<'a>(
})
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum CertVersion {
V1,
V3,
}

// mozilla::pkix supports v1, v2, v3, and v4, including both the implicit
// (correct) and explicit (incorrect) encoding of v1. We allow only v3.
fn version3(input: &mut untrusted::Reader) -> Result<(), Error> {
// (correct) and explicit (incorrect) encoding of v1. We allow v3, explicit v1
// and implicit v1.
fn version(input: &mut untrusted::Reader) -> Result<CertVersion, Error> {
if !input.peek(u8::from(der::Tag::ContextSpecificConstructed0)) {
return Ok(CertVersion::V1);
}

der::nested(
input,
der::Tag::ContextSpecificConstructed0,
Error::UnsupportedCertVersion,
Error::BadDER,
|input| {
let version = der::small_nonnegative_integer(input)?;
if version != 2 {
// v3
return Err(Error::UnsupportedCertVersion);
match version {
0 => Ok(CertVersion::V1),
2 => Ok(CertVersion::V3),
_ => Err(Error::UnsupportedCertVersion),
}
Ok(())
},
)
}
Expand Down
6 changes: 3 additions & 3 deletions tests/cert_v1_unsupported.rs → tests/cert_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
use core::convert::TryFrom;

#[test]
fn test_cert_v1_unsupported() {
fn test_cert_v1() {
// Check with `openssl x509 -text -noout -in cert_v1.der -inform DER`
// to verify this is a correct version 1 certificate.
const CERT_V1_DER: &[u8] = include_bytes!("cert_v1.der");

assert_eq!(
Some(webpki::Error::UnsupportedCertVersion),
webpki::EndEntityCert::try_from(CERT_V1_DER).err()
webpki::EndEntityCert::try_from(CERT_V1_DER).is_ok(),
true
);
}