diff --git a/src/cert.rs b/src/cert.rs index 75e022a1..c6bfa1ea 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -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)?; @@ -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 @@ -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 { + 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(()) }, ) } diff --git a/tests/cert_v1_unsupported.rs b/tests/cert_v1.rs similarity index 87% rename from tests/cert_v1_unsupported.rs rename to tests/cert_v1.rs index d2c71323..f3a65007 100644 --- a/tests/cert_v1_unsupported.rs +++ b/tests/cert_v1.rs @@ -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 ); }