| // Copyright 2015 Brian Smith. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| use crate::der::Tag; |
| use crate::signed_data::SignedData; |
| use crate::x509::{remember_extension, set_extension_once, Extension}; |
| use crate::{der, Error}; |
| |
| /// An enumeration indicating whether a [`Cert`] is a leaf end-entity cert, or a linked |
| /// list node from the CA `Cert` to a child `Cert` it issued. |
| pub enum EndEntityOrCa<'a> { |
| /// The [`Cert`] is a leaf end-entity certificate. |
| EndEntity, |
| |
| /// The [`Cert`] is an issuer certificate, and issued the referenced child `Cert`. |
| Ca(&'a Cert<'a>), |
| } |
| |
| /// A parsed X509 certificate. |
| pub struct Cert<'a> { |
| pub(crate) ee_or_ca: EndEntityOrCa<'a>, |
| |
| pub(crate) serial: untrusted::Input<'a>, |
| pub(crate) signed_data: SignedData<'a>, |
| pub(crate) issuer: untrusted::Input<'a>, |
| pub(crate) validity: untrusted::Input<'a>, |
| pub(crate) subject: untrusted::Input<'a>, |
| pub(crate) spki: der::Value<'a>, |
| |
| pub(crate) basic_constraints: Option<untrusted::Input<'a>>, |
| // key usage (KU) extension (if any). When validating certificate revocation lists (CRLs) this |
| // field will be consulted to determine if the cert is allowed to sign CRLs. For cert validation |
| // this field is ignored (for more detail see in `verify_cert.rs` and |
| // `check_issuer_independent_properties`). |
| pub(crate) key_usage: Option<untrusted::Input<'a>>, |
| pub(crate) eku: Option<untrusted::Input<'a>>, |
| pub(crate) name_constraints: Option<untrusted::Input<'a>>, |
| pub(crate) subject_alt_name: Option<untrusted::Input<'a>>, |
| } |
| |
| impl<'a> Cert<'a> { |
| pub(crate) fn from_der( |
| cert_der: untrusted::Input<'a>, |
| ee_or_ca: EndEntityOrCa<'a>, |
| ) -> Result<Self, Error> { |
| let (tbs, signed_data) = cert_der.read_all(Error::BadDer, |cert_der| { |
| der::nested(cert_der, der::Tag::Sequence, Error::BadDer, |der| { |
| // limited to SEQUENCEs of size 2^16 or less. |
| SignedData::from_der(der, der::TWO_BYTE_DER_SIZE) |
| }) |
| })?; |
| |
| tbs.read_all(Error::BadDer, |tbs| { |
| version3(tbs)?; |
| |
| let serial = lenient_certificate_serial_number(tbs)?; |
| |
| let signature = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; |
| // TODO: In mozilla::pkix, the comparison is done based on the |
| // normalized value (ignoring whether or not there is an optional NULL |
| // parameter for RSA-based algorithms), so this may be too strict. |
| if signature != signed_data.algorithm { |
| return Err(Error::SignatureAlgorithmMismatch); |
| } |
| |
| let issuer = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; |
| let validity = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; |
| let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; |
| let spki = der::expect_tag(tbs, der::Tag::Sequence)?; |
| |
| // In theory there could be fields [1] issuerUniqueID and [2] |
| // subjectUniqueID, but in practice there never are, and to keep the |
| // code small and simple we don't accept any certificates that do |
| // contain them. |
| |
| let mut cert = Cert { |
| ee_or_ca, |
| |
| signed_data, |
| serial, |
| issuer, |
| validity, |
| subject, |
| spki, |
| |
| basic_constraints: None, |
| key_usage: None, |
| eku: None, |
| name_constraints: None, |
| subject_alt_name: None, |
| }; |
| |
| if !tbs.at_end() { |
| der::nested( |
| tbs, |
| der::Tag::ContextSpecificConstructed3, |
| Error::MalformedExtensions, |
| |tagged| { |
| der::nested_of_mut( |
| tagged, |
| der::Tag::Sequence, |
| der::Tag::Sequence, |
| Error::BadDer, |
| |extension| { |
| remember_cert_extension(&mut cert, &Extension::parse(extension)?) |
| }, |
| ) |
| }, |
| )?; |
| } |
| |
| Ok(cert) |
| }) |
| } |
| |
| /// Raw DER encoded certificate serial number. |
| pub fn serial(&self) -> &[u8] { |
| self.serial.as_slice_less_safe() |
| } |
| |
| /// Raw DER encoded certificate issuer. |
| pub fn issuer(&self) -> &[u8] { |
| self.issuer.as_slice_less_safe() |
| } |
| |
| /// Raw DER encoded certificate subject. |
| pub fn subject(&self) -> &[u8] { |
| self.subject.as_slice_less_safe() |
| } |
| |
| /// Returns an indication of whether the certificate is an end-entity (leaf) certificate, |
| /// or a certificate authority. |
| pub fn end_entity_or_ca(&self) -> &EndEntityOrCa { |
| &self.ee_or_ca |
| } |
| } |
| |
| // 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> { |
| der::nested( |
| input, |
| der::Tag::ContextSpecificConstructed0, |
| Error::UnsupportedCertVersion, |
| |input| { |
| let version = der::small_nonnegative_integer(input)?; |
| if version != 2 { |
| // v3 |
| return Err(Error::UnsupportedCertVersion); |
| } |
| Ok(()) |
| }, |
| ) |
| } |
| |
| pub(crate) fn lenient_certificate_serial_number<'a>( |
| input: &mut untrusted::Reader<'a>, |
| ) -> Result<untrusted::Input<'a>, Error> { |
| // https://tools.ietf.org/html/rfc5280#section-4.1.2.2: |
| // * Conforming CAs MUST NOT use serialNumber values longer than 20 octets." |
| // * "The serial number MUST be a positive integer [...]" |
| // |
| // However, we don't enforce these constraints, as there are widely-deployed trust anchors |
| // and many X.509 implementations in common use that violate these constraints. This is called |
| // out by the same section of RFC 5280 as cited above: |
| // Note: Non-conforming CAs may issue certificates with serial numbers |
| // that are negative or zero. Certificate users SHOULD be prepared to |
| // gracefully handle such certificates. |
| der::expect_tag_and_get_value(input, Tag::Integer) |
| } |
| |
| fn remember_cert_extension<'a>( |
| cert: &mut Cert<'a>, |
| extension: &Extension<'a>, |
| ) -> Result<(), Error> { |
| // We don't do anything with certificate policies so we can safely ignore |
| // all policy-related stuff. We assume that the policy-related extensions |
| // are not marked critical. |
| |
| remember_extension(extension, |id| { |
| let out = match id { |
| // id-ce-keyUsage 2.5.29.15. |
| 15 => &mut cert.key_usage, |
| |
| // id-ce-subjectAltName 2.5.29.17 |
| 17 => &mut cert.subject_alt_name, |
| |
| // id-ce-basicConstraints 2.5.29.19 |
| 19 => &mut cert.basic_constraints, |
| |
| // id-ce-nameConstraints 2.5.29.30 |
| 30 => &mut cert.name_constraints, |
| |
| // id-ce-extKeyUsage 2.5.29.37 |
| 37 => &mut cert.eku, |
| |
| // Unsupported extension |
| _ => return extension.unsupported(), |
| }; |
| |
| set_extension_once(out, || { |
| extension.value.read_all(Error::BadDer, |value| match id { |
| // Unlike the other extensions we remember KU is a BitString and not a Sequence. We |
| // read the raw bytes here and parse at the time of use. |
| 15 => Ok(value.read_bytes_to_end()), |
| // All other remembered certificate extensions are wrapped in a Sequence. |
| _ => der::expect_tag_and_get_value(value, Tag::Sequence), |
| }) |
| }) |
| }) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::cert::{Cert, EndEntityOrCa}; |
| |
| #[test] |
| // Note: cert::parse_cert is crate-local visibility, and EndEntityCert doesn't expose the |
| // inner Cert, or the serial number. As a result we test that the raw serial value |
| // is read correctly here instead of in tests/integration.rs. |
| fn test_serial_read() { |
| let ee = include_bytes!("../tests/misc/serial_neg_ee.der"); |
| let cert = Cert::from_der(untrusted::Input::from(ee), EndEntityOrCa::EndEntity) |
| .expect("failed to parse certificate"); |
| assert_eq!(cert.serial.as_slice_less_safe(), &[255, 33, 82, 65, 17]); |
| |
| let ee = include_bytes!("../tests/misc/serial_large_positive.der"); |
| let cert = Cert::from_der(untrusted::Input::from(ee), EndEntityOrCa::EndEntity) |
| .expect("failed to parse certificate"); |
| assert_eq!( |
| cert.serial.as_slice_less_safe(), |
| &[ |
| 0, 230, 9, 254, 122, 234, 0, 104, 140, 224, 36, 180, 237, 32, 27, 31, 239, 82, 180, |
| 68, 209 |
| ] |
| ) |
| } |
| } |