1use std::fmt;
13
14use serde_json::{Map, Number, Value};
15
16pub const DEFAULT_MAX_CBOR_BYTE_LENGTH: usize = 16 * 1024 * 1024;
18pub const DEFAULT_MAX_CBOR_CONTAINER_LENGTH: usize = 1_000_000;
20pub const DEFAULT_MAX_CBOR_DEPTH: usize = 64;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct CborError(pub String);
26
27impl fmt::Display for CborError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.write_str(&self.0)
30 }
31}
32
33impl std::error::Error for CborError {}
34
35#[derive(Debug, Clone, Copy)]
37pub struct CborOptions {
38 pub max_byte_length: usize,
39 pub max_container_length: usize,
40 pub max_depth: usize,
41}
42
43impl Default for CborOptions {
44 fn default() -> Self {
45 Self {
46 max_byte_length: DEFAULT_MAX_CBOR_BYTE_LENGTH,
47 max_container_length: DEFAULT_MAX_CBOR_CONTAINER_LENGTH,
48 max_depth: DEFAULT_MAX_CBOR_DEPTH,
49 }
50 }
51}
52
53fn write_head(out: &mut Vec<u8>, major: u8, value: u64) {
54 let m = major << 5;
55 if value < 24 {
56 out.push(m | value as u8);
57 } else if value <= u8::MAX as u64 {
58 out.push(m | 24);
59 out.push(value as u8);
60 } else if value <= u16::MAX as u64 {
61 out.push(m | 25);
62 out.extend_from_slice(&(value as u16).to_be_bytes());
63 } else if value <= u32::MAX as u64 {
64 out.push(m | 26);
65 out.extend_from_slice(&(value as u32).to_be_bytes());
66 } else {
67 out.push(m | 27);
68 out.extend_from_slice(&value.to_be_bytes());
69 }
70}
71
72fn encode_value(
73 value: &Value,
74 out: &mut Vec<u8>,
75 options: &CborOptions,
76 depth: usize,
77) -> Result<(), CborError> {
78 if depth > options.max_depth {
79 return Err(CborError("CBOR nesting depth exceeded".into()));
80 }
81 match value {
82 Value::Null => out.push(0xf6),
83 Value::Bool(false) => out.push(0xf4),
84 Value::Bool(true) => out.push(0xf5),
85 Value::Number(n) => {
86 if let Some(u) = n.as_u64() {
87 write_head(out, 0, u);
88 } else if let Some(i) = n.as_i64() {
89 write_head(out, 1, (-(i as i128) - 1) as u64);
91 } else if let Some(f) = n.as_f64() {
92 out.push(0xfb);
93 out.extend_from_slice(&f.to_be_bytes());
94 } else {
95 return Err(CborError("unsupported number".into()));
96 }
97 }
98 Value::String(s) => {
99 let bytes = s.as_bytes();
100 if bytes.len() > options.max_byte_length {
101 return Err(CborError("CBOR string exceeds max byte length".into()));
102 }
103 write_head(out, 3, bytes.len() as u64);
104 out.extend_from_slice(bytes);
105 }
106 Value::Array(items) => {
107 if items.len() > options.max_container_length {
108 return Err(CborError("CBOR array exceeds max container length".into()));
109 }
110 write_head(out, 4, items.len() as u64);
111 for item in items {
112 encode_value(item, out, options, depth + 1)?;
113 }
114 }
115 Value::Object(map) => {
116 if map.len() > options.max_container_length {
117 return Err(CborError("CBOR map exceeds max container length".into()));
118 }
119 write_head(out, 5, map.len() as u64);
120 for (key, item) in map {
121 if key.len() > options.max_byte_length {
122 return Err(CborError("CBOR string exceeds max byte length".into()));
123 }
124 write_head(out, 3, key.len() as u64);
125 out.extend_from_slice(key.as_bytes());
126 encode_value(item, out, options, depth + 1)?;
127 }
128 }
129 }
130 Ok(())
131}
132
133pub fn encode(value: &Value, options: &CborOptions) -> Result<Vec<u8>, CborError> {
135 let mut out = Vec::new();
136 encode_value(value, &mut out, options, 0)?;
137 if out.len() > options.max_byte_length {
138 return Err(CborError("CBOR payload exceeds max byte length".into()));
139 }
140 Ok(out)
141}
142
143struct Reader<'a> {
144 bytes: &'a [u8],
145 pos: usize,
146}
147
148impl<'a> Reader<'a> {
149 fn take(&mut self, n: usize) -> Result<&'a [u8], CborError> {
150 if self.pos + n > self.bytes.len() {
151 return Err(CborError("unexpected end of CBOR input".into()));
152 }
153 let slice = &self.bytes[self.pos..self.pos + n];
154 self.pos += n;
155 Ok(slice)
156 }
157
158 fn u8(&mut self) -> Result<u8, CborError> {
159 Ok(self.take(1)?[0])
160 }
161
162 fn read_len(&mut self, additional: u8) -> Result<Option<u64>, CborError> {
163 match additional {
164 0..=23 => Ok(Some(additional as u64)),
165 24 => Ok(Some(self.u8()? as u64)),
166 25 => {
167 let b = self.take(2)?;
168 Ok(Some(u16::from_be_bytes([b[0], b[1]]) as u64))
169 }
170 26 => {
171 let b = self.take(4)?;
172 Ok(Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]) as u64))
173 }
174 27 => {
175 let b = self.take(8)?;
176 Ok(Some(u64::from_be_bytes([
177 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
178 ])))
179 }
180 _ => Ok(None),
181 }
182 }
183}
184
185fn decode_value(
186 reader: &mut Reader<'_>,
187 options: &CborOptions,
188 depth: usize,
189) -> Result<Value, CborError> {
190 if depth > options.max_depth {
191 return Err(CborError("CBOR nesting depth exceeded".into()));
192 }
193 let initial = reader.u8()?;
194 let major = initial >> 5;
195 let additional = initial & 0x1f;
196 match major {
197 0 => {
198 let n = reader
199 .read_len(additional)?
200 .ok_or_else(|| CborError("invalid CBOR integer".into()))?;
201 Ok(Value::Number(Number::from(n)))
202 }
203 1 => {
204 let n = reader
205 .read_len(additional)?
206 .ok_or_else(|| CborError("invalid CBOR integer".into()))?;
207 if n > i64::MAX as u64 {
208 return Err(CborError("CBOR negative integer out of range".into()));
209 }
210 Ok(Value::Number(Number::from(-(n as i64) - 1)))
211 }
212 2 => {
213 let len = reader
216 .read_len(additional)?
217 .ok_or_else(|| CborError("invalid CBOR byte string".into()))?
218 as usize;
219 if len > options.max_byte_length {
220 return Err(CborError("CBOR string exceeds max byte length".into()));
221 }
222 let bytes = reader.take(len)?;
223 Ok(Value::Array(
224 bytes
225 .iter()
226 .map(|b| Value::Number(Number::from(*b)))
227 .collect(),
228 ))
229 }
230 3 => {
231 let len = reader
232 .read_len(additional)?
233 .ok_or_else(|| CborError("invalid CBOR text string".into()))?
234 as usize;
235 if len > options.max_byte_length {
236 return Err(CborError("CBOR string exceeds max byte length".into()));
237 }
238 let bytes = reader.take(len)?;
239 let text = std::str::from_utf8(bytes)
240 .map_err(|_| CborError("invalid UTF-8 in CBOR text string".into()))?;
241 Ok(Value::String(text.to_string()))
242 }
243 4 => {
244 let len = reader
245 .read_len(additional)?
246 .ok_or_else(|| CborError("invalid CBOR array".into()))?
247 as usize;
248 if len > options.max_container_length {
249 return Err(CborError("CBOR array exceeds max container length".into()));
250 }
251 let mut items = Vec::with_capacity(len.min(4096));
252 for _ in 0..len {
253 items.push(decode_value(reader, options, depth + 1)?);
254 }
255 Ok(Value::Array(items))
256 }
257 5 => {
258 let len = reader
259 .read_len(additional)?
260 .ok_or_else(|| CborError("invalid CBOR map".into()))?
261 as usize;
262 if len > options.max_container_length {
263 return Err(CborError("CBOR map exceeds max container length".into()));
264 }
265 let mut map = Map::new();
266 for _ in 0..len {
267 let key = decode_value(reader, options, depth + 1)?;
268 let Value::String(key) = key else {
269 return Err(CborError("CBOR map key must be a text string".into()));
270 };
271 let value = decode_value(reader, options, depth + 1)?;
272 map.insert(key, value);
273 }
274 Ok(Value::Object(map))
275 }
276 6 => {
277 let _ = reader.read_len(additional)?;
279 decode_value(reader, options, depth)
280 }
281 7 => match additional {
282 20 => Ok(Value::Bool(false)),
283 21 => Ok(Value::Bool(true)),
284 22 => Ok(Value::Null),
285 26 => {
286 let b = reader.take(4)?;
287 let f = f32::from_be_bytes([b[0], b[1], b[2], b[3]]) as f64;
288 Ok(Number::from_f64(f)
289 .map(Value::Number)
290 .unwrap_or(Value::Null))
291 }
292 27 => {
293 let b = reader.take(8)?;
294 let f = f64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
295 Ok(Number::from_f64(f)
296 .map(Value::Number)
297 .unwrap_or(Value::Null))
298 }
299 _ => Err(CborError("unsupported CBOR simple value".into())),
300 },
301 _ => Err(CborError("unsupported CBOR major type".into())),
302 }
303}
304
305pub fn decode(bytes: &[u8], options: &CborOptions) -> Result<Value, CborError> {
307 if bytes.len() > options.max_byte_length {
308 return Err(CborError("CBOR payload exceeds max byte length".into()));
309 }
310 let mut reader = Reader { bytes, pos: 0 };
311 let value = decode_value(&mut reader, options, 0)?;
312 if reader.pos != bytes.len() {
313 return Err(CborError("trailing bytes after CBOR value".into()));
314 }
315 Ok(value)
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use serde_json::json;
322
323 fn round_trip(value: Value) {
324 let options = CborOptions::default();
325 let bytes = encode(&value, &options).unwrap();
326 let decoded = decode(&bytes, &options).unwrap();
327 assert_eq!(decoded, value);
328 }
329
330 #[test]
331 fn round_trips_scalars() {
332 round_trip(json!(null));
333 round_trip(json!(true));
334 round_trip(json!(false));
335 round_trip(json!(0));
336 round_trip(json!(23));
337 round_trip(json!(24));
338 round_trip(json!(300));
339 round_trip(json!(70000));
340 round_trip(json!(5_000_000_000i64));
341 round_trip(json!(-1));
342 round_trip(json!(-300));
343 round_trip(json!("hello"));
344 round_trip(json!(1.5));
345 }
346
347 #[test]
348 fn round_trips_containers() {
349 round_trip(json!([1, "two", [3, 4], { "a": true }]));
350 round_trip(json!({ "type": "hello", "nested": { "x": [1, 2, 3] } }));
351 }
352
353 #[test]
354 fn rejects_deep_nesting() {
355 let options = CborOptions {
356 max_depth: 3,
357 ..Default::default()
358 };
359 let deep = json!([[[[1]]]]);
360 assert!(encode(&deep, &options).is_err());
361 }
362
363 #[test]
364 fn rejects_trailing_bytes() {
365 let options = CborOptions::default();
366 let mut bytes = encode(&json!(1), &options).unwrap();
367 bytes.push(0);
368 assert!(decode(&bytes, &options).is_err());
369 }
370
371 #[test]
372 fn rejects_truncated() {
373 let options = CborOptions::default();
374 let bytes = encode(&json!("abcdef"), &options).unwrap();
375 assert!(decode(&bytes[..3], &options).is_err());
376 }
377}