Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 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 | |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 17 | #![cfg_attr(not(feature = "std"), no_std)] |
| 18 | |
| 19 | extern crate alloc; |
| 20 | |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame^] | 21 | use alloc::string::String; |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 22 | use alloc::vec::Vec; |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame^] | 23 | use ciborium::value::{Integer, Value}; |
| 24 | use coset::{CoseError, CoseKey, Label, Result}; |
| 25 | use log::error; |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 26 | use serde::{de::DeserializeOwned, Serialize}; |
| 27 | |
| 28 | /// Serializes the given data to a CBOR-encoded byte vector. |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 29 | pub fn serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> { |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 30 | let mut data = Vec::new(); |
| 31 | ciborium::into_writer(v, &mut data)?; |
| 32 | Ok(data) |
| 33 | } |
| 34 | |
| 35 | /// Deserializes the given type from a CBOR-encoded byte slice, failing if any extra |
| 36 | /// data remains after the type has been read. |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 37 | pub fn deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> { |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 38 | let res = ciborium::from_reader(&mut data)?; |
| 39 | if data.is_empty() { |
| 40 | Ok(res) |
| 41 | } else { |
| 42 | Err(CoseError::ExtraneousData) |
| 43 | } |
| 44 | } |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame^] | 45 | |
| 46 | /// Converts the provided value `v` to a value array. |
| 47 | pub fn value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>> { |
| 48 | v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context)) |
| 49 | } |
| 50 | |
| 51 | /// Converts the provided value `v` to a text string. |
| 52 | pub fn value_to_text(v: Value, context: &'static str) -> Result<String> { |
| 53 | v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context)) |
| 54 | } |
| 55 | |
| 56 | /// Converts the provided value `v` to a map. |
| 57 | pub fn value_to_map(v: Value, context: &'static str) -> Result<Vec<(Value, Value)>> { |
| 58 | v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context)) |
| 59 | } |
| 60 | |
| 61 | /// Converts the provided value `v` to a number. |
| 62 | pub fn value_to_num<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T> { |
| 63 | let num = v.into_integer().map_err(|e| to_unexpected_item_error(&e, "int", context))?; |
| 64 | num.try_into().map_err(|_| { |
| 65 | error!("The provided value '{num:?}' is not a valid number: {context}"); |
| 66 | CoseError::OutOfRangeIntegerValue |
| 67 | }) |
| 68 | } |
| 69 | |
| 70 | /// Converts the provided value `v` to a byte array of length `N`. |
| 71 | pub fn value_to_byte_array<const N: usize>(v: Value, context: &'static str) -> Result<[u8; N]> { |
| 72 | let arr = value_to_bytes(v, context)?; |
| 73 | arr.try_into().map_err(|e| { |
| 74 | error!("The provided value '{context}' is not an array of length {N}: {e:?}"); |
| 75 | CoseError::UnexpectedItem("bstr", "array of length {N}") |
| 76 | }) |
| 77 | } |
| 78 | |
| 79 | /// Converts the provided value `v` to bytes array. |
| 80 | pub fn value_to_bytes(v: Value, context: &'static str) -> Result<Vec<u8>> { |
| 81 | v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context)) |
| 82 | } |
| 83 | |
| 84 | /// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected |
| 85 | /// type `expected_type` and logs the error message with the provided `context`. |
| 86 | pub fn to_unexpected_item_error( |
| 87 | v: &Value, |
| 88 | expected_type: &'static str, |
| 89 | context: &'static str, |
| 90 | ) -> CoseError { |
| 91 | let v_type = cbor_value_type(v); |
| 92 | assert!(v_type != expected_type); |
| 93 | error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}"); |
| 94 | CoseError::UnexpectedItem(v_type, expected_type) |
| 95 | } |
| 96 | |
| 97 | /// Reads the type of the provided value `v`. |
| 98 | pub fn cbor_value_type(v: &Value) -> &'static str { |
| 99 | match v { |
| 100 | Value::Integer(_) => "int", |
| 101 | Value::Bytes(_) => "bstr", |
| 102 | Value::Float(_) => "float", |
| 103 | Value::Text(_) => "tstr", |
| 104 | Value::Bool(_) => "bool", |
| 105 | Value::Null => "nul", |
| 106 | Value::Tag(_, _) => "tag", |
| 107 | Value::Array(_) => "array", |
| 108 | Value::Map(_) => "map", |
| 109 | _ => "other", |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /// Returns the value of the given label in the given COSE key as bytes. |
| 114 | pub fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> { |
| 115 | let v = get_label_value(key, label)?; |
| 116 | Ok(v.as_bytes().ok_or_else(|| { |
| 117 | to_unexpected_item_error(v, "bstr", "Get label value in CoseKey as bytes") |
| 118 | })?) |
| 119 | } |
| 120 | |
| 121 | /// Returns the value of the given label in the given COSE key. |
| 122 | pub fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> { |
| 123 | Ok(&key |
| 124 | .params |
| 125 | .iter() |
| 126 | .find(|(k, _)| k == &label) |
| 127 | .ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))? |
| 128 | .1) |
| 129 | } |