ct-rs/tests/log_entry_parsing.rs

106 lines
3.1 KiB
Rust

use std::sync::Arc;
use base64ct::Encoding;
use ct::{
merkle::{
consts::EXTRA_DATA_BASE64_BUFFER_SIZE,
types::{MerkleLeafType, Version}
},
parsing::entry::{parse_entry_extra_data_precert, parse_entry_extra_data_x509}
};
use serde::Deserialize;
use tokio::task::JoinSet;
#[derive(Debug, Deserialize)]
pub struct Entry {
leaf_input: String,
extra_data: String
}
#[derive(Debug, Deserialize)]
pub struct GetEntriesResponse {
entries: Vec<Entry>
}
async fn fetch_entries() -> impl Iterator<Item = Entry> {
let client = Arc::new(reqwest::Client::new());
let mut join_set: JoinSet<GetEntriesResponse> = JoinSet::new();
for i in 0..12u32 {
let client = client.clone();
join_set.spawn(async move {
client
.get("https://oak.ct.letsencrypt.org/2024h2/ct/v1/get-entries")
.query(&[("start", i * 256), ("end", (i + 1) * 256 - 1)])
.send()
.await
.expect("Request to ct log should succeed")
.json()
.await
.expect("Request to ct log should parse properly")
});
}
join_set
.join_all()
.await
.into_iter()
.flat_map(|e| e.entries)
}
#[tokio::test]
async fn test_letsencrypt_2024h2_parsing() {
let mut buffer = [0u8; EXTRA_DATA_BASE64_BUFFER_SIZE];
for entry in fetch_entries().await {
// Parse leaf_input
buffer[..entry.leaf_input.len()].copy_from_slice(entry.leaf_input.as_bytes());
let parsed =
base64ct::Base64::decode_in_place(&mut buffer[..entry.leaf_input.len()])
.expect("leaf_input should parse as base64 properly");
let (input, merkle_tree_leaf) =
ct::parsing::entry::parse_merkle_tree_leaf(parsed)
.expect("leaf_input should parse properly");
// leaf_input Assertions
assert_eq!(input.len(), 0, "All input should be parsed");
let MerkleLeafType::TimeStampedEntry { entry_type, .. } =
merkle_tree_leaf.leaf_type
else {
panic!("Merkle tree leaf should be TimestampedEntry");
};
let Version::V1 = merkle_tree_leaf.version else {
panic!("Merkle tree leaf should be Version::V1");
};
// Calculate this before we have to mutably borrow the buffer again to avoid
// multiple mutable references
let parse_entry_extra_data = match entry_type {
ct::merkle::types::LogEntryType::X509Entry(..) => parse_entry_extra_data_x509,
ct::merkle::types::LogEntryType::PrecertEntry { .. } =>
parse_entry_extra_data_precert,
};
// Parse extra_data
buffer[..entry.extra_data.len()].copy_from_slice(entry.extra_data.as_bytes());
let parsed =
base64ct::Base64::decode_in_place(&mut buffer[..entry.extra_data.len()])
.expect("extra_data should parse as base64 properly");
let (input, parsed) =
parse_entry_extra_data(parsed).expect("extra_data should parse properly");
// extra_data assertions
assert_eq!(input.len(), 0, "All input should be parsed");
match parsed {
ct::merkle::types::EntryExtraData::X509Certificate(chain) =>
assert_ne!(chain.len(), 0, "x509 chain should not be 0 length"),
// TODO: Verify main_certificate against the tbs certificate loaded from
// leaf_entry
ct::merkle::types::EntryExtraData::Precertificate(chain_entry) => assert_ne!(
chain_entry.certificate_chain.len(),
0,
"precert chain should not be 0 length"
)
}
}
}