| // This program does benchmarking of the functions in verify.rs, |
| // that do certificate chain validation and signature verification. |
| // |
| // Note: we don't use any of the standard 'cargo bench', 'test::Bencher', |
| // etc. because it's unstable at the time of writing. |
| |
| use std::time::{Duration, Instant, SystemTime}; |
| |
| use crate::key; |
| use crate::verify; |
| use crate::verify::ServerCertVerifier; |
| use crate::{anchors, OwnedTrustAnchor}; |
| |
| use webpki_roots; |
| |
| fn duration_nanos(d: Duration) -> u64 { |
| ((d.as_secs() as f64) * 1e9 + (d.subsec_nanos() as f64)) as u64 |
| } |
| |
| #[test] |
| fn test_reddit_cert() { |
| Context::new( |
| "reddit", |
| "reddit.com", |
| &[ |
| include_bytes!("testdata/cert-reddit.0.der"), |
| include_bytes!("testdata/cert-reddit.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_github_cert() { |
| Context::new( |
| "github", |
| "github.com", |
| &[ |
| include_bytes!("testdata/cert-github.0.der"), |
| include_bytes!("testdata/cert-github.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_arstechnica_cert() { |
| Context::new( |
| "arstechnica", |
| "arstechnica.com", |
| &[ |
| include_bytes!("testdata/cert-arstechnica.0.der"), |
| include_bytes!("testdata/cert-arstechnica.1.der"), |
| include_bytes!("testdata/cert-arstechnica.2.der"), |
| include_bytes!("testdata/cert-arstechnica.3.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_servo_cert() { |
| Context::new( |
| "servo", |
| "servo.org", |
| &[ |
| include_bytes!("testdata/cert-servo.0.der"), |
| include_bytes!("testdata/cert-servo.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_twitter_cert() { |
| Context::new( |
| "twitter", |
| "twitter.com", |
| &[ |
| include_bytes!("testdata/cert-twitter.0.der"), |
| include_bytes!("testdata/cert-twitter.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_wikipedia_cert() { |
| Context::new( |
| "wikipedia", |
| "wikipedia.org", |
| &[ |
| include_bytes!("testdata/cert-wikipedia.0.der"), |
| include_bytes!("testdata/cert-wikipedia.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_google_cert() { |
| Context::new( |
| "google", |
| "www.google.com", |
| &[ |
| include_bytes!("testdata/cert-google.0.der"), |
| include_bytes!("testdata/cert-google.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_hn_cert() { |
| Context::new( |
| "hn", |
| "news.ycombinator.com", |
| &[ |
| include_bytes!("testdata/cert-hn.0.der"), |
| include_bytes!("testdata/cert-hn.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_stackoverflow_cert() { |
| Context::new( |
| "stackoverflow", |
| "stackoverflow.com", |
| &[ |
| include_bytes!("testdata/cert-stackoverflow.0.der"), |
| include_bytes!("testdata/cert-stackoverflow.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_duckduckgo_cert() { |
| Context::new( |
| "duckduckgo", |
| "duckduckgo.com", |
| &[ |
| include_bytes!("testdata/cert-duckduckgo.0.der"), |
| include_bytes!("testdata/cert-duckduckgo.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_rustlang_cert() { |
| Context::new( |
| "rustlang", |
| "www.rust-lang.org", |
| &[ |
| include_bytes!("testdata/cert-rustlang.0.der"), |
| include_bytes!("testdata/cert-rustlang.1.der"), |
| include_bytes!("testdata/cert-rustlang.2.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| #[test] |
| fn test_wapo_cert() { |
| Context::new( |
| "wapo", |
| "www.washingtonpost.com", |
| &[ |
| include_bytes!("testdata/cert-wapo.0.der"), |
| include_bytes!("testdata/cert-wapo.1.der"), |
| ], |
| ) |
| .bench(100) |
| } |
| |
| struct Context { |
| name: &'static str, |
| domain: &'static str, |
| roots: anchors::RootCertStore, |
| chain: Vec<key::Certificate>, |
| now: SystemTime, |
| } |
| |
| impl Context { |
| fn new(name: &'static str, domain: &'static str, certs: &[&'static [u8]]) -> Self { |
| let mut roots = anchors::RootCertStore::empty(); |
| roots.add_trust_anchors( |
| webpki_roots::TLS_SERVER_ROOTS |
| .iter() |
| .map(|ta| { |
| OwnedTrustAnchor::from_subject_spki_name_constraints( |
| ta.subject, |
| ta.spki, |
| ta.name_constraints, |
| ) |
| }), |
| ); |
| Self { |
| name, |
| domain, |
| roots, |
| chain: certs |
| .iter() |
| .copied() |
| .map(|bytes| key::Certificate(bytes.to_vec())) |
| .collect(), |
| now: SystemTime::UNIX_EPOCH + Duration::from_secs(1640870720), |
| } |
| } |
| |
| fn bench(&self, count: usize) { |
| let verifier = verify::WebPkiVerifier::new(self.roots.clone(), None); |
| const SCTS: &[&[u8]] = &[]; |
| const OCSP_RESPONSE: &[u8] = &[]; |
| let mut times = Vec::new(); |
| |
| let (end_entity, intermediates) = self.chain.split_first().unwrap(); |
| for _ in 0..count { |
| let start = Instant::now(); |
| let server_name = self.domain.try_into().unwrap(); |
| verifier |
| .verify_server_cert( |
| end_entity, |
| intermediates, |
| &server_name, |
| &mut SCTS.iter().copied(), |
| OCSP_RESPONSE, |
| self.now, |
| ) |
| .unwrap(); |
| times.push(duration_nanos(Instant::now().duration_since(start))); |
| } |
| |
| println!( |
| "verify_server_cert({}): min {:?}us", |
| self.name, |
| times.iter().min().unwrap() / 1000 |
| ); |
| } |
| } |