1use crate::{
2 Version,
3 store::Hashable,
4 utils::codec::{CodecError, Decode, Encode},
5 v1::{LogEntry, extension::CtExtensions},
6};
7use sha2::{Digest, Sha256};
8use std::io::{Cursor, Read, Write};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct MerkleTreeLeaf {
13 pub(crate) version: Version,
14 pub(crate) leaf: Leaf,
15}
16
17impl Hashable for MerkleTreeLeaf {
18 fn hash(&self) -> [u8; 32] {
19 let mut bytes = Cursor::new(vec![]);
20 bytes.write_all(&[0]).unwrap();
21 self.encode(&mut bytes).unwrap();
22
23 Sha256::digest(bytes.into_inner()).into()
24 }
25}
26
27impl Encode for MerkleTreeLeaf {
28 fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
29 self.version.encode(&mut writer)?;
30 self.leaf.encode(&mut writer)?;
31 Ok(())
32 }
33}
34
35impl Decode for MerkleTreeLeaf {
36 fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
37 Ok(Self {
38 version: Version::decode(&mut reader)?,
39 leaf: Leaf::decode(&mut reader)?,
40 })
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub(crate) enum Leaf {
46 TimestampedEntry(TimestampedEntry),
47}
48
49impl Encode for Leaf {
50 fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
51 match self {
52 Leaf::TimestampedEntry(entry) => {
53 writer.write_all(&[0])?;
54 entry.encode(&mut writer)?;
55 }
56 };
57 Ok(())
58 }
59}
60
61impl Decode for Leaf {
62 fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
63 let mut buf = vec![0u8];
64 reader.read_exact(&mut buf)?;
65
66 match buf[0] {
67 0 => Ok(Leaf::TimestampedEntry(TimestampedEntry::decode(
68 &mut reader,
69 )?)),
70 val => Err(CodecError::UnknownVariant("MerkleLeafType", val as u64)),
71 }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub(crate) struct TimestampedEntry {
77 pub(crate) timestamp: u64,
78 pub(crate) log_entry: LogEntry,
79 pub(crate) extensions: CtExtensions,
80}
81
82impl Encode for TimestampedEntry {
83 fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
84 self.timestamp.encode(&mut writer)?;
85 self.log_entry.encode(&mut writer)?;
86 self.extensions.encode(&mut writer)?;
87 Ok(())
88 }
89}
90
91impl Decode for TimestampedEntry {
92 fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
93 Ok(Self {
94 timestamp: u64::decode(&mut reader)?,
95 log_entry: LogEntry::decode(&mut reader)?,
96 extensions: CtExtensions::decode(&mut reader)?,
97 })
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::{
105 CertificateChain, tests::CERT_CHAIN_GOOGLE_COM, v1::responses::GetEntriesResponse,
106 };
107
108 const GOOGLE_GET_ENTRY: &str = include_str!("../../../testdata/google-entry.json");
109
110 #[test]
111 fn parse_get_entry_response() {
112 let response: GetEntriesResponse = serde_json::from_str(GOOGLE_GET_ENTRY).unwrap();
113 assert_eq!(response.entries.len(), 1);
114
115 let reencoded = serde_json::to_string(&response).unwrap();
117 let response2: GetEntriesResponse = serde_json::from_str(&reencoded).unwrap();
118 assert_eq!(response, response2);
119 }
120
121 #[test]
122 fn generate_precert_comparison() {
123 let response: GetEntriesResponse = serde_json::from_str(GOOGLE_GET_ENTRY).unwrap();
124 let leaf = response.entries[0].leaf_input.0.0.clone();
125
126 let Leaf::TimestampedEntry(entry) = leaf.leaf;
127 let log_entry1 = entry.log_entry;
128
129 let cert2 = CertificateChain::from_pem_chain(CERT_CHAIN_GOOGLE_COM).unwrap();
130 cert2.verify_chain().unwrap();
131 let log_entry2 = cert2.as_log_entry_v1(true).unwrap();
132
133 assert_eq!(log_entry1, log_entry2);
134 }
135
136 #[test]
137 fn test_leaf_creation() {
138 let response: GetEntriesResponse = serde_json::from_str(GOOGLE_GET_ENTRY).unwrap();
139 let leaf1 = response.entries[0].leaf_input.0.0.clone();
140
141 let cert2 = CertificateChain::from_pem_chain(CERT_CHAIN_GOOGLE_COM).unwrap();
142 cert2.verify_chain().unwrap();
143 let sct2 = cert2.cert().extract_scts_v1().unwrap();
144 let leaf2 = cert2.as_leaf_v1(&sct2[0], true).unwrap();
145
146 assert_eq!(leaf1, leaf2)
147 }
148}