blob: 6e834f1dc815e9665598d692ba6981be6aabe348 [file] [log] [blame]
// 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::{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<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> {
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<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> {
let res = ciborium::from_reader(&mut data)?;
if data.is_empty() {
Ok(res)
} else {
Err(CoseError::ExtraneousData)
}
}
/// Converts the provided value `v` to a value array.
pub fn value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>> {
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<String> {
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<Vec<(Value, Value)>> {
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<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T> {
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<const N: usize>(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<Vec<u8>> {
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)
}