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}; |
Alice Wang | ff38111 | 2024-05-22 12:14:39 +0000 | [diff] [blame] | 24 | use coset::{ |
| 25 | iana::{self, EnumI64}, |
| 26 | CborSerializable, CoseError, CoseKey, Label, Result, |
| 27 | }; |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 28 | use log::error; |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 29 | use serde::{de::DeserializeOwned, Serialize}; |
| 30 | |
| 31 | /// Serializes the given data to a CBOR-encoded byte vector. |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 32 | pub fn serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> { |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 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. |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 40 | pub fn deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> { |
Alice Wang | facc2b8 | 2023-10-05 14:05:47 +0000 | [diff] [blame] | 41 | let res = ciborium::from_reader(&mut data)?; |
| 42 | if data.is_empty() { |
| 43 | Ok(res) |
| 44 | } else { |
| 45 | Err(CoseError::ExtraneousData) |
| 46 | } |
| 47 | } |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 48 | |
Alice Wang | 5fb76c5 | 2024-01-05 13:16:13 +0000 | [diff] [blame] | 49 | /// Parses the given CBOR-encoded byte slice as a value array. |
| 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 | |
Alice Wang | dd29c5d | 2023-12-07 09:56:23 +0000 | [diff] [blame] | 54 | /// Converts the provided value `v` to a value array. |
| 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. |
| 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. |
| 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. |
| 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`. |
| 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. |
| 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`. |
| 94 | 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`. |
| 106 | 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. |
| 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. |
| 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 | } |
Alice Wang | ff38111 | 2024-05-22 12:14:39 +0000 | [diff] [blame] | 138 | |
| 139 | /// Converts the provided COSE key algorithm integer to an `iana::Algorithm` used |
| 140 | /// by DICE chains. |
| 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 | } |