1use serde_json::Value;
15
16use crate::canonical::canonicalize;
17use crate::generated::proof_bundle::ProofBundle;
18use crate::generated::proof_event::ProofEvent;
19
20pub const TFLOG_MAGIC: &[u8; 8] = b"TFLOG\x01\x00\x00";
21pub const TFPROOF_MAGIC: &[u8; 8] = b"TFPROOF\x01";
22
23#[derive(Debug, thiserror::Error, PartialEq, Eq)]
24pub enum FormatError {
25 #[error("unexpected end of input at offset {0}")]
26 Truncated(usize),
27 #[error("bad magic header at offset {0}")]
28 BadMagic(usize),
29 #[error("length prefix {1} exceeds remaining bytes at offset {0}")]
30 BadLength(usize, u32),
31 #[error("canonical JSON error: {0}")]
32 Canonical(String),
33 #[error("serde error: {0}")]
34 Serde(String),
35 #[error("utf-8 error: {0}")]
36 Utf8(String),
37}
38
39fn put_u32_be(buf: &mut Vec<u8>, n: usize) -> Result<(), FormatError> {
40 let n = u32::try_from(n).map_err(|_| FormatError::BadLength(buf.len(), u32::MAX))?;
41 buf.extend_from_slice(&n.to_be_bytes());
42 Ok(())
43}
44
45fn read_u32_be(buf: &[u8], off: usize) -> Result<u32, FormatError> {
46 if off + 4 > buf.len() {
47 return Err(FormatError::Truncated(off));
48 }
49 let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
50 Ok(u32::from_be_bytes(arr))
51}
52
53pub fn write_tflog(events: &[ProofEvent]) -> Result<Vec<u8>, FormatError> {
56 let mut buf = Vec::with_capacity(256);
57 buf.extend_from_slice(TFLOG_MAGIC);
58 for e in events {
59 let json = serde_json::to_value(e).map_err(|e| FormatError::Serde(e.to_string()))?;
60 let body = canonicalize(&json).map_err(|e| FormatError::Canonical(e.to_string()))?;
61 let bytes = body.into_bytes();
62 put_u32_be(&mut buf, bytes.len())?;
63 buf.extend_from_slice(&bytes);
64 }
65 Ok(buf)
66}
67
68pub fn append_tflog(existing: &mut Vec<u8>, event: &ProofEvent) -> Result<(), FormatError> {
69 if existing.is_empty() {
70 existing.extend_from_slice(TFLOG_MAGIC);
71 } else if existing.len() < TFLOG_MAGIC.len() || &existing[..TFLOG_MAGIC.len()] != TFLOG_MAGIC {
72 return Err(FormatError::BadMagic(0));
73 }
74 let json = serde_json::to_value(event).map_err(|e| FormatError::Serde(e.to_string()))?;
75 let body = canonicalize(&json).map_err(|e| FormatError::Canonical(e.to_string()))?;
76 let bytes = body.into_bytes();
77 put_u32_be(existing, bytes.len())?;
78 existing.extend_from_slice(&bytes);
79 Ok(())
80}
81
82pub fn read_tflog(buf: &[u8]) -> Result<Vec<ProofEvent>, FormatError> {
83 if buf.len() < TFLOG_MAGIC.len() {
84 return Err(FormatError::Truncated(0));
85 }
86 if &buf[..TFLOG_MAGIC.len()] != TFLOG_MAGIC {
87 return Err(FormatError::BadMagic(0));
88 }
89 let mut out = Vec::new();
90 let mut off = TFLOG_MAGIC.len();
91 while off < buf.len() {
92 let len = read_u32_be(buf, off)? as usize;
93 off += 4;
94 if off + len > buf.len() {
95 return Err(FormatError::BadLength(off - 4, len as u32));
96 }
97 let slice = &buf[off..off + len];
98 let text = std::str::from_utf8(slice).map_err(|e| FormatError::Utf8(e.to_string()))?;
99 let value: Value =
100 serde_json::from_str(text).map_err(|e| FormatError::Serde(e.to_string()))?;
101 let event: ProofEvent =
102 serde_json::from_value(value).map_err(|e| FormatError::Serde(e.to_string()))?;
103 out.push(event);
104 off += len;
105 }
106 Ok(out)
107}
108
109pub fn write_tfproof(bundle: &ProofBundle, signature: &[u8]) -> Result<Vec<u8>, FormatError> {
112 let mut buf = Vec::with_capacity(1024);
113 buf.extend_from_slice(TFPROOF_MAGIC);
114 let body_json = serde_json::to_value(bundle).map_err(|e| FormatError::Serde(e.to_string()))?;
115 let body = canonicalize(&body_json).map_err(|e| FormatError::Canonical(e.to_string()))?;
116 let body_bytes = body.into_bytes();
117 put_u32_be(&mut buf, body_bytes.len())?;
118 buf.extend_from_slice(&body_bytes);
119 put_u32_be(&mut buf, signature.len())?;
120 buf.extend_from_slice(signature);
121 Ok(buf)
122}
123
124#[derive(Debug)]
125pub struct TfproofParts {
126 pub bundle: ProofBundle,
127 pub signature: Vec<u8>,
128 pub canonical_body: Vec<u8>,
129}
130
131pub fn read_tfproof(buf: &[u8]) -> Result<TfproofParts, FormatError> {
132 if buf.len() < TFPROOF_MAGIC.len() {
133 return Err(FormatError::Truncated(0));
134 }
135 if &buf[..TFPROOF_MAGIC.len()] != TFPROOF_MAGIC {
136 return Err(FormatError::BadMagic(0));
137 }
138 let mut off = TFPROOF_MAGIC.len();
139 let body_len = read_u32_be(buf, off)? as usize;
140 off += 4;
141 if off + body_len > buf.len() {
142 return Err(FormatError::BadLength(off - 4, body_len as u32));
143 }
144 let body_slice = &buf[off..off + body_len];
145 let body_text =
146 std::str::from_utf8(body_slice).map_err(|e| FormatError::Utf8(e.to_string()))?;
147 let body_value: Value =
148 serde_json::from_str(body_text).map_err(|e| FormatError::Serde(e.to_string()))?;
149 let bundle: ProofBundle =
150 serde_json::from_value(body_value).map_err(|e| FormatError::Serde(e.to_string()))?;
151 off += body_len;
152
153 let sig_len = read_u32_be(buf, off)? as usize;
154 off += 4;
155 if off + sig_len > buf.len() {
156 return Err(FormatError::BadLength(off - 4, sig_len as u32));
157 }
158 let signature = buf[off..off + sig_len].to_vec();
159
160 Ok(TfproofParts {
161 bundle,
162 signature,
163 canonical_body: body_slice.to_vec(),
164 })
165}