blob: 3d1575454d87db2bf88252cef12492874c3193a3 [file] [log] [blame]
// 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
]
)
}
}