2024-10-25 22:05:38 -06:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use base64ct::Encoding;
|
|
|
|
use ct::{
|
2024-10-28 17:42:42 -06:00
|
|
|
api::{responses::GetEntriesResponseEntry, CtApiClient},
|
|
|
|
merkle::{
|
2024-10-26 17:41:49 -06:00
|
|
|
consts::EXTRA_DATA_BASE64_BUFFER_SIZE,
|
|
|
|
types::{MerkleLeafType, Version}
|
2024-10-28 17:42:42 -06:00
|
|
|
},
|
|
|
|
parsing::entry::{parse_entry_extra_data_precert, parse_entry_extra_data_x509}
|
2024-10-25 22:05:38 -06:00
|
|
|
};
|
|
|
|
use tokio::task::JoinSet;
|
|
|
|
|
2024-10-27 17:18:35 -06:00
|
|
|
async fn fetch_entries() -> impl Iterator<Item = GetEntriesResponseEntry> {
|
2024-10-28 17:42:42 -06:00
|
|
|
let client = Arc::new(CtApiClient::new_with_client(
|
|
|
|
"https://oak.ct.letsencrypt.org/2024h2/ct/v1/get-entries",
|
|
|
|
Arc::new(reqwest::Client::new())
|
|
|
|
));
|
2024-10-27 17:18:35 -06:00
|
|
|
let mut join_set: JoinSet<_> = JoinSet::new();
|
|
|
|
for i in 0..12u64 {
|
2024-10-25 22:05:38 -06:00
|
|
|
let client = client.clone();
|
|
|
|
join_set.spawn(async move {
|
|
|
|
client
|
2024-10-28 17:42:42 -06:00
|
|
|
.get_log_entries(i * 256, (i + 1) * 256 - 1)
|
2024-10-25 22:05:38 -06:00
|
|
|
.await
|
2024-10-27 17:18:35 -06:00
|
|
|
.expect("Request should succeed")
|
2024-10-25 22:05:38 -06:00
|
|
|
});
|
|
|
|
}
|
|
|
|
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];
|
|
|
|
|
2024-10-26 17:41:49 -06:00
|
|
|
for entry in fetch_entries().await {
|
2024-10-25 22:05:38 -06:00
|
|
|
// 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
|
2024-10-26 17:41:49 -06:00
|
|
|
assert_eq!(input.len(), 0, "All input should be parsed");
|
2024-10-25 22:05:38 -06:00
|
|
|
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"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|