1pub mod checkpoint;
20pub mod tile;
21pub mod tlog;
22
23pub use checkpoint::*;
24pub use tile::*;
25pub use tlog::*;
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30 use base64::prelude::*;
31 use serde::{Deserialize, Deserializer};
32 use std::fs::File;
33 use std::io::Read;
34 use std::path::PathBuf;
35 use url::form_urlencoded;
36
37 #[derive(Deserialize)]
38 struct CtTree {
39 #[serde(rename = "tree_size")]
40 size: u64,
41 #[serde(rename = "sha256_root_hash")]
42 hash: Hash,
43 }
44
45 #[derive(Deserialize)]
46 struct CtEntries {
47 entries: Vec<CtEntry>,
48 }
49
50 #[derive(Deserialize)]
51 struct CtEntry {
52 #[serde(rename = "leaf_input", deserialize_with = "from_base64")]
53 data: Vec<u8>,
54 }
55
56 fn from_base64<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
57 let base64 = String::deserialize(d)?;
58 BASE64_STANDARD
59 .decode(&base64)
60 .map_err(serde::de::Error::custom)
61 }
62
63 #[derive(Deserialize)]
64 struct CtRecordProof {
65 #[serde(rename = "audit_path")]
66 proof: Vec<Hash>,
67 }
68
69 #[derive(Deserialize)]
70 struct CtTreeProof {
71 consistency: Vec<Hash>,
72 }
73
74 fn http_get<T: for<'de> Deserialize<'de>>(url: &str) -> T {
76 let basename = &url
77 .rsplit_once('/')
78 .unwrap()
79 .1
80 .to_string()
81 .replace(|c| !char::is_ascii_alphanumeric(&c), "-");
82
83 let path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests/http_get", basename]
84 .iter()
85 .collect();
86 let mut file = File::open(path).expect("Unable to open file");
87 let mut body = String::new();
88
89 file.read_to_string(&mut body).expect("Unable to read file");
90
91 serde_json::from_str(&body).expect("File contained invalid JSON")
92 }
93
94 #[test]
95 fn test_certificate_transparency() -> Result<(), Error> {
96 let root: CtTree = http_get("http://ct.googleapis.com/logs/argon2020/ct/v1/get-sth");
97
98 let leaf: CtEntries = http_get(
99 "http://ct.googleapis.com/logs/argon2020/ct/v1/get-entries?start=10000&end=10000",
100 );
101
102 let hash = tlog::record_hash(&leaf.entries[0].data);
103
104 let url = format!(
105 "http://ct.googleapis.com/logs/argon2020/ct/v1/get-proof-by-hash?tree_size={}&hash={}",
106 root.size,
107 form_urlencoded::byte_serialize(hash.to_string().as_bytes()).collect::<String>()
108 );
109 let rp: CtRecordProof = http_get(&url);
110
111 tlog::check_record(&rp.proof, root.size, root.hash, 10000, hash)?;
112
113 let url = format!(
114 "http://ct.googleapis.com/logs/argon2020/ct/v1/get-sth-consistency?first=3654490&second={}",
115 root.size);
116 let tp: CtTreeProof = http_get(&url);
117
118 let oh = Hash::parse_hash("AuIZ5V6sDUj1vn3Y1K85oOaQ7y+FJJKtyRTl1edIKBQ=")?;
119 tlog::check_tree(&tp.consistency, root.size, root.hash, 3_654_490, oh)?;
120
121 Ok(())
122 }
123}