1use std::fmt;
21
22const MAX_DEPTH: usize = 128;
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum Value {
29 Integer(i128),
32 Bytes(Vec<u8>),
33 Text(String),
34 Array(Vec<Value>),
35 Map(Vec<(Value, Value)>),
37 Tag(u64, Box<Value>),
38 Bool(bool),
39 Null,
40 Float(f64),
42}
43
44impl Value {
45 pub fn map_get(&self, key: &str) -> Option<&Value> {
47 match self {
48 Value::Map(entries) => entries.iter().find_map(|(k, v)| match k {
49 Value::Text(t) if t == key => Some(v),
50 _ => None,
51 }),
52 _ => None,
53 }
54 }
55
56 pub fn map_get_int(&self, key: i128) -> Option<&Value> {
58 match self {
59 Value::Map(entries) => entries.iter().find_map(|(k, v)| match k {
60 Value::Integer(i) if *i == key => Some(v),
61 _ => None,
62 }),
63 _ => None,
64 }
65 }
66
67 pub fn as_bytes(&self) -> Option<&[u8]> {
68 match self {
69 Value::Bytes(b) => Some(b),
70 _ => None,
71 }
72 }
73
74 pub fn as_text(&self) -> Option<&str> {
75 match self {
76 Value::Text(t) => Some(t),
77 _ => None,
78 }
79 }
80
81 pub fn as_integer(&self) -> Option<i128> {
82 match self {
83 Value::Integer(i) => Some(*i),
84 _ => None,
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub enum CborError {
91 Truncated,
93 Invalid(&'static str),
95 TooDeep,
97 Unrepresentable(&'static str),
99}
100
101impl fmt::Display for CborError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 CborError::Truncated => write!(f, "truncated CBOR input"),
105 CborError::Invalid(what) => write!(f, "invalid CBOR: {what}"),
106 CborError::TooDeep => write!(f, "CBOR nesting exceeds depth limit"),
107 CborError::Unrepresentable(what) => write!(f, "unrepresentable in CBOR: {what}"),
108 }
109 }
110}
111
112impl std::error::Error for CborError {}
113
114fn put_header(out: &mut Vec<u8>, major: u8, arg: u64) {
119 let mt = major << 5;
120 if arg < 24 {
121 out.push(mt | arg as u8);
122 } else if arg <= u8::MAX as u64 {
123 out.push(mt | 24);
124 out.push(arg as u8);
125 } else if arg <= u16::MAX as u64 {
126 out.push(mt | 25);
127 out.extend_from_slice(&(arg as u16).to_be_bytes());
128 } else if arg <= u32::MAX as u64 {
129 out.push(mt | 26);
130 out.extend_from_slice(&(arg as u32).to_be_bytes());
131 } else {
132 out.push(mt | 27);
133 out.extend_from_slice(&arg.to_be_bytes());
134 }
135}
136
137fn encode_into(value: &Value, out: &mut Vec<u8>) -> Result<(), CborError> {
138 match value {
139 Value::Integer(i) => {
140 if *i >= 0 {
141 let u = u64::try_from(*i)
142 .map_err(|_| CborError::Unrepresentable("integer above u64::MAX"))?;
143 put_header(out, 0, u);
144 } else {
145 let magnitude = i
146 .checked_neg()
147 .and_then(|m| m.checked_sub(1))
148 .and_then(|m| u64::try_from(m).ok())
149 .ok_or(CborError::Unrepresentable("integer below -2^64"))?;
150 put_header(out, 1, magnitude);
151 }
152 }
153 Value::Bytes(b) => {
154 put_header(out, 2, b.len() as u64);
155 out.extend_from_slice(b);
156 }
157 Value::Text(t) => {
158 put_header(out, 3, t.len() as u64);
159 out.extend_from_slice(t.as_bytes());
160 }
161 Value::Array(items) => {
162 put_header(out, 4, items.len() as u64);
163 for item in items {
164 encode_into(item, out)?;
165 }
166 }
167 Value::Map(entries) => {
168 put_header(out, 5, entries.len() as u64);
169 for (k, v) in entries {
170 encode_into(k, out)?;
171 encode_into(v, out)?;
172 }
173 }
174 Value::Tag(tag, inner) => {
175 put_header(out, 6, *tag);
176 encode_into(inner, out)?;
177 }
178 Value::Bool(b) => out.push(if *b { 0xf5 } else { 0xf4 }),
179 Value::Null => out.push(0xf6),
180 Value::Float(f) => {
181 out.push(0xfb);
184 out.extend_from_slice(&f.to_be_bytes());
185 }
186 }
187 Ok(())
188}
189
190pub fn encode(value: &Value) -> Result<Vec<u8>, CborError> {
191 let mut out = Vec::new();
192 encode_into(value, &mut out)?;
193 Ok(out)
194}
195
196struct Decoder<'a> {
201 buf: &'a [u8],
202 pos: usize,
203}
204
205impl<'a> Decoder<'a> {
206 fn byte(&mut self) -> Result<u8, CborError> {
207 let b = *self.buf.get(self.pos).ok_or(CborError::Truncated)?;
208 self.pos += 1;
209 Ok(b)
210 }
211
212 fn take(&mut self, n: usize) -> Result<&'a [u8], CborError> {
213 let end = self.pos.checked_add(n).ok_or(CborError::Truncated)?;
216 if end > self.buf.len() {
217 return Err(CborError::Truncated);
218 }
219 let s = &self.buf[self.pos..end];
220 self.pos = end;
221 Ok(s)
222 }
223
224 fn arg(&mut self, info: u8) -> Result<u64, CborError> {
225 match info {
226 0..=23 => Ok(info as u64),
227 24 => Ok(self.byte()? as u64),
228 25 => Ok(u16::from_be_bytes(self.take(2)?.try_into().unwrap()) as u64),
229 26 => Ok(u32::from_be_bytes(self.take(4)?.try_into().unwrap()) as u64),
230 27 => Ok(u64::from_be_bytes(self.take(8)?.try_into().unwrap())),
231 _ => Err(CborError::Invalid("reserved additional-info value")),
232 }
233 }
234
235 fn arg_as_len(&self, arg: u64) -> Result<usize, CborError> {
239 let remaining = self.buf.len() - self.pos;
240 if arg > remaining as u64 {
241 return Err(CborError::Truncated);
242 }
243 Ok(arg as usize)
244 }
245
246 fn value(&mut self, depth: usize) -> Result<Value, CborError> {
247 if depth > MAX_DEPTH {
248 return Err(CborError::TooDeep);
249 }
250 let initial = self.byte()?;
251 let major = initial >> 5;
252 let info = initial & 0x1f;
253 match major {
254 0 => Ok(Value::Integer(self.arg(info)? as i128)),
255 1 => Ok(Value::Integer(-1 - self.arg(info)? as i128)),
256 2 => {
257 if info == 31 {
258 self.indefinite_string(depth, 2).map(Value::Bytes)
259 } else {
260 let arg = self.arg(info)?;
261 let len = self.arg_as_len(arg)?;
262 Ok(Value::Bytes(self.take(len)?.to_vec()))
263 }
264 }
265 3 => {
266 let bytes = if info == 31 {
267 self.indefinite_string(depth, 3)?
268 } else {
269 let arg = self.arg(info)?;
270 let len = self.arg_as_len(arg)?;
271 self.take(len)?.to_vec()
272 };
273 String::from_utf8(bytes)
274 .map(Value::Text)
275 .map_err(|_| CborError::Invalid("text string is not UTF-8"))
276 }
277 4 => {
278 if info == 31 {
279 let mut items = Vec::new();
280 while !self.at_break()? {
281 items.push(self.value(depth + 1)?);
282 }
283 Ok(Value::Array(items))
284 } else {
285 let arg = self.arg(info)?;
288 let len = self.arg_as_len(arg)?;
289 let mut items = Vec::with_capacity(len);
290 for _ in 0..len {
291 items.push(self.value(depth + 1)?);
292 }
293 Ok(Value::Array(items))
294 }
295 }
296 5 => {
297 if info == 31 {
298 let mut entries = Vec::new();
299 while !self.at_break()? {
300 let k = self.value(depth + 1)?;
301 let v = self.value(depth + 1)?;
302 entries.push((k, v));
303 }
304 Ok(Value::Map(entries))
305 } else {
306 let count = self.arg(info)?;
307 if count > (self.buf.len() - self.pos) as u64 / 2 {
309 return Err(CborError::Truncated);
310 }
311 let count = count as usize;
312 let mut entries = Vec::with_capacity(count);
313 for _ in 0..count {
314 let k = self.value(depth + 1)?;
315 let v = self.value(depth + 1)?;
316 entries.push((k, v));
317 }
318 Ok(Value::Map(entries))
319 }
320 }
321 6 => {
322 let tag = self.arg(info)?;
323 Ok(Value::Tag(tag, Box::new(self.value(depth + 1)?)))
324 }
325 7 => match info {
326 20 => Ok(Value::Bool(false)),
327 21 => Ok(Value::Bool(true)),
328 22 => Ok(Value::Null),
329 23 => Ok(Value::Null), 24 => {
331 let b = self.byte()?;
332 if b < 32 {
333 return Err(CborError::Invalid("non-minimal simple value"));
334 }
335 Ok(Value::Integer(b as i128)) }
337 25 => {
338 let raw = u16::from_be_bytes(self.take(2)?.try_into().unwrap());
339 Ok(Value::Float(half_to_f64(raw)))
340 }
341 26 => {
342 let raw = u32::from_be_bytes(self.take(4)?.try_into().unwrap());
343 Ok(Value::Float(f32::from_bits(raw) as f64))
344 }
345 27 => {
346 let raw = u64::from_be_bytes(self.take(8)?.try_into().unwrap());
347 Ok(Value::Float(f64::from_bits(raw)))
348 }
349 31 => Err(CborError::Invalid("unexpected break")),
350 _ => Err(CborError::Invalid("reserved simple value")),
351 },
352 _ => unreachable!("major type is 3 bits"),
353 }
354 }
355
356 fn indefinite_string(&mut self, depth: usize, major: u8) -> Result<Vec<u8>, CborError> {
359 if depth + 1 > MAX_DEPTH {
360 return Err(CborError::TooDeep);
361 }
362 let mut out = Vec::new();
363 loop {
364 let initial = self.byte()?;
365 if initial == 0xff {
366 return Ok(out);
367 }
368 if initial >> 5 != major || initial & 0x1f == 31 {
369 return Err(CborError::Invalid("bad chunk in indefinite string"));
370 }
371 let arg = self.arg(initial & 0x1f)?;
372 let len = self.arg_as_len(arg)?;
373 out.extend_from_slice(self.take(len)?);
374 }
375 }
376
377 fn at_break(&mut self) -> Result<bool, CborError> {
378 if *self.buf.get(self.pos).ok_or(CborError::Truncated)? == 0xff {
379 self.pos += 1;
380 Ok(true)
381 } else {
382 Ok(false)
383 }
384 }
385}
386
387fn half_to_f64(raw: u16) -> f64 {
388 let exp = (raw >> 10) & 0x1f;
390 let mant = (raw & 0x3ff) as f64;
391 let magnitude = match exp {
392 0 => mant * 2f64.powi(-24),
393 31 => {
394 if mant == 0.0 {
395 f64::INFINITY
396 } else {
397 f64::NAN
398 }
399 }
400 _ => (mant + 1024.0) * 2f64.powi(exp as i32 - 25),
401 };
402 if raw & 0x8000 != 0 {
403 -magnitude
404 } else {
405 magnitude
406 }
407}
408
409pub fn decode(bytes: &[u8]) -> Result<Value, CborError> {
412 let mut d = Decoder { buf: bytes, pos: 0 };
413 d.value(0)
414}
415
416pub fn from_json(v: &serde_json::Value) -> Result<Value, CborError> {
423 use serde_json::Value as J;
424 Ok(match v {
425 J::Null => Value::Null,
426 J::Bool(b) => Value::Bool(*b),
427 J::Number(n) => {
428 if let Some(i) = n.as_i64() {
429 Value::Integer(i as i128)
430 } else if let Some(u) = n.as_u64() {
431 Value::Integer(u as i128)
432 } else {
433 Value::Float(n.as_f64().ok_or(CborError::Unrepresentable("number"))?)
434 }
435 }
436 J::String(s) => Value::Text(s.clone()),
437 J::Array(items) => Value::Array(
438 items
439 .iter()
440 .map(from_json)
441 .collect::<Result<Vec<_>, _>>()?,
442 ),
443 J::Object(map) => Value::Map(
444 map.iter()
445 .map(|(k, val)| Ok((Value::Text(k.clone()), from_json(val)?)))
446 .collect::<Result<Vec<_>, CborError>>()?,
447 ),
448 })
449}
450
451pub fn to_json(v: &Value) -> Result<serde_json::Value, CborError> {
455 use serde_json::Value as J;
456 Ok(match v {
457 Value::Null => J::Null,
458 Value::Bool(b) => J::Bool(*b),
459 Value::Integer(i) => {
460 if let Ok(n) = i64::try_from(*i) {
461 J::Number(n.into())
462 } else if let Ok(n) = u64::try_from(*i) {
463 J::Number(n.into())
464 } else {
465 return Err(CborError::Unrepresentable("integer outside JSON range"));
466 }
467 }
468 Value::Float(f) => serde_json::Number::from_f64(*f)
469 .map(J::Number)
470 .ok_or(CborError::Unrepresentable("non-finite float"))?,
471 Value::Text(t) => J::String(t.clone()),
472 Value::Array(items) => J::Array(
473 items
474 .iter()
475 .map(to_json)
476 .collect::<Result<Vec<_>, _>>()?,
477 ),
478 Value::Map(entries) => {
479 let mut out = serde_json::Map::with_capacity(entries.len());
480 for (k, val) in entries {
481 let Value::Text(key) = k else {
482 return Err(CborError::Unrepresentable("non-text map key in JSON"));
483 };
484 out.insert(key.clone(), to_json(val)?);
485 }
486 J::Object(out)
487 }
488 Value::Tag(..) => return Err(CborError::Unrepresentable("tag in JSON")),
489 Value::Bytes(_) => return Err(CborError::Unrepresentable("byte string in JSON")),
490 })
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 fn hex(bytes: &[u8]) -> String {
498 bytes.iter().map(|b| format!("{b:02x}")).collect()
499 }
500
501 fn unhex(s: &str) -> Vec<u8> {
502 (0..s.len())
503 .step_by(2)
504 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
505 .collect()
506 }
507
508 #[test]
509 fn rfc8949_appendix_a_encodings() {
510 let cases: Vec<(Value, &str)> = vec![
512 (Value::Integer(0), "00"),
513 (Value::Integer(10), "0a"),
514 (Value::Integer(23), "17"),
515 (Value::Integer(24), "1818"),
516 (Value::Integer(100), "1864"),
517 (Value::Integer(1000), "1903e8"),
518 (Value::Integer(1000000), "1a000f4240"),
519 (Value::Integer(1000000000000), "1b000000e8d4a51000"),
520 (Value::Integer(u64::MAX as i128), "1bffffffffffffffff"),
521 (Value::Integer(-1), "20"),
522 (Value::Integer(-10), "29"),
523 (Value::Integer(-100), "3863"),
524 (Value::Integer(-1000), "3903e7"),
525 (Value::Integer(-(u64::MAX as i128) - 1), "3bffffffffffffffff"),
526 (Value::Bool(false), "f4"),
527 (Value::Bool(true), "f5"),
528 (Value::Null, "f6"),
529 (Value::Float(1.1), "fb3ff199999999999a"),
530 (Value::Float(-4.1), "fbc010666666666666"),
531 (Value::Bytes(vec![]), "40"),
532 (Value::Bytes(vec![1, 2, 3, 4]), "4401020304"),
533 (Value::Text(String::new()), "60"),
534 (Value::Text("IETF".into()), "6449455446"),
535 (Value::Text("\u{00fc}".into()), "62c3bc"),
536 (Value::Text("\u{6c34}".into()), "63e6b0b4"),
537 (Value::Array(vec![]), "80"),
538 (
539 Value::Array(vec![
540 Value::Integer(1),
541 Value::Integer(2),
542 Value::Integer(3),
543 ]),
544 "83010203",
545 ),
546 (Value::Map(vec![]), "a0"),
547 (
548 Value::Map(vec![
549 (Value::Text("a".into()), Value::Integer(1)),
550 (
551 Value::Text("b".into()),
552 Value::Array(vec![Value::Integer(2), Value::Integer(3)]),
553 ),
554 ]),
555 "a26161016162820203",
556 ),
557 (
558 Value::Tag(1, Box::new(Value::Integer(1363896240))),
559 "c11a514b67b0",
560 ),
561 ];
562 for (value, expected) in cases {
563 assert_eq!(hex(&encode(&value).unwrap()), expected, "{value:?}");
564 assert_eq!(decode(&unhex(expected)).unwrap(), value, "{expected}");
565 }
566 }
567
568 #[test]
569 fn float_widths_decode_to_f64() {
570 assert_eq!(decode(&unhex("f90000")).unwrap(), Value::Float(0.0));
571 assert_eq!(decode(&unhex("f93c00")).unwrap(), Value::Float(1.0));
572 assert_eq!(decode(&unhex("f97c00")).unwrap(), Value::Float(f64::INFINITY));
573 assert_eq!(decode(&unhex("fa47c35000")).unwrap(), Value::Float(100000.0));
574 assert_eq!(
576 decode(&unhex("f90001")).unwrap(),
577 Value::Float(5.960464477539063e-8)
578 );
579 }
580
581 #[test]
582 fn indefinite_lengths_accepted() {
583 assert_eq!(
585 decode(&unhex("5f42010243030405ff")).unwrap(),
586 Value::Bytes(vec![1, 2, 3, 4, 5])
587 );
588 assert_eq!(
590 decode(&unhex("826161bf61626163ff")).unwrap(),
591 Value::Array(vec![
592 Value::Text("a".into()),
593 Value::Map(vec![(Value::Text("b".into()), Value::Text("c".into()))]),
594 ])
595 );
596 }
597
598 #[test]
599 fn hostile_inputs_rejected_without_allocation() {
600 assert_eq!(
602 decode(&unhex("5affffffff")).unwrap_err(),
603 CborError::Truncated
604 );
605 assert_eq!(
607 decode(&unhex("9bffffffffffffffff")).unwrap_err(),
608 CborError::Truncated
609 );
610 assert_eq!(
612 decode(&unhex("bbffffffffffffffff")).unwrap_err(),
613 CborError::Truncated
614 );
615 assert_eq!(decode(&[]).unwrap_err(), CborError::Truncated);
617 assert!(matches!(
619 decode(&unhex("ff")).unwrap_err(),
620 CborError::Invalid(_)
621 ));
622 assert!(matches!(
624 decode(&unhex("61ff")).unwrap_err(),
625 CborError::Invalid(_)
626 ));
627 }
628
629 #[test]
630 fn depth_limit_enforced() {
631 let mut buf = vec![0x81u8; 200];
633 buf.push(0x00);
634 assert_eq!(decode(&buf).unwrap_err(), CborError::TooDeep);
635 let mut ok = vec![0x81u8; 100];
637 ok.push(0x00);
638 assert!(decode(&ok).is_ok());
639 }
640
641 #[test]
642 fn trailing_bytes_ignored() {
643 assert_eq!(decode(&unhex("01ffffff")).unwrap(), Value::Integer(1));
644 }
645
646 #[test]
647 fn json_round_trip() {
648 let json = serde_json::json!({
649 "z": "last",
650 "a": [1, 2.5, true, null, {"nested": "x"}],
651 "n": -42,
652 "big": u64::MAX,
653 });
654 let value = from_json(&json).unwrap();
655 let bytes = encode(&value).unwrap();
656 let back = to_json(&decode(&bytes).unwrap()).unwrap();
657 assert_eq!(back, json);
658 }
659}