1pub mod cbor;
9pub mod json;
10
11use serde::{de::DeserializeOwned, Serialize};
12use std::io::{Read, Write};
13
14pub const CBOR_TAG_CPOP: u64 = 1129336656;
18
19pub const CBOR_TAG_CWAR: u64 = 1129791826;
23
24pub const CBOR_TAG_COMPACT_REF: u64 = 1129336657;
27
28pub const IANA_PEN: u32 = 65074;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub enum Format {
34 #[default]
36 Cbor,
37 CborWar,
39 Json,
41}
42
43impl Format {
44 pub fn mime_type(&self) -> &'static str {
46 match self {
47 Format::Cbor => "application/cpop+cbor",
48 Format::CborWar => "application/cwar+cbor",
49 Format::Json => "application/json",
50 }
51 }
52
53 pub fn extension(&self) -> &'static str {
55 match self {
56 Format::Cbor => "cpop",
57 Format::CborWar => "cwar",
58 Format::Json => "json",
59 }
60 }
61
62 pub fn detect(data: &[u8]) -> Option<Self> {
64 if data.is_empty() {
65 return None;
66 }
67 match data[0] {
70 0x7B | 0x5B => Some(Format::Json),
71 0xA0..=0xBF | 0xD9 | 0xDA | 0xDB => Some(Format::Cbor),
72 _ => None,
73 }
74 }
75}
76
77#[derive(Debug, thiserror::Error)]
79pub enum CodecError {
80 #[error("CBOR encoding error: {0}")]
81 CborEncode(String),
82 #[error("CBOR decoding error: {0}")]
83 CborDecode(String),
84 #[error("JSON encoding error: {0}")]
85 JsonEncode(String),
86 #[error("JSON decoding error: {0}")]
87 JsonDecode(String),
88 #[error("io error: {0}")]
89 Io(#[from] std::io::Error),
90 #[error("invalid format: {0}")]
91 InvalidFormat(String),
92 #[error("missing semantic tag")]
93 MissingTag,
94 #[error("invalid semantic tag: expected {expected}, got {actual}")]
95 InvalidTag { expected: u64, actual: u64 },
96 #[error("validation error: {0}")]
97 Validation(String),
98}
99
100pub type Result<T> = std::result::Result<T, CodecError>;
101
102pub fn encode<T: Serialize>(value: &T, format: Format) -> Result<Vec<u8>> {
104 match format {
105 Format::Cbor | Format::CborWar => cbor::encode(value),
106 Format::Json => json::encode(value),
107 }
108}
109
110pub fn decode<T: DeserializeOwned>(data: &[u8], format: Format) -> Result<T> {
112 match format {
113 Format::Cbor | Format::CborWar => cbor::decode(data),
114 Format::Json => json::decode(data),
115 }
116}
117
118pub fn decode_auto<T: DeserializeOwned>(data: &[u8]) -> Result<T> {
120 let format = Format::detect(data)
121 .ok_or_else(|| CodecError::InvalidFormat("unable to detect format".to_string()))?;
122 decode(data, format)
123}
124
125pub fn encode_to<T: Serialize, W: Write>(value: &T, writer: W, format: Format) -> Result<()> {
127 match format {
128 Format::Cbor | Format::CborWar => cbor::encode_to(value, writer),
129 Format::Json => json::encode_to(value, writer),
130 }
131}
132
133pub fn decode_from<T: DeserializeOwned, R: Read>(reader: R, format: Format) -> Result<T> {
135 match format {
136 Format::Cbor | Format::CborWar => cbor::decode_from(reader),
137 Format::Json => json::decode_from(reader),
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use serde::{Deserialize, Serialize};
145
146 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
147 struct TestStruct {
148 name: String,
149 value: i32,
150 data: Vec<u8>,
151 }
152
153 #[test]
154 fn test_format_detection() {
155 assert_eq!(Format::detect(b"{\"key\":\"value\"}"), Some(Format::Json));
157 assert_eq!(Format::detect(b"[1,2,3]"), Some(Format::Json));
159 assert_eq!(Format::detect(&[0xA1, 0x01, 0x02]), Some(Format::Cbor));
161 assert_eq!(Format::detect(&[]), None);
163 }
164
165 #[test]
166 fn test_roundtrip_cbor() {
167 let original = TestStruct {
168 name: "test".to_string(),
169 value: 42,
170 data: vec![1, 2, 3, 4],
171 };
172
173 let encoded = encode(&original, Format::Cbor).unwrap();
174 let decoded: TestStruct = decode(&encoded, Format::Cbor).unwrap();
175
176 assert_eq!(original, decoded);
177 }
178
179 #[test]
180 fn test_roundtrip_json() {
181 let original = TestStruct {
182 name: "test".to_string(),
183 value: 42,
184 data: vec![1, 2, 3, 4],
185 };
186
187 let encoded = encode(&original, Format::Json).unwrap();
188 let decoded: TestStruct = decode(&encoded, Format::Json).unwrap();
189
190 assert_eq!(original, decoded);
191 }
192
193 #[test]
194 fn test_auto_detect_decode() {
195 let original = TestStruct {
196 name: "auto".to_string(),
197 value: 100,
198 data: vec![5, 6, 7],
199 };
200
201 let cbor_encoded = encode(&original, Format::Cbor).unwrap();
202 let cbor_decoded: TestStruct = decode_auto(&cbor_encoded).unwrap();
203 assert_eq!(original, cbor_decoded);
204
205 let json_encoded = encode(&original, Format::Json).unwrap();
206 let json_decoded: TestStruct = decode_auto(&json_encoded).unwrap();
207 assert_eq!(original, json_decoded);
208 }
209
210 #[test]
211 fn test_format_mime_type() {
212 assert_eq!(Format::Cbor.mime_type(), "application/cpop+cbor");
213 assert_eq!(Format::CborWar.mime_type(), "application/cwar+cbor");
214 assert_eq!(Format::Json.mime_type(), "application/json");
215 }
216
217 #[test]
218 fn test_format_extension() {
219 assert_eq!(Format::Cbor.extension(), "cpop");
220 assert_eq!(Format::CborWar.extension(), "cwar");
221 assert_eq!(Format::Json.extension(), "json");
222 }
223
224 #[test]
225 fn test_format_default_is_cbor() {
226 assert_eq!(Format::default(), Format::Cbor);
227 }
228
229 #[test]
230 fn test_format_detect_tagged_cbor() {
231 assert_eq!(Format::detect(&[0xD9, 0x01, 0x02]), Some(Format::Cbor));
233 assert_eq!(
235 Format::detect(&[0xDA, 0x00, 0x00, 0x00, 0x01]),
236 Some(Format::Cbor)
237 );
238 assert_eq!(Format::detect(&[0xDB]), Some(Format::Cbor));
240 }
241
242 #[test]
243 fn test_format_detect_unknown_byte() {
244 assert_eq!(Format::detect(&[0x00]), None);
246 assert_eq!(Format::detect(&[0x42]), None);
247 assert_eq!(Format::detect(&[0xFF]), None);
248 }
249
250 #[test]
251 fn test_decode_auto_empty_data() {
252 let result = decode_auto::<TestStruct>(&[]);
253 assert!(matches!(result, Err(CodecError::InvalidFormat(_))));
254 }
255
256 #[test]
257 fn test_encode_to_decode_from_cbor() {
258 let original = TestStruct {
259 name: "writer".to_string(),
260 value: -7,
261 data: vec![0xFF, 0x00],
262 };
263
264 let mut buf = Vec::new();
265 encode_to(&original, &mut buf, Format::Cbor).unwrap();
266 let decoded: TestStruct = decode_from(&buf[..], Format::Cbor).unwrap();
267 assert_eq!(original, decoded);
268 }
269
270 #[test]
271 fn test_encode_to_decode_from_json() {
272 let original = TestStruct {
273 name: "writer".to_string(),
274 value: -7,
275 data: vec![0xFF, 0x00],
276 };
277
278 let mut buf = Vec::new();
279 encode_to(&original, &mut buf, Format::Json).unwrap();
280 let decoded: TestStruct = decode_from(&buf[..], Format::Json).unwrap();
281 assert_eq!(original, decoded);
282 }
283}
284
285pub const CBOR_TAG_EVIDENCE_PACKET: u64 = CBOR_TAG_CPOP;
288pub const CBOR_TAG_ATTESTATION_RESULT: u64 = CBOR_TAG_CWAR;
290
291pub fn encode_evidence(packet: &crate::rfc::EvidencePacket) -> crate::error::Result<Vec<u8>> {
293 cbor::encode_cpop(packet).map_err(|e| crate::error::Error::Serialization(e.to_string()))
294}
295
296pub fn decode_evidence(bytes: &[u8]) -> crate::error::Result<crate::rfc::EvidencePacket> {
298 cbor::decode_cpop(bytes).map_err(|e| crate::error::Error::Serialization(e.to_string()))
299}
300
301pub fn encode_attestation(result: &crate::rfc::AttestationResult) -> crate::error::Result<Vec<u8>> {
303 cbor::encode_cwar(result).map_err(|e| crate::error::Error::Serialization(e.to_string()))
304}
305
306pub fn decode_attestation(bytes: &[u8]) -> crate::error::Result<crate::rfc::AttestationResult> {
308 cbor::decode_cwar(bytes).map_err(|e| crate::error::Error::Serialization(e.to_string()))
309}