xref: /aosp_15_r20/system/authgraph/wire/src/cbor.rs (revision 4185b0660fbe514985fdcf75410317caad8afad1)
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! CBOR serialization code.
16 
17 use alloc::{vec, vec::Vec};
18 use ciborium::value::Value;
19 
20 /// Marker structure indicating that the EOF was encountered when reading CBOR data.
21 #[derive(Debug)]
22 pub struct EndOfFile;
23 
24 /// Error type for failures in encoding or decoding CBOR types.
25 pub enum CborError {
26     /// CBOR decoding failure.
27     DecodeFailed(ciborium::de::Error<EndOfFile>),
28     /// CBOR encoding failure.
29     EncodeFailed,
30     /// CBOR input had extra data.
31     ExtraneousData,
32     /// Integer value outside expected range.
33     OutOfRangeIntegerValue,
34     /// Integer value that doesn't match expected set of allowed enum values.
35     NonEnumValue,
36     /// Unexpected CBOR item encountered (got, want).
37     UnexpectedItem(&'static str, &'static str),
38     /// Value conversion failure.
39     InvalidValue,
40     /// Allocation failure.
41     AllocationFailed,
42 }
43 
44 impl<T> From<ciborium::de::Error<T>> for CborError {
from(e: ciborium::de::Error<T>) -> Self45     fn from(e: ciborium::de::Error<T>) -> Self {
46         // Make sure we use our [`EndOfFile`] marker.
47         use ciborium::de::Error::{Io, RecursionLimitExceeded, Semantic, Syntax};
48         let e = match e {
49             Io(_) => Io(EndOfFile),
50             Syntax(x) => Syntax(x),
51             Semantic(a, b) => Semantic(a, b),
52             RecursionLimitExceeded => RecursionLimitExceeded,
53         };
54         CborError::DecodeFailed(e)
55     }
56 }
57 
58 impl<T> From<ciborium::ser::Error<T>> for CborError {
from(_e: ciborium::ser::Error<T>) -> Self59     fn from(_e: ciborium::ser::Error<T>) -> Self {
60         CborError::EncodeFailed
61     }
62 }
63 
64 impl core::fmt::Debug for CborError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result65     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66         match self {
67             CborError::DecodeFailed(de) => write!(f, "decode CBOR failure: {:?}", de),
68             CborError::EncodeFailed => write!(f, "encode CBOR failure"),
69             CborError::ExtraneousData => write!(f, "extraneous data in CBOR input"),
70             CborError::OutOfRangeIntegerValue => write!(f, "out of range integer value"),
71             CborError::NonEnumValue => write!(f, "integer not a valid enum value"),
72             CborError::UnexpectedItem(got, want) => write!(f, "got {}, expected {}", got, want),
73             CborError::InvalidValue => write!(f, "invalid CBOR value"),
74             CborError::AllocationFailed => write!(f, "allocation failed"),
75         }
76     }
77 }
78 
79 /// Return an error indicating that an unexpected CBOR type was encountered.
cbor_type_error<T>(value: &Value, want: &'static str) -> Result<T, CborError>80 pub fn cbor_type_error<T>(value: &Value, want: &'static str) -> Result<T, CborError> {
81     let got = match value {
82         Value::Integer(_) => "int",
83         Value::Bytes(_) => "bstr",
84         Value::Text(_) => "tstr",
85         Value::Array(_) => "array",
86         Value::Map(_) => "map",
87         Value::Tag(_, _) => "tag",
88         Value::Float(_) => "float",
89         Value::Bool(_) => "bool",
90         Value::Null => "null",
91         _ => "unknown",
92     };
93     Err(CborError::UnexpectedItem(got, want))
94 }
95 
96 /// Read a [`Value`] from a byte slice, failing if any extra data remains after the `Value` has been
97 /// read.
read_to_value(mut slice: &[u8]) -> Result<Value, CborError>98 pub fn read_to_value(mut slice: &[u8]) -> Result<Value, CborError> {
99     let value = ciborium::de::from_reader_with_recursion_limit(&mut slice, 16)?;
100     if slice.is_empty() {
101         Ok(value)
102     } else {
103         Err(CborError::ExtraneousData)
104     }
105 }
106 
107 /// Trait for types that can be converted to/from a [`Value`]. This is essentially the same as the
108 /// `coset::AsCborValue` trait, but defining a copy locally allows us to implement it for primitive
109 /// types without falling foul of the orphan trait rules.
110 pub trait AsCborValue: Sized {
111     /// Convert a [`Value`] into an instance of the type.
from_cbor_value(value: Value) -> Result<Self, CborError>112     fn from_cbor_value(value: Value) -> Result<Self, CborError>;
113 
114     /// Convert the object into a [`Value`], consuming it along the way.
to_cbor_value(self) -> Result<Value, CborError>115     fn to_cbor_value(self) -> Result<Value, CborError>;
116 
117     /// Create an object instance from serialized CBOR data in a slice.
from_slice(slice: &[u8]) -> Result<Self, CborError>118     fn from_slice(slice: &[u8]) -> Result<Self, CborError> {
119         Self::from_cbor_value(read_to_value(slice)?)
120     }
121 
122     /// Serialize this object to a vector, consuming it along the way.
into_vec(self) -> Result<Vec<u8>, CborError>123     fn into_vec(self) -> Result<Vec<u8>, CborError> {
124         let mut data = Vec::new();
125         ciborium::ser::into_writer(&self.to_cbor_value()?, &mut data)?;
126         Ok(data)
127     }
128 }
129 
130 /// An `Option<T>` encodes as `( ? t )`, where `t` is whatever `T` encodes as in CDDL.
131 impl<T: AsCborValue> AsCborValue for Option<T> {
from_cbor_value(value: Value) -> Result<Self, CborError>132     fn from_cbor_value(value: Value) -> Result<Self, CborError> {
133         let mut arr = match value {
134             Value::Array(a) => a,
135             _ => return Err(CborError::UnexpectedItem("non-arr", "arr")),
136         };
137         match arr.len() {
138             0 => Ok(None),
139             1 => Ok(Some(<T>::from_cbor_value(arr.remove(0))?)),
140             _ => Err(CborError::UnexpectedItem("arr len >1", "arr len 0/1")),
141         }
142     }
to_cbor_value(self) -> Result<Value, CborError>143     fn to_cbor_value(self) -> Result<Value, CborError> {
144         match self {
145             Some(t) => Ok(Value::Array(vec![t.to_cbor_value()?])),
146             None => Ok(Value::Array(Vec::new())),
147         }
148     }
149 }
150 
151 impl<T: AsCborValue, const N: usize> AsCborValue for [T; N] {
from_cbor_value(value: Value) -> Result<Self, CborError>152     fn from_cbor_value(value: Value) -> Result<Self, CborError> {
153         let arr = match value {
154             Value::Array(a) => a,
155             _ => return cbor_type_error(&value, "arr"),
156         };
157         let results: Result<Vec<_>, _> = arr.into_iter().map(<T>::from_cbor_value).collect();
158         let results: Vec<_> = results?;
159         results.try_into().map_err(|_e| CborError::UnexpectedItem("arr other len", "arr fixed len"))
160     }
to_cbor_value(self) -> Result<Value, CborError>161     fn to_cbor_value(self) -> Result<Value, CborError> {
162         let values: Result<Vec<_>, _> = self.into_iter().map(|v| v.to_cbor_value()).collect();
163         Ok(Value::Array(values?))
164     }
165 }
166 
167 impl AsCborValue for Vec<u8> {
from_cbor_value(value: Value) -> Result<Self, CborError>168     fn from_cbor_value(value: Value) -> Result<Self, CborError> {
169         match value {
170             Value::Bytes(bstr) => Ok(bstr),
171             _ => cbor_type_error(&value, "bstr"),
172         }
173     }
to_cbor_value(self) -> Result<Value, CborError>174     fn to_cbor_value(self) -> Result<Value, CborError> {
175         Ok(Value::Bytes(self))
176     }
177 }
178 
179 impl AsCborValue for i32 {
from_cbor_value(value: Value) -> Result<Self, CborError>180     fn from_cbor_value(value: Value) -> Result<Self, CborError> {
181         match value {
182             Value::Integer(i) => i.try_into().map_err(|_| CborError::OutOfRangeIntegerValue),
183             _ => crate::cbor_type_error(&value, "i32"),
184         }
185     }
to_cbor_value(self) -> Result<Value, CborError>186     fn to_cbor_value(self) -> Result<Value, CborError> {
187         Ok(Value::Integer(self.into()))
188     }
189 }
190