Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Check expiry of TrustAnchors
Browse files Browse the repository at this point in the history
ctz committed Oct 29, 2016
1 parent 7b99d68 commit 2704eb9
Showing 9 changed files with 140 additions and 29 deletions.
23 changes: 16 additions & 7 deletions src/trust_anchor_util.rs
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
use {Error, TrustAnchor};
use cert::{certificate_serial_number, Cert, EndEntityOrCA, parse_cert_internal};
use verify_cert::parse_validity;
use std;
use super::der;
use untrusted;
@@ -37,7 +38,7 @@ pub fn cert_der_as_trust_anchor<'a>(cert_der: untrusted::Input<'a>)
// embedded name constraints in a v1 certificate.
match parse_cert_internal(cert_der, EndEntityOrCA::EndEntity,
possibly_invalid_certificate_serial_number) {
Ok(cert) => Ok(trust_anchor_from_cert(cert)),
Ok(cert) => trust_anchor_from_cert(cert),
Err(Error::BadDER) => parse_cert_v1(cert_der).or(Err(Error::BadDER)),
Err(err) => Err(err),
}
@@ -71,13 +72,16 @@ pub fn generate_code_for_trust_anchors(name: &str,
decl + &value
}

fn trust_anchor_from_cert<'a>(cert: Cert<'a>) -> TrustAnchor<'a> {
TrustAnchor {
fn trust_anchor_from_cert<'a>(cert: Cert<'a>) -> Result<TrustAnchor<'a>, Error> {
let (_, not_after) = try!(parse_validity(&cert.validity));

Ok(TrustAnchor {
subject: cert.subject.as_slice_less_safe(),
spki: cert.spki.as_slice_less_safe(),
name_constraints: cert.name_constraints
.map(|nc| nc.as_slice_less_safe())
}
.map(|nc| nc.as_slice_less_safe()),
not_after: not_after.sec,
})
}

/// Parses a v1 certificate directly into a TrustAnchor.
@@ -93,18 +97,23 @@ fn parse_cert_v1<'a>(cert_der: untrusted::Input<'a>)

try!(skip(tbs, der::Tag::Sequence)); // signature.
try!(skip(tbs, der::Tag::Sequence)); // issuer.
try!(skip(tbs, der::Tag::Sequence)); // validity.

let validity =
try!(der::expect_tag_and_get_value(tbs,
der::Tag::Sequence));
let subject =
try!(der::expect_tag_and_get_value(tbs,
der::Tag::Sequence));
let spki =
try!(der::expect_tag_and_get_value(tbs,
der::Tag::Sequence));
let (_, not_after) = try!(parse_validity(&validity));

Ok(TrustAnchor {
subject: subject.as_slice_less_safe(),
spki: spki.as_slice_less_safe(),
name_constraints: None
name_constraints: None,
not_after: not_after.sec,
})
});

22 changes: 17 additions & 5 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
@@ -54,6 +54,10 @@ pub fn build_chain<'a>(required_eku_if_present: KeyPurposeId,
return Err(Error::UnknownIssuer);
}

if time.sec >= trust_anchor.not_after {
return Err(Error::CertExpired);
}

let name_constraints =
trust_anchor.name_constraints.map(untrusted::Input::from);

@@ -150,8 +154,8 @@ fn check_issuer_independent_properties<'a>(
// See the comment in `remember_extensions` for why we don't check the
// KeyUsage extension.

try!(cert.validity.read_all(Error::BadDER,
|value| check_validity(value, time)));
try!(parse_validity(&cert.validity)
.and_then(|bounds| check_validity(bounds, time)));
try!(untrusted::read_all_optional(
cert.basic_constraints, Error::BadDER,
|value| check_basic_constraints(value, used_as_ca, sub_ca_count)));
@@ -163,10 +167,9 @@ fn check_issuer_independent_properties<'a>(
}

