107 lines
3.2 KiB
Rust
107 lines
3.2 KiB
Rust
|
use std::sync::Arc;
|
||
|
|
||
|
use base64ct::Encoding;
|
||
|
use ct::{
|
||
|
merkle::{
|
||
|
consts::{EXTRA_DATA_BASE64_BUFFER_SIZE, LEAF_BASE64_BUFFER_SIZE},
|
||
|
types::{LogEntryType, 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 (i, entry) in fetch_entries().await.enumerate() {
|
||
|
// 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 at entry {i}");
|
||
|
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"
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|