Skip to content

Commit

Permalink
Unify high-level API to make key usage explicit
Browse files Browse the repository at this point in the history
  • Loading branch information
djc authored and ctz committed Jul 26, 2023
1 parent ee95916 commit 875fcef
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 58 deletions.
36 changes: 23 additions & 13 deletions src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
use crate::subject_name::GeneralDnsNameRef;
use crate::{
cert, signed_data, subject_name, verify_cert, CertRevocationList, Error, KeyUsage,
NonTlsTrustAnchors, SignatureAlgorithm, SubjectNameRef, Time, TlsClientTrustAnchors,
TlsServerTrustAnchors, TrustAnchor,
SignatureAlgorithm, SubjectNameRef, Time, TrustAnchor,
};
#[allow(deprecated)]
use crate::{TlsClientTrustAnchors, TlsServerTrustAnchors};

/// An end-entity certificate.
///
Expand Down Expand Up @@ -100,28 +101,33 @@ impl<'a> EndEntityCert<'a> {
/// Verifies that the end-entity certificate is valid for use against the
/// specified Extended Key Usage (EKU).
///
/// `supported_sig_algs` is the list of signature algorithms that are
/// trusted for use in certificate signatures; the end-entity certificate's
/// public key is not validated against this list. `trust_anchors` is the
/// list of root CAs to trust. `intermediate_certs` is the sequence of
/// intermediate certificates that the server sent in the TLS handshake.
/// `time` is the time for which the validation is effective (usually the
/// current time).
pub fn verify_is_valid_cert_with_eku(
/// * `supported_sig_algs` is the list of signature algorithms that are
/// trusted for use in certificate signatures; the end-entity certificate's
/// public key is not validated against this list.
/// * `trust_anchors` is the list of root CAs to trust
/// * `intermediate_certs` is the sequence of intermediate certificates that
/// the server sent in the TLS handshake.
/// * `time` is the time for which the validation is effective (usually the
/// current time).
/// * `usage` is the intended usage of the certificate, indicating what kind
/// of usage we're verifying the certificate for.
/// * `crls` is the list of certificate revocation lists to check
/// the certificate against.
pub fn verify_for_usage(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
&NonTlsTrustAnchors(trust_anchors): &NonTlsTrustAnchors,
trust_anchors: &[TrustAnchor],
intermediate_certs: &[&[u8]],
time: Time,
eku: KeyUsage,
usage: KeyUsage,
crls: &[&dyn CertRevocationList],
) -> Result<(), Error> {
self.verify_is_valid_cert(
supported_sig_algs,
trust_anchors,
intermediate_certs,
time,
eku,
usage,
crls,
)
}
Expand All @@ -136,6 +142,8 @@ impl<'a> EndEntityCert<'a> {
/// intermediate certificates that the server sent in the TLS handshake.
/// `time` is the time for which the validation is effective (usually the
/// current time).
#[allow(deprecated)]
#[deprecated(since = "0.101.2", note = "Use `verify_for_usage` instead")]
pub fn verify_is_valid_tls_server_cert(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
Expand Down Expand Up @@ -164,6 +172,8 @@ impl<'a> EndEntityCert<'a> {
/// `cert` is the purported end-entity certificate of the client. `time` is
/// the time for which the validation is effective (usually the current
/// time).
#[allow(deprecated)]
#[deprecated(since = "0.101.2", note = "Use `verify_for_usage` instead")]
pub fn verify_is_valid_tls_client_cert(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ mod crl;
mod verify_cert;
mod x509;

#[allow(deprecated)]
pub use trust_anchor::{TlsClientTrustAnchors, TlsServerTrustAnchors};
pub use {
cert::{Cert, EndEntityOrCa},
crl::{BorrowedCertRevocationList, BorrowedRevokedCert, CertRevocationList, RevocationReason},
Expand All @@ -72,7 +74,7 @@ pub use {
SubjectNameRef,
},
time::Time,
trust_anchor::{NonTlsTrustAnchors, TlsClientTrustAnchors, TlsServerTrustAnchors, TrustAnchor},
trust_anchor::TrustAnchor,
verify_cert::KeyUsage,
};

Expand Down
6 changes: 2 additions & 4 deletions src/trust_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ pub struct TrustAnchor<'a> {
pub name_constraints: Option<&'a [u8]>,
}

/// Trust anchors which may be used for authenticating certificates of any kind.
#[derive(Debug)]
pub struct NonTlsTrustAnchors<'a>(pub &'a [TrustAnchor<'a>]);

/// Trust anchors which may be used for authenticating servers.
#[deprecated(since = "0.101.2")]
#[derive(Debug)]
pub struct TlsServerTrustAnchors<'a>(pub &'a [TrustAnchor<'a>]);

/// Trust anchors which may be used for authenticating clients.
#[deprecated(since = "0.101.2")]
#[derive(Debug)]
pub struct TlsClientTrustAnchors<'a>(pub &'a [TrustAnchor<'a>]);

Expand Down
9 changes: 5 additions & 4 deletions tests/better_tls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use base64::{engine::general_purpose, Engine as _};
use serde::Deserialize;
use std::collections::HashMap;
use webpki::TrustAnchor;
use webpki::{KeyUsage, TrustAnchor};

#[test]
pub fn path_building() {
Expand All @@ -11,7 +11,6 @@ pub fn path_building() {

let root_der = &better_tls.root_der();
let roots = &[TrustAnchor::try_from_cert_der(root_der).expect("invalid trust anchor")];
let trust_anchors = &webpki::TlsServerTrustAnchors(roots);

let path_building_suite = better_tls
.suites
Expand All @@ -35,11 +34,13 @@ pub fn path_building() {
// certificates won't expire.
let now = webpki::Time::from_seconds_since_unix_epoch(1_688_651_734);

let result = ee_cert.verify_is_valid_tls_server_cert(
let result = ee_cert.verify_for_usage(
&[&webpki::ECDSA_P256_SHA256], // All of the BetterTLS testcases use P256 keys.
trust_anchors,
roots,
intermediates,
now,
KeyUsage::server_auth(),
&[],
);

match testcase.expected {
Expand Down
15 changes: 11 additions & 4 deletions tests/client_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// 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;
#[cfg(feature = "alloc")]
use webpki::KeyUsage;

#[cfg(feature = "alloc")]
static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[
Expand All @@ -29,12 +30,18 @@ static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[

#[cfg(feature = "alloc")]
fn check_cert(ee: &[u8], ca: &[u8]) -> Result<(), webpki::Error> {
let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsClientTrustAnchors(&anchors);
let anchors = &[webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(0x1fed_f00d);
let cert = webpki::EndEntityCert::try_from(ee).unwrap();
cert.verify_is_valid_tls_client_cert(ALL_SIGALGS, &anchors, &[], time, &[])
cert.verify_for_usage(
ALL_SIGALGS,
anchors,
&[],
time,
KeyUsage::client_auth(),
&[],
)
}

// DO NOT EDIT BELOW: generated by tests/generate.py
Expand Down
8 changes: 4 additions & 4 deletions tests/client_auth_revocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// 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::KeyUsage;

fn check_cert(
ee: &[u8],
Expand All @@ -21,15 +21,15 @@ fn check_cert(
crls: &[&dyn webpki::CertRevocationList],
) -> Result<(), webpki::Error> {
let anchors = &[webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsClientTrustAnchors(anchors.as_slice());
let cert = webpki::EndEntityCert::try_from(ee).unwrap();
let time = webpki::Time::from_seconds_since_unix_epoch(0x1fed_f00d);

cert.verify_is_valid_tls_client_cert(
cert.verify_for_usage(
&[&webpki::ECDSA_P256_SHA256],
&anchors,
anchors,
intermediates,
time,
KeyUsage::client_auth(),
crls,
)
}
Expand Down
5 changes: 2 additions & 3 deletions tests/custom_ekus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ fn check_cert(
time: webpki::Time,
result: Result<(), webpki::Error>,
) {
let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::NonTlsTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let algs = &[
&webpki::RSA_PKCS1_2048_8192_SHA256,
&webpki::ECDSA_P256_SHA256,
Expand All @@ -19,7 +18,7 @@ fn check_cert(
let cert = webpki::EndEntityCert::try_from(ee).unwrap();

assert_eq!(
cert.verify_is_valid_cert_with_eku(algs, &anchors, &[], time, eku, &[]),
cert.verify_for_usage(algs, &anchors, &[], time, eku, &[]),
result
);
}
Expand Down
87 changes: 66 additions & 21 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// 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::KeyUsage;

static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[
&webpki::ECDSA_P256_SHA256,
Expand All @@ -39,15 +39,21 @@ pub fn netflix() {
let inter = include_bytes!("netflix/inter.der");
let ca = include_bytes!("netflix/ca.der");

let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(1_492_441_716); // 2017-04-17T15:08:36Z

let cert = webpki::EndEntityCert::try_from(ee).unwrap();
assert_eq!(
Ok(()),
cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[inter], time)
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[inter],
time,
KeyUsage::server_auth(),
&[]
)
);
}

Expand All @@ -59,15 +65,21 @@ pub fn cloudflare_dns() {
let inter = include_bytes!("cloudflare_dns/inter.der");
let ca = include_bytes!("cloudflare_dns/ca.der");

let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(1_663_495_771);

let cert = webpki::EndEntityCert::try_from(ee).unwrap();
assert_eq!(
Ok(()),
cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[inter], time)
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[inter],
time,
KeyUsage::server_auth(),
&[]
)
);

let check_name = |name: &str| {
Expand Down Expand Up @@ -107,15 +119,21 @@ pub fn wpt() {
let ee: &[u8] = include_bytes!("wpt/ee.der");
let ca = include_bytes!("wpt/ca.der");

let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(1_619_256_684); // 2021-04-24T09:31:24Z

let cert = webpki::EndEntityCert::try_from(ee).unwrap();
assert_eq!(
Ok(()),
cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time)
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[],
time,
KeyUsage::server_auth(),
&[]
)
);
}

Expand All @@ -124,15 +142,21 @@ pub fn ed25519() {
let ee: &[u8] = include_bytes!("ed25519/ee.der");
let ca = include_bytes!("ed25519/ca.der");

let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(1_547_363_522); // 2019-01-13T07:12:02Z

let cert = webpki::EndEntityCert::try_from(ee).unwrap();
assert_eq!(
Ok(()),
cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time)
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[],
time,
KeyUsage::server_auth(),
&[]
)
);
}

Expand All @@ -144,16 +168,31 @@ fn critical_extensions() {

let time = webpki::Time::from_seconds_since_unix_epoch(1_670_779_098);
let anchors = [webpki::TrustAnchor::try_from_cert_der(root).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);

let ee = include_bytes!("critical_extensions/ee-cert-noncrit-unknown-ext.der");
let res = webpki::EndEntityCert::try_from(&ee[..])
.and_then(|cert| cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[ca], time));
let res = webpki::EndEntityCert::try_from(&ee[..]).and_then(|cert| {
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[ca],
time,
KeyUsage::server_auth(),
&[],
)
});
assert_eq!(res, Ok(()), "accept non-critical unknown extension");

let ee = include_bytes!("critical_extensions/ee-cert-crit-unknown-ext.der");
let res = webpki::EndEntityCert::try_from(&ee[..])
.and_then(|cert| cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[ca], time));
let res = webpki::EndEntityCert::try_from(&ee[..]).and_then(|cert| {
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[ca],
time,
KeyUsage::server_auth(),
&[],
)
});
assert_eq!(
res,
Err(webpki::Error::UnsupportedCriticalExtension),
Expand All @@ -180,15 +219,21 @@ fn read_ee_with_neg_serial() {
let ca: &[u8] = include_bytes!("misc/serial_neg_ca.der");
let ee: &[u8] = include_bytes!("misc/serial_neg_ee.der");

let anchors = vec![webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];
let anchors = webpki::TlsServerTrustAnchors(&anchors);
let anchors = [webpki::TrustAnchor::try_from_cert_der(ca).unwrap()];

let time = webpki::Time::from_seconds_since_unix_epoch(1_667_401_500); // 2022-11-02T15:05:00Z

let cert = webpki::EndEntityCert::try_from(ee).unwrap();
assert_eq!(
Ok(()),
cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time)
cert.verify_for_usage(
ALL_SIGALGS,
&anchors,
&[],
time,
KeyUsage::server_auth(),
&[]
)
);
}

Expand Down
Loading

0 comments on commit 875fcef

Please sign in to comment.