// Copyright 2023, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Utility functions for CBOR serialization/deserialization. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use alloc::string::String; use alloc::vec::Vec; use ciborium::value::{Integer, Value}; use coset::{ iana::{self, EnumI64}, CborSerializable, CoseError, CoseKey, Label, Result, }; use log::error; use serde::{de::DeserializeOwned, Serialize}; /// Serializes the given data to a CBOR-encoded byte vector. pub fn serialize(v: &T) -> Result> { let mut data = Vec::new(); ciborium::into_writer(v, &mut data)?; Ok(data) } /// Deserializes the given type from a CBOR-encoded byte slice, failing if any extra /// data remains after the type has been read. pub fn deserialize(mut data: &[u8]) -> Result { let res = ciborium::from_reader(&mut data)?; if data.is_empty() { Ok(res) } else { Err(CoseError::ExtraneousData) } } /// Parses the given CBOR-encoded byte slice as a value array. pub fn parse_value_array(data: &[u8], context: &'static str) -> Result> { value_to_array(Value::from_slice(data)?, context) } /// Converts the provided value `v` to a value array. pub fn value_to_array(v: Value, context: &'static str) -> Result> { v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context)) } /// Converts the provided value `v` to a text string. pub fn value_to_text(v: Value, context: &'static str) -> Result { v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context)) } /// Converts the provided value `v` to a map. pub fn value_to_map(v: Value, context: &'static str) -> Result> { v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context)) } /// Converts the provided value `v` to a number. pub fn value_to_num>(v: Value, context: &'static str) -> Result { let num = v.into_integer().map_err(|e| to_unexpected_item_error(&e, "int", context))?; num.try_into().map_err(|_| { error!("The provided value '{num:?}' is not a valid number: {context}"); CoseError::OutOfRangeIntegerValue }) } /// Converts the provided value `v` to a byte array of length `N`. pub fn value_to_byte_array(v: Value, context: &'static str) -> Result<[u8; N]> { let arr = value_to_bytes(v, context)?; arr.try_into().map_err(|e| { error!("The provided value '{context}' is not an array of length {N}: {e:?}"); CoseError::UnexpectedItem("bstr", "array of length {N}") }) } /// Converts the provided value `v` to bytes array. pub fn value_to_bytes(v: Value, context: &'static str) -> Result> { v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context)) } /// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected /// type `expected_type` and logs the error message with the provided `context`. pub fn to_unexpected_item_error( v: &Value, expected_type: &'static str, context: &'static str, ) -> CoseError { let v_type = cbor_value_type(v); assert!(v_type != expected_type); error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}"); CoseError::UnexpectedItem(v_type, expected_type) } /// Reads the type of the provided value `v`. pub fn cbor_value_type(v: &Value) -> &'static str { match v { Value::Integer(_) => "int", Value::Bytes(_) => "bstr", Value::Float(_) => "float", Value::Text(_) => "tstr", Value::Bool(_) => "bool", Value::Null => "nul", Value::Tag(_, _) => "tag", Value::Array(_) => "array", Value::Map(_) => "map", _ => "other", } } /// Returns the value of the given label in the given COSE key as bytes. pub fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> { let v = get_label_value(key, label)?; Ok(v.as_bytes().ok_or_else(|| { to_unexpected_item_error(v, "bstr", "Get label value in CoseKey as bytes") })?) } /// Returns the value of the given label in the given COSE key. pub fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> { Ok(&key .params .iter() .find(|(k, _)| k == &label) .ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))? .1) } /// Converts the provided COSE key algorithm integer to an `iana::Algorithm` used /// by DICE chains. pub fn dice_cose_key_alg(cose_key_alg: i32) -> Result { let key_alg = iana::Algorithm::from_i64(cose_key_alg as i64).ok_or_else(|| { error!("Unsupported COSE key algorithm for DICE: {cose_key_alg}"); CoseError::UnexpectedItem("COSE key algorithm", "") })?; match key_alg { iana::Algorithm::EdDSA | iana::Algorithm::ES256 | iana::Algorithm::ES384 => Ok(key_alg), _ => { error!("Unsupported COSE key algorithm for DICE: {key_alg:?}"); Err(CoseError::UnexpectedItem("-8, -7 or -35", "")) } } }