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