Skip to content

Commit

Permalink
bettertls: test both pathbuilding and nameconstraints.
Browse files Browse the repository at this point in the history
Previously the bettertls test data and runner only included the
'pathbuilding' suite. This commit brings in the remaining
'nameconstraints' tests.

It turns out very little needs to change to also run these tests. In
addition to trying path building with `verify_for_usage`, we also need
to perform name verification for the subject indicated in the testcase
'hostname' field with `verify_is_valid_for_subject_name`. That's about
it, the rest is plumbing. :)

One additional point of interest: with the full tests included the
vendored JSON was about ~33mb. The `Cargo.toml` config doesn't
include the `third-party` directory in the packaged crate, but it
still felt too large. As a workaround this commit applies bzip2
compression, bringing it down to ~7mb, and updates the test runner to
decompresses it on the fly.
  • Loading branch information
cpu committed Aug 14, 2023
1 parent 91e42a5 commit eb1a4dd
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 27 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ untrusted = "0.7.1"
[dev-dependencies]
base64 = "0.21"
bencher = "0.1.5"
bzip2 = "0.4.4"
once_cell = "1.17.2"
rcgen = { version = "0.11.1", default-features = false }
serde = { version = "1.0", features = ["derive"] }
Expand Down
88 changes: 66 additions & 22 deletions tests/better_tls.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
#![cfg(feature = "ring")]

use std::collections::HashMap;
use std::fs::File;

use base64::{engine::general_purpose, Engine as _};
use bzip2::read::BzDecoder;
use serde::Deserialize;
use std::collections::HashMap;
use webpki::{KeyUsage, TrustAnchor};

use webpki::{KeyUsage, SubjectNameRef, TrustAnchor};

#[test]
pub fn path_building() {
let raw_json = include_bytes!("../third-party/bettertls/pathbuilding.tests.json");
let better_tls: BetterTls = serde_json::from_slice(raw_json).expect("invalid test JSON");
println!("Testing BetterTLS revision {:?}", better_tls.revision);
fn better_tls() {
let better_tls = testdata();
let root_der = &better_tls.root_der();
let roots = &[TrustAnchor::try_from_cert_der(root_der).expect("invalid trust anchor")];

let suite = "pathbuilding";
run_testsuite(
suite,
better_tls
.suites
.get(suite)
.expect(&format!("missing {suite} suite")),
roots,
);
}

#[test]
fn name_constraints() {
let better_tls = testdata();
let root_der = &better_tls.root_der();
let roots = &[TrustAnchor::try_from_cert_der(root_der).expect("invalid trust anchor")];

let path_building_suite = better_tls
.suites
.get("pathbuilding")
.expect("missing pathbuilding suite");
let suite = "nameconstraints";
run_testsuite(
suite,
better_tls
.suites
.get(suite)
.expect(&format!("missing {suite} suite")),
roots,
);
}

for testcase in &path_building_suite.test_cases {
println!("Testing path building test case {:?}", testcase.id);
fn run_testsuite(suite_name: &str, suite: &BetterTlsSuite, roots: &[TrustAnchor]) {
for testcase in &suite.test_cases {
println!("Testing {suite_name} test case {}", testcase.id);

let certs_der = testcase.certs_der();
let ee_der = &certs_der[0];
Expand All @@ -34,16 +59,23 @@ pub fn path_building() {

// Set the time to the time of test case generation. This ensures that the test case
// certificates won't expire.
let now = webpki::Time::from_seconds_since_unix_epoch(1_688_651_734);

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

let result = ee_cert
.verify_for_usage(
&[webpki::ECDSA_P256_SHA256], // All of the BetterTLS testcases use P256 keys.
roots,
intermediates,
now,
KeyUsage::server_auth(),
None,
)
.and_then(|_| {
ee_cert.verify_is_valid_for_subject_name(
SubjectNameRef::try_from_ascii_str(&testcase.hostname)
.expect("invalid testcase hostname"),
)
});

match testcase.expected {
ExpectedResult::Accept => assert!(result.is_ok(), "expected success, got {:?}", result),
Expand All @@ -54,6 +86,17 @@ pub fn path_building() {
}
}

fn testdata() -> BetterTls {
let mut data_file = File::open("third-party/bettertls/bettertls.tests.json.bz2")
.expect("failed to open data file");
let decompressor = BzDecoder::new(&mut data_file);

let better_tls: BetterTls = serde_json::from_reader(decompressor).expect("invalid test JSON");
println!("Testing BetterTLS revision {:?}", better_tls.revision);

better_tls
}

#[derive(Deserialize, Debug)]
struct BetterTls {
#[serde(rename(deserialize = "betterTlsRevision"))]
Expand Down Expand Up @@ -81,6 +124,7 @@ struct BetterTlsSuite {
struct BetterTlsTest {
id: u32,
certificates: Vec<String>,
hostname: String,
expected: ExpectedResult,
}

Expand Down
14 changes: 10 additions & 4 deletions third-party/bettertls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ Generated using the Netflix [bettertls] project.

[bettertls]: https://github.com/Netflix/bettertls

## Pathbuilding
## Test Data

To regenerate pathbuilding test data:
To regenerate vendored test data:

1. Install Go
2. Generate the JSON testdata export for the path building suite:
2. Generate the JSON testdata export:

```bash
GOBIN=$PWD go install github.com/Netflix/bettertls/test-suites/cmd/bettertls@latest
./bettertls export-tests --suite pathbuilding --out ./pathbuilding.tests.json
./bettertls export-tests --out ./bettertls.tests.json
```

3. Bzip2 compress it:

```bash
bzip2 ./bettertls.tests.json
```
Binary file added third-party/bettertls/bettertls.tests.json.bz2
Binary file not shown.
1 change: 0 additions & 1 deletion third-party/bettertls/pathbuilding.tests.json

This file was deleted.

0 comments on commit eb1a4dd

Please sign in to comment.