1use ciborium::value::Value;
8use serde::{de::DeserializeOwned, Serialize};
9use std::io::{Read, Write};
10
11use super::{CodecError, Result, CBOR_TAG_COMPACT_REF, CBOR_TAG_CPOP, CBOR_TAG_CWAR};
12
13pub fn encode<T: Serialize>(value: &T) -> Result<Vec<u8>> {
15 let mut buffer = Vec::new();
16 ciborium::into_writer(value, &mut buffer).map_err(|e| CodecError::CborEncode(e.to_string()))?;
17 Ok(buffer)
18}
19
20pub fn decode<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
22 ciborium::from_reader(data).map_err(|e| CodecError::CborDecode(e.to_string()))
23}
24
25pub fn encode_to<T: Serialize, W: Write>(value: &T, writer: W) -> Result<()> {
27 ciborium::into_writer(value, writer).map_err(|e| CodecError::CborEncode(e.to_string()))
28}
29
30pub fn decode_from<T: DeserializeOwned, R: Read>(reader: R) -> Result<T> {
32 ciborium::from_reader(reader).map_err(|e| CodecError::CborDecode(e.to_string()))
33}
34
35pub fn encode_cpop<T: Serialize>(value: &T) -> Result<Vec<u8>> {
37 encode_tagged(value, CBOR_TAG_CPOP)
38}
39
40pub fn encode_cwar<T: Serialize>(value: &T) -> Result<Vec<u8>> {
42 encode_tagged(value, CBOR_TAG_CWAR)
43}
44
45pub fn encode_compact_ref<T: Serialize>(value: &T) -> Result<Vec<u8>> {
47 encode_tagged(value, CBOR_TAG_COMPACT_REF)
48}
49
50pub fn encode_tagged<T: Serialize>(value: &T, tag: u64) -> Result<Vec<u8>> {
52 let inner = encode(value)?;
53 let inner_value: Value =
54 ciborium::from_reader(&inner[..]).map_err(|e| CodecError::CborDecode(e.to_string()))?;
55
56 let tagged = Value::Tag(tag, Box::new(inner_value));
57
58 let mut buffer = Vec::new();
59 ciborium::into_writer(&tagged, &mut buffer)
60 .map_err(|e| CodecError::CborEncode(e.to_string()))?;
61
62 Ok(buffer)
63}
64
65pub fn decode_tagged<T: DeserializeOwned>(data: &[u8], expected_tag: u64) -> Result<T> {
67 let value: Value =
68 ciborium::from_reader(data).map_err(|e| CodecError::CborDecode(e.to_string()))?;
69
70 match value {
71 Value::Tag(actual_tag, inner) => {
72 if actual_tag != expected_tag {
73 return Err(CodecError::InvalidTag {
74 expected: expected_tag,
75 actual: actual_tag,
76 });
77 }
78
79 let mut inner_bytes = Vec::new();
80 ciborium::into_writer(&*inner, &mut inner_bytes)
81 .map_err(|e| CodecError::CborEncode(e.to_string()))?;
82
83 ciborium::from_reader(&inner_bytes[..])
84 .map_err(|e| CodecError::CborDecode(e.to_string()))
85 }
86 _ => Err(CodecError::MissingTag),
87 }
88}
89
90pub fn decode_cpop<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
92 decode_tagged(data, CBOR_TAG_CPOP)
93}
94
95pub fn decode_cwar<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
97 decode_tagged(data, CBOR_TAG_CWAR)
98}
99
100pub fn decode_compact_ref<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
102 decode_tagged(data, CBOR_TAG_COMPACT_REF)
103}
104
105pub fn has_tag(data: &[u8], expected_tag: u64) -> bool {
107 if let Ok(value) = ciborium::from_reader::<Value, _>(data) {
108 matches!(value, Value::Tag(tag, _) if tag == expected_tag)
109 } else {
110 false
111 }
112}
113
114pub fn extract_tag(data: &[u8]) -> Option<u64> {
116 if let Ok(value) = ciborium::from_reader::<Value, _>(data) {
117 match value {
118 Value::Tag(tag, _) => Some(tag),
119 _ => None,
120 }
121 } else {
122 None
123 }
124}
125
126pub mod keys {
128 pub const VERSION: i64 = 1;
129 pub const EXPORTED_AT: i64 = 2;
130 pub const STRENGTH: i64 = 3;
131 pub const PROVENANCE: i64 = 4;
132 pub const DOCUMENT: i64 = 5;
133 pub const CHECKPOINTS: i64 = 6;
134 pub const VDF_PARAMS: i64 = 7;
135 pub const CHAIN_HASH: i64 = 8;
136 pub const DECLARATION: i64 = 9;
137 pub const PRESENCE: i64 = 10;
138 pub const HARDWARE: i64 = 11;
139 pub const KEYSTROKE: i64 = 12;
140 pub const BEHAVIORAL: i64 = 13;
141 pub const CONTEXTS: i64 = 14;
142 pub const EXTERNAL: i64 = 15;
143 pub const KEY_HIERARCHY: i64 = 16;
144 pub const JITTER_BINDING: i64 = 17;
145 pub const TIME_EVIDENCE: i64 = 18;
146 pub const BIOLOGY_CLAIM: i64 = 19;
147 pub const CLAIMS: i64 = 20;
148
149 pub const ENTROPY_COMMITMENT: i64 = 21;
150 pub const SOURCES: i64 = 22;
151 pub const SUMMARY: i64 = 23;
152 pub const BINDING_MAC: i64 = 24;
153 pub const RAW_INTERVALS: i64 = 25;
154 pub const ACTIVE_PROBES: i64 = 26;
155 pub const LABYRINTH_STRUCTURE: i64 = 27;
156
157 pub const VALIDATION_STATUS: i64 = 31;
158 pub const MILLIBITS: i64 = 32;
159 pub const PARAMETER_VERSION: i64 = 33;
160 pub const THRESHOLDS: i64 = 34;
161
162 pub const BINDING_TIER: i64 = 41;
163 pub const TSA_RESPONSES: i64 = 42;
164 pub const BLOCKCHAIN_ANCHORS: i64 = 43;
165 pub const ROUGHTIME_SAMPLES: i64 = 44;
166
167 pub const INPUT: i64 = 51;
168 pub const OUTPUT: i64 = 52;
169 pub const ITERATIONS: i64 = 53;
170 pub const PROOF: i64 = 54;
171 pub const CALIBRATION: i64 = 55;
172
173 pub const GALTON_INVARIANT: i64 = 61;
174 pub const REFLEX_GATE: i64 = 62;
175
176 pub const EMBEDDING_DIMENSION: i64 = 71;
177 pub const TIME_DELAY: i64 = 72;
178 pub const ATTRACTOR_POINTS: i64 = 73;
179 pub const BETTI_NUMBERS: i64 = 74;
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use serde::{Deserialize, Serialize};
186
187 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
188 struct TestPacket {
189 version: i32,
190 data: Vec<u8>,
191 }
192
193 #[test]
194 fn test_tagged_roundtrip() {
195 let original = TestPacket {
196 version: 1,
197 data: vec![1, 2, 3, 4, 5],
198 };
199
200 let encoded = encode_cpop(&original).unwrap();
201 let decoded: TestPacket = decode_cpop(&encoded).unwrap();
202
203 assert_eq!(original, decoded);
204 }
205
206 #[test]
207 fn test_tag_detection() {
208 let packet = TestPacket {
209 version: 1,
210 data: vec![],
211 };
212
213 let cpop_encoded = encode_cpop(&packet).unwrap();
214 let cwar_encoded = encode_cwar(&packet).unwrap();
215
216 assert!(has_tag(&cpop_encoded, CBOR_TAG_CPOP));
217 assert!(!has_tag(&cpop_encoded, CBOR_TAG_CWAR));
218
219 assert!(has_tag(&cwar_encoded, CBOR_TAG_CWAR));
220 assert!(!has_tag(&cwar_encoded, CBOR_TAG_CPOP));
221 }
222
223 #[test]
224 fn test_tag_extraction() {
225 let packet = TestPacket {
226 version: 1,
227 data: vec![],
228 };
229
230 let encoded = encode_cpop(&packet).unwrap();
231 assert_eq!(extract_tag(&encoded), Some(CBOR_TAG_CPOP));
232
233 let untagged = encode(&packet).unwrap();
234 assert_eq!(extract_tag(&untagged), None);
235 }
236
237 #[test]
238 fn test_wrong_tag_error() {
239 let packet = TestPacket {
240 version: 1,
241 data: vec![],
242 };
243
244 let encoded = encode_cpop(&packet).unwrap();
245 let result: Result<TestPacket> = decode_cwar(&encoded);
246
247 assert!(matches!(
248 result,
249 Err(CodecError::InvalidTag {
250 expected: CBOR_TAG_CWAR,
251 actual: CBOR_TAG_CPOP
252 })
253 ));
254 }
255
256 #[test]
257 fn test_has_tag_on_untagged_data() {
258 let packet = TestPacket {
259 version: 1,
260 data: vec![10, 20],
261 };
262 let encoded = encode(&packet).unwrap();
263 assert!(!has_tag(&encoded, CBOR_TAG_CPOP));
264 assert!(!has_tag(&encoded, CBOR_TAG_CWAR));
265 }
266
267 #[test]
268 fn test_has_tag_on_invalid_cbor() {
269 assert!(!has_tag(&[0xFF, 0xFE, 0xFD], CBOR_TAG_CPOP));
271 assert!(!has_tag(&[], CBOR_TAG_CPOP));
272 }
273
274 #[test]
275 fn test_extract_tag_on_invalid_cbor() {
276 assert_eq!(extract_tag(&[0xFF, 0xFE, 0xFD]), None);
277 assert_eq!(extract_tag(&[]), None);
278 }
279
280 #[test]
281 fn test_decode_tagged_on_untagged_data_returns_missing_tag() {
282 let packet = TestPacket {
283 version: 1,
284 data: vec![],
285 };
286 let encoded = encode(&packet).unwrap();
287 let result: Result<TestPacket> = decode_tagged(&encoded, CBOR_TAG_CPOP);
288 assert!(matches!(result, Err(CodecError::MissingTag)));
289 }
290
291 #[test]
292 fn test_decode_invalid_cbor_returns_error() {
293 let garbage = &[0xFF, 0xFE, 0xFD, 0xFC];
294 let result: Result<TestPacket> = decode(garbage);
295 assert!(matches!(result, Err(CodecError::CborDecode(_))));
296 }
297
298 #[test]
299 fn test_compact_ref_roundtrip() {
300 let packet = TestPacket {
301 version: 3,
302 data: vec![99, 100],
303 };
304
305 let encoded = encode_compact_ref(&packet).unwrap();
306 assert!(has_tag(&encoded, CBOR_TAG_COMPACT_REF));
307 assert_eq!(extract_tag(&encoded), Some(CBOR_TAG_COMPACT_REF));
308
309 let decoded: TestPacket = decode_compact_ref(&encoded).unwrap();
310 assert_eq!(packet, decoded);
311 }
312
313 #[test]
314 fn test_cwar_roundtrip() {
315 let packet = TestPacket {
316 version: 2,
317 data: vec![7, 8, 9],
318 };
319
320 let encoded = encode_cwar(&packet).unwrap();
321 assert!(has_tag(&encoded, CBOR_TAG_CWAR));
322
323 let decoded: TestPacket = decode_cwar(&encoded).unwrap();
324 assert_eq!(packet, decoded);
325 }
326
327 #[test]
328 fn test_encode_to_decode_from_cbor() {
329 let packet = TestPacket {
330 version: 5,
331 data: vec![1, 2, 3],
332 };
333
334 let mut buf = Vec::new();
335 encode_to(&packet, &mut buf).unwrap();
336 let decoded: TestPacket = decode_from(&buf[..]).unwrap();
337 assert_eq!(packet, decoded);
338 }
339}