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