1 // Copyright 2023, The Android Open Source Project
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 //! Utility functions for CBOR serialization/deserialization.
16 
17 #![cfg_attr(not(feature = "std"), no_std)]
18 
19 extern crate alloc;
20 
21 use alloc::string::String;
22 use alloc::vec::Vec;
23 use ciborium::value::{Integer, Value};
24 use coset::{
25     iana::{self, EnumI64},
26     CborSerializable, CoseError, CoseKey, Label, Result,
27 };
28 use log::error;
29 use serde::{de::DeserializeOwned, Serialize};
30 
31 /// Serializes the given data to a CBOR-encoded byte vector.
serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>>32 pub fn serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> {
33     let mut data = Vec::new();
34     ciborium::into_writer(v, &mut data)?;
35     Ok(data)
36 }
37 
38 /// Deserializes the given type from a CBOR-encoded byte slice, failing if any extra
39 /// data remains after the type has been read.
deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T>40 pub fn deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> {
41     let res = ciborium::from_reader(&mut data)?;
42     if data.is_empty() {
43         Ok(res)
44     } else {
45         Err(CoseError::ExtraneousData)
46     }
47 }
48 
49 /// Parses the given CBOR-encoded byte slice as a value array.
parse_value_array(data: &[u8], context: &'static str) -> Result<Vec<Value>>50 pub fn parse_value_array(data: &[u8], context: &'static str) -> Result<Vec<Value>> {
51     value_to_array(Value::from_slice(data)?, context)
52 }
53 
54 /// Converts the provided value `v` to a value array.
value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>>55 pub fn value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>> {
56     v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context))
57 }
58 
59 /// Converts the provided value `v` to a text string.
value_to_text(v: Value, context: &'static str) -> Result<String>60 pub fn value_to_text(v: Value, context: &'static str) -> Result<String> {
61     v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context))
62 }
63 
64 /// Converts the provided value `v` to a map.
value_to_map(v: Value, context: &'static str) -> Result<Vec<(Value, Value)>>65 pub fn value_to_map(v: Value, context: &'static str) -> Result<Vec<(Value, Value)>> {
66     v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context))
67 }
68 
69 /// Converts the provided value `v` to a number.
value_to_num<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T>70 pub fn value_to_num<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T> {
71     let num = v.into_integer().map_err(|e| to_unexpected_item_error(&e, "int", context))?;
72     num.try_into().map_err(|_| {
73         error!("The provided value '{num:?}' is not a valid number: {context}");
74         CoseError::OutOfRangeIntegerValue
75     })
76 }
77 
78 /// Converts the provided value `v` to a byte array of length `N`.
value_to_byte_array<const N: usize>(v: Value, context: &'static str) -> Result<[u8; N]>79 pub fn value_to_byte_array<const N: usize>(v: Value, context: &'static str) -> Result<[u8; N]> {
80     let arr = value_to_bytes(v, context)?;
81     arr.try_into().map_err(|e| {
82         error!("The provided value '{context}' is not an array of length {N}: {e:?}");
83         CoseError::UnexpectedItem("bstr", "array of length {N}")
84     })
85 }
86 
87 /// Converts the provided value `v` to bytes array.
value_to_bytes(v: Value, context: &'static str) -> Result<Vec<u8>>88 pub fn value_to_bytes(v: Value, context: &'static str) -> Result<Vec<u8>> {
89     v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context))
90 }
91 
92 /// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected
93 /// type `expected_type` and logs the error message with the provided `context`.
to_unexpected_item_error( v: &Value, expected_type: &'static str, context: &'static str, ) -> CoseError94 pub fn to_unexpected_item_error(
95     v: &Value,
96     expected_type: &'static str,
97     context: &'static str,
98 ) -> CoseError {
99     let v_type = cbor_value_type(v);
100     assert!(v_type != expected_type);
101     error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}");
102     CoseError::UnexpectedItem(v_type, expected_type)
103 }
104 
105 /// Reads the type of the provided value `v`.
cbor_value_type(v: &Value) -> &'static str106 pub fn cbor_value_type(v: &Value) -> &'static str {
107     match v {
108         Value::Integer(_) => "int",
109         Value::Bytes(_) => "bstr",
110         Value::Float(_) => "float",
111         Value::Text(_) => "tstr",
112         Value::Bool(_) => "bool",
113         Value::Null => "nul",
114         Value::Tag(_, _) => "tag",
115         Value::Array(_) => "array",
116         Value::Map(_) => "map",
117         _ => "other",
118     }
119 }
120 
121 /// Returns the value of the given label in the given COSE key as bytes.
get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]>122 pub fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
123     let v = get_label_value(key, label)?;
124     Ok(v.as_bytes().ok_or_else(|| {
125         to_unexpected_item_error(v, "bstr", "Get label value in CoseKey as bytes")
126     })?)
127 }
128 
129 /// Returns the value of the given label in the given COSE key.
get_label_value(key: &CoseKey, label: Label) -> Result<&Value>130 pub fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
131     Ok(&key
132         .params
133         .iter()
134         .find(|(k, _)| k == &label)
135         .ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))?
136         .1)
137 }
138 
139 /// Converts the provided COSE key algorithm integer to an `iana::Algorithm` used
140 /// by DICE chains.
dice_cose_key_alg(cose_key_alg: i32) -> Result<iana::Algorithm>141 pub fn dice_cose_key_alg(cose_key_alg: i32) -> Result<iana::Algorithm> {
142     let key_alg = iana::Algorithm::from_i64(cose_key_alg as i64).ok_or_else(|| {
143         error!("Unsupported COSE key algorithm for DICE: {cose_key_alg}");
144         CoseError::UnexpectedItem("COSE key algorithm", "")
145     })?;
146     match key_alg {
147         iana::Algorithm::EdDSA | iana::Algorithm::ES256 | iana::Algorithm::ES384 => Ok(key_alg),
148         _ => {
149             error!("Unsupported COSE key algorithm for DICE: {key_alg:?}");
150             Err(CoseError::UnexpectedItem("-8, -7 or -35", ""))
151         }
152     }
153 }
154