// https://tools.ietf.org/html/rfc5280#section-4.1.2.5
fn check_validity(input: &mut untrusted::Reader, time: time::Timespec)
fn check_validity(bounds: (time::Timespec, time::Timespec), time: time::Timespec)
-> Result<(), Error> {
let not_before = try!(der::time_choice(input));
let not_after = try!(der::time_choice(input));
let (not_before, not_after) = bounds;

if not_before > not_after {
return Err(Error::InvalidCertValidity);
@@ -185,6 +188,15 @@ fn check_validity(input: &mut untrusted::Reader, time: time::Timespec)
Ok(())
}

pub fn parse_validity(validity: &untrusted::Input)
-> Result<(time::Timespec, time::Timespec), Error> {
validity.read_all(Error::BadDER, |input| {
let not_before = try!(der::time_choice(input));
let not_after = try!(der::time_choice(input));
Ok((not_before, not_after))
})
}

#[derive(Clone, Copy)]
enum UsedAsCA { Yes, No }

6 changes: 5 additions & 1 deletion src/webpki.rs
Original file line number Diff line number Diff line change
@@ -305,5 +305,9 @@ pub struct TrustAnchor<'a> {

/// The value of a DER-encoded NameConstraints, containing name
/// constraints to apply to the trust anchor, if any.
pub name_constraints: Option<&'a [u8]>
pub name_constraints: Option<&'a [u8]>,

/// The root's expiry in seconds since the UNIX epoch. The anchor
/// is only considered when the validation time is before this value.
pub not_after: i64
}
32 changes: 32 additions & 0 deletions tests/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2016 Joseph Birr-Pixton.
//
// 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.

extern crate webpki;
use webpki::*;

pub static ALL_SIGALGS: &'static [&'static SignatureAlgorithm] = &[
&ECDSA_P256_SHA1,
&ECDSA_P256_SHA256,
&ECDSA_P256_SHA384,
&ECDSA_P256_SHA512,
&ECDSA_P384_SHA1,
&ECDSA_P384_SHA256,
&ECDSA_P384_SHA384,
&ECDSA_P384_SHA512,
&RSA_PKCS1_2048_8192_SHA1,
&RSA_PKCS1_2048_8192_SHA256,
&RSA_PKCS1_2048_8192_SHA384,
&RSA_PKCS1_2048_8192_SHA512,
&RSA_PKCS1_3072_8192_SHA384
];
68 changes: 68 additions & 0 deletions tests/expiry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2016 Joseph Birr-Pixton.
//
// 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.

extern crate untrusted;
extern crate webpki;
extern crate ring;
extern crate time;
mod common;

use untrusted::Input;
use webpki::*;
use common::ALL_SIGALGS;

static EE_CERT: &'static [u8] = include_bytes!("expiry/ee.der");
static EE_LONG_CERT: &'static [u8] = include_bytes!("expiry/eelong.der");

pub fn expect_expiry(when: i64, ee: &'static [u8], expect_err: Option<Error>) {
let ca = include_bytes!("expiry/ca.der");

let ee_input = Input::from(ee);
let inter_vec = vec![];
let anchors = vec![
trust_anchor_util::cert_der_as_trust_anchor(
Input::from(ca)
).unwrap()
];

let rough_time = time::Timespec::new(when, 0);

let cert = EndEntityCert::from(ee_input).unwrap();
let rc = cert.verify_is_valid_tls_server_cert(ALL_SIGALGS,
&anchors,
&inter_vec,
rough_time);

assert_eq!(expect_err, rc.err());
}

#[test]
pub fn valid() {
expect_expiry(1479496432, EE_CERT, None);
}

#[test]
pub fn expired_ee_before() {
expect_expiry(1476644886, EE_CERT, Some(Error::CertNotValidYet));
}

#[test]
pub fn expired_ee_after() {
expect_expiry(1479496434, EE_CERT, Some(Error::CertExpired));
}

#[test]
pub fn expired_ca_after() {
expect_expiry(1528571613, EE_LONG_CERT, Some(Error::UnknownIssuer));
}
Binary file added tests/expiry/ca.der
Binary file not shown.
Binary file added tests/expiry/ee.der
Binary file not shown.
Binary file added tests/expiry/eelong.der
Binary file not shown.
18 changes: 2 additions & 16 deletions tests/integration.rs
Original file line number Diff line number Diff line change
@@ -16,25 +16,11 @@ extern crate untrusted;
extern crate webpki;
extern crate ring;
extern crate time;
mod common;

use untrusted::Input;
use webpki::*;

static ALL_SIGALGS: &'static [&'static SignatureAlgorithm] = &[
&ECDSA_P256_SHA1,
&ECDSA_P256_SHA256,
&ECDSA_P256_SHA384,
&ECDSA_P256_SHA512,
&ECDSA_P384_SHA1,
&ECDSA_P384_SHA256,
&ECDSA_P384_SHA384,
&ECDSA_P384_SHA512,
&RSA_PKCS1_2048_8192_SHA1,
&RSA_PKCS1_2048_8192_SHA256,
&RSA_PKCS1_2048_8192_SHA384,
&RSA_PKCS1_2048_8192_SHA512,
&RSA_PKCS1_3072_8192_SHA384
];
use common::ALL_SIGALGS;

/* Checks we can verify netflix's cert chain. This is notable
* because they're rooted at a Verisign v1 root. */

0 comments on commit 2704eb9

Please sign in to comment.