forked from rustls/webpki
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
benches: initial CRL parsing/searching benchmarks.
This commit adds two kinds of CRL benchmark tests: 1. Parsing benchmarks, testing how long it takes to use `CertRevocationList::from_der`. 2. Searching benchmarks, testing how long it takes to search an already parsed CRL for a serial number that doesn't appear in the CRL. In both cases we add benchmarks that use three sizes of CRLs: 1. a small CRL (2,000 serials, ~72kb) 2. a medium CRL (600,000 serials, ~22mb) 3. a large CRL (1,500,000 serials, ~50mb) Since large test files aren't suitable for inclusion in git, these CRLs are generated on first benchmark run using `rcgen`. The upstream `rcgen` project doesn't yet have CRL generation support, so a Cargo patch is used to pin the `rcgen` dependency to a branch of a fork that adds the required capability. Initial results from my laptop: ``` Running benches/benchmark.rs (target/release/deps/benchmarks-787bf5636ab586f8) running 6 tests test bench_parse_crl_large ... bench: 49 ns/iter (+/- 12) test bench_parse_crl_medium ... bench: 56 ns/iter (+/- 12) test bench_parse_crl_small ... bench: 53 ns/iter (+/- 1) test bench_search_crl_large ... bench: 70,972,415 ns/iter (+/- 5,266,259) test bench_search_crl_medium ... bench: 29,121,229 ns/iter (+/- 2,619,706) test bench_search_crl_small ... bench: 62,582 ns/iter (+/- 2,309) test result: ok. 0 passed; 0 failed; 0 ignored; 6 measured ``` Laptop specs: * rustc 1.69.0 * NixOS 22.11 * 12th Gen Intel(R) Core(TM) i7-1280P * 32GB DDR4-3200 * WD_BLACK SN850 NVMe
- Loading branch information
Showing
3 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,6 @@ target/ | |
# IntelliJ junk | ||
*.iml | ||
.idea | ||
|
||
# Benchmark data | ||
benches/*.der |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
use bencher::{benchmark_group, benchmark_main, Bencher}; | ||
use once_cell::sync::Lazy; | ||
use rcgen::{ | ||
date_time_ymd, BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, | ||
CertificateRevocationListParams, IsCa, KeyIdMethod, KeyUsagePurpose, RevocationReason, | ||
RevokedCertParams, SerialNumber, PKCS_ECDSA_P256_SHA256, | ||
}; | ||
|
||
use webpki::CertRevocationList; | ||
|
||
use std::fs::File; | ||
use std::io::{ErrorKind, Read, Write}; | ||
use std::path::Path; | ||
use std::sync::Mutex; | ||
|
||
/// Lazy initialized CRL issuer to be used when generating CRL data. Includes | ||
/// `KeyUsagePurpose::CrlSign` key usage bit. | ||
static CRL_ISSUER: Lazy<Mutex<Certificate>> = Lazy::new(|| { | ||
let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]); | ||
issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
issuer_params.key_usages = vec![ | ||
KeyUsagePurpose::KeyCertSign, | ||
KeyUsagePurpose::DigitalSignature, | ||
KeyUsagePurpose::CrlSign, | ||
]; | ||
Mutex::new(Certificate::from_params(issuer_params).unwrap()) | ||
}); | ||
|
||
/// Number of revoked certificates to include in the small benchmark CRL. Produces a CRL roughly | ||
/// ~72kb in size when serialized to disk. | ||
const SMALL_CRL_CERT_COUNT: usize = 2_000; | ||
/// Number of revoked certificates to include in the medium benchmark CRL. Produces a CRL roughly | ||
/// ~22mb in size when serialized to disk. | ||
const MEDIUM_CRL_CERT_COUNT: usize = 600_000; | ||
/// Number of revoked certificates to include in the large benchmark CRL. Produces a CRL roughly | ||
/// ~50mb in size when serialized to disk. | ||
const LARGE_CRL_CERT_COUNT: usize = 1_500_000; | ||
|
||
/// A fake serial number to use in the search tests. In order to provoke a full scan of the CRL | ||
/// contents this serial should **not** appear in the revoked certificates. | ||
const FAKE_SERIAL: &[u8] = &[0xC0, 0xFF, 0xEE]; | ||
|
||
/// Try to load a DER bytes from `crl_path`. If that file path does not exist, generate a CRL | ||
/// with `revoked_count` revoked certificates, write the DER encoding to `crl_path` and return the | ||
/// newly created DER bytes. | ||
fn load_or_generate(crl_path: impl AsRef<Path> + Copy, revoked_count: usize) -> Vec<u8> { | ||
match File::open(crl_path) { | ||
Ok(mut crl_file) => { | ||
let mut crl_der = Vec::new(); | ||
crl_file.read_to_end(&mut crl_der).unwrap(); | ||
return crl_der; | ||
} | ||
Err(e) => match e.kind() { | ||
ErrorKind::NotFound => match File::create(crl_path) { | ||
Err(e) => panic!("unexpected err creating CRL file: {:?}", e), | ||
Ok(mut crl_file) => { | ||
let new_crl = generate_crl(revoked_count); | ||
crl_file.write_all(&new_crl).unwrap(); | ||
return new_crl; | ||
} | ||
}, | ||
e => { | ||
panic!("unexpected err opening CRL file: {:?}", e); | ||
} | ||
}, | ||
} | ||
} | ||
|
||
/// Create a new benchmark CRL with `revoked_count` revoked certificates. | ||
fn generate_crl(revoked_count: usize) -> Vec<u8> { | ||
let mut revoked_certs = Vec::with_capacity(revoked_count); | ||
(0..revoked_count).for_each(|_| { | ||
revoked_certs.push(RevokedCertParams { | ||
// TODO(@cpu): Randomize parameters. | ||
serial_number: SerialNumber::from(9999), | ||
revocation_time: date_time_ymd(2024, 06, 17), | ||
reason_code: Some(RevocationReason::KeyCompromise), | ||
invalidity_date: None, | ||
}); | ||
}); | ||
|
||
let crl = CertificateRevocationListParams { | ||
this_update: date_time_ymd(2023, 06, 17), | ||
next_update: date_time_ymd(2024, 06, 17), | ||
crl_number: SerialNumber::from(1234), | ||
alg: &PKCS_ECDSA_P256_SHA256, | ||
key_identifier_method: KeyIdMethod::Sha256, | ||
revoked_certs, | ||
}; | ||
let crl = CertificateRevocationList::from_params(crl).unwrap(); | ||
let issuer = CRL_ISSUER.lock().unwrap(); | ||
crl.serialize_der_with_signer(&issuer).unwrap() | ||
} | ||
|
||
/// Benchmark parsing a small CRL file. | ||
fn bench_parse_crl_small(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/small.crl.der", SMALL_CRL_CERT_COUNT); | ||
|
||
c.iter(|| CertRevocationList::from_der(&crl_bytes).unwrap()); | ||
} | ||
|
||
/// Benchmark parsing a medium CRL file. | ||
fn bench_parse_crl_medium(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/medium.crl.der", MEDIUM_CRL_CERT_COUNT); | ||
|
||
c.iter(|| CertRevocationList::from_der(&crl_bytes).unwrap()); | ||
} | ||
|
||
/// Benchmark parsing a large CRL file. | ||
fn bench_parse_crl_large(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/large.crl.der", LARGE_CRL_CERT_COUNT); | ||
|
||
c.iter(|| CertRevocationList::from_der(&crl_bytes).unwrap()); | ||
} | ||
|
||
/// Benchmark searching a small CRL file for a serial that does not appear. Doesn't include the | ||
/// time it takes to parse the CRL in the benchmark task. | ||
fn bench_search_crl_small(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/small.crl.der", SMALL_CRL_CERT_COUNT); | ||
let crl = CertRevocationList::from_der(&crl_bytes).unwrap(); | ||
|
||
c.iter(|| crl.find_serial(FAKE_SERIAL)) | ||
} | ||
|
||
/// Benchmark searching a medium CRL file for a serial that does not appear. Doesn't include the | ||
/// time it takes to parse the CRL in the benchmark task. | ||
fn bench_search_crl_medium(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/medium.crl.der", MEDIUM_CRL_CERT_COUNT); | ||
let crl = CertRevocationList::from_der(&crl_bytes).unwrap(); | ||
|
||
c.iter(|| crl.find_serial(FAKE_SERIAL)) | ||
} | ||
|
||
/// Benchmark searching a large CRL file for a serial that does not appear. Doesn't include the | ||
/// time it takes to parse the CRL in the benchmark task. | ||
fn bench_search_crl_large(c: &mut Bencher) { | ||
let crl_bytes = load_or_generate("./benches/large.crl.der", LARGE_CRL_CERT_COUNT); | ||
let crl = CertRevocationList::from_der(&crl_bytes).unwrap(); | ||
|
||
c.iter(|| crl.find_serial(FAKE_SERIAL)) | ||
} | ||
|
||
benchmark_group!( | ||
crl_benches, | ||
bench_parse_crl_small, | ||
bench_parse_crl_medium, | ||
bench_parse_crl_large, | ||
bench_search_crl_small, | ||
bench_search_crl_medium, | ||
bench_search_crl_large, | ||
); | ||
|
||
benchmark_main!(crl_benches); |