1use serde::{Deserialize, Serialize};
2
3use crate::{
4 CtLog, Version,
5 signature::{Signature, SignatureValidationError},
6 tree::HashOutput,
7 utils::codec::{CodecError, Decode, Encode},
8 v1::{SignatureType, responses::GetSthResponse},
9};
10use std::io::{Read, Write};
11
12impl CtLog {
13 pub fn validate_sth_v1(&self, sth: &SignedTreeHead) -> Result<(), SignatureValidationError> {
14 let tree_head_tbs = TreeHeadSignature::from(sth);
15 sth.tree_head_signature
16 .validate(&tree_head_tbs, &self.config.key)
17 }
18}
19
20#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
24pub struct SignedTreeHead {
25 pub(crate) tree_size: u64,
26 pub(crate) timestamp: u64,
27 pub(crate) sha256_root_hash: HashOutput,
28 pub(crate) tree_head_signature: Signature<TreeHeadSignature>,
29}
30
31impl std::fmt::Debug for SignedTreeHead {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("SignedTreeHead")
34 .field("tree_size", &self.tree_size)
35 .field("timestamp", &self.timestamp)
36 .field("sha256_root_hash", &hex::encode(self.sha256_root_hash))
37 .field("tree_head_signature", &self.tree_head_signature)
38 .finish()
39 }
40}
41
42impl SignedTreeHead {
43 pub fn tree_size(&self) -> u64 {
44 self.tree_size
45 }
46
47 pub fn timestamp(&self) -> u64 {
48 self.timestamp
49 }
50
51 pub fn sha256_root_hash(&self) -> &HashOutput {
52 &self.sha256_root_hash
53 }
54}
55
56impl TryFrom<GetSthResponse> for SignedTreeHead {
57 type Error = ();
58
59 fn try_from(value: GetSthResponse) -> Result<Self, Self::Error> {
60 Ok(Self {
61 tree_size: value.tree_size,
62 timestamp: value.timestamp,
63 sha256_root_hash: value.sha256_root_hash.0.try_into().map_err(|_| ())?,
64 tree_head_signature: value.tree_head_signature.0.0,
65 })
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
71pub(crate) struct TreeHeadSignature {
72 pub(crate) version: Version,
73 pub(crate) timestamp: u64,
75 pub(crate) tree_size: u64,
76 pub(crate) sha256_root_hash: [u8; 32],
77}
78
79impl Encode for TreeHeadSignature {
80 fn encode(&self, mut writer: impl Write) -> Result<(), CodecError> {
81 self.version.encode(&mut writer)?;
82 SignatureType::TreeHash.encode(&mut writer)?;
83 self.timestamp.encode(&mut writer)?;
84 self.tree_size.encode(&mut writer)?;
85 self.sha256_root_hash.encode(&mut writer)?;
86 Ok(())
87 }
88}
89
90impl Decode for TreeHeadSignature {
91 fn decode(mut reader: impl Read) -> Result<Self, CodecError> {
92 let version = Version::decode(&mut reader)?;
93 let signature_type = SignatureType::decode(&mut reader)?;
94 match signature_type {
95 SignatureType::CertificateTimeStamp => return Err(CodecError::UnexpectedVariant),
96 SignatureType::TreeHash => (),
97 }
98 let timestamp = u64::decode(&mut reader)?;
99 let tree_size = u64::decode(&mut reader)?;
100 let sha256_root_hash = <[u8; 32]>::decode(&mut reader)?;
101
102 Ok(Self {
103 version,
104 timestamp,
105 tree_size,
106 sha256_root_hash,
107 })
108 }
109}
110
111impl From<&SignedTreeHead> for TreeHeadSignature {
112 fn from(value: &SignedTreeHead) -> Self {
113 Self {
114 version: Version::V1,
115 timestamp: value.timestamp,
116 tree_size: value.tree_size,
117 sha256_root_hash: value.sha256_root_hash,
118 }
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use super::*;
125 use crate::tests::{ARGON2025H1_STH2806, get_log_argon2025h1};
126
127 #[test]
128 fn sth_codec_roundtrip() {
129 let sth: GetSthResponse = serde_json::from_str(ARGON2025H1_STH2806).unwrap();
130 let sth_bytes = serde_json::to_string(&sth).unwrap();
131 let sth2: GetSthResponse = serde_json::from_str(&sth_bytes).unwrap();
132 assert_eq!(sth, sth2);
133 }
134
135 #[test]
136 fn validate_sth() {
137 let log = get_log_argon2025h1();
138 let sth: GetSthResponse = serde_json::from_str(ARGON2025H1_STH2806).unwrap();
139 log.validate_sth_v1(&sth.try_into().unwrap()).unwrap();
140 }
141}