// Copyright 2021 Google LLC // // 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. // //////////////////////////////////////////////////////////////////////////////// //! COSE_KDF_Context functionality. use crate::{ cbor::value::Value, common::AsCborValue, iana, util::{cbor_type_error, ValueTryAs}, Algorithm, CoseError, ProtectedHeader, Result, }; use alloc::{vec, vec::Vec}; use core::convert::TryInto; #[cfg(test)] mod tests; /// A nonce value. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Nonce { Bytes(Vec), Integer(i64), } /// Structure representing a party involved in key derivation. /// /// ```cddl /// PartyInfo = ( /// identity : bstr / nil, /// nonce : bstr / int / nil, /// other : bstr / nil /// ) /// ``` #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct PartyInfo { pub identity: Option>, pub nonce: Option, pub other: Option>, } impl crate::CborSerializable for PartyInfo {} impl AsCborValue for PartyInfo { fn from_cbor_value(value: Value) -> Result { let mut a = value.try_as_array()?; if a.len() != 3 { return Err(CoseError::UnexpectedItem("array", "array with 3 items")); } // Remove array elements in reverse order to avoid shifts. Ok(Self { other: match a.remove(2) { Value::Null => None, Value::Bytes(b) => Some(b), v => return cbor_type_error(&v, "bstr / nil"), }, nonce: match a.remove(1) { Value::Null => None, Value::Bytes(b) => Some(Nonce::Bytes(b)), Value::Integer(u) => Some(Nonce::Integer(u.try_into()?)), v => return cbor_type_error(&v, "bstr / int / nil"), }, identity: match a.remove(0) { Value::Null => None, Value::Bytes(b) => Some(b), v => return cbor_type_error(&v, "bstr / nil"), }, }) } fn to_cbor_value(self) -> Result { Ok(Value::Array(vec![ match self.identity { None => Value::Null, Some(b) => Value::Bytes(b), }, match self.nonce { None => Value::Null, Some(Nonce::Bytes(b)) => Value::Bytes(b), Some(Nonce::Integer(i)) => Value::from(i), }, match self.other { None => Value::Null, Some(b) => Value::Bytes(b), }, ])) } } /// Builder for [`PartyInfo`] objects. #[derive(Debug, Default)] pub struct PartyInfoBuilder(PartyInfo); impl PartyInfoBuilder { builder! {PartyInfo} builder_set_optional! {identity: Vec} builder_set_optional! {nonce: Nonce} builder_set_optional! {other: Vec} } /// Structure representing supplemental public information. /// /// ```cddl /// SuppPubInfo : [ /// keyDataLength : uint, /// protected : empty_or_serialized_map, /// ? other : bstr /// ], /// ``` #[derive(Clone, Debug, Default, PartialEq)] pub struct SuppPubInfo { pub key_data_length: u64, pub protected: ProtectedHeader, pub other: Option>, } impl crate::CborSerializable for SuppPubInfo {} impl AsCborValue for SuppPubInfo { fn from_cbor_value(value: Value) -> Result { let mut a = value.try_as_array()?; if a.len() != 2 && a.len() != 3 { return Err(CoseError::UnexpectedItem( "array", "array with 2 or 3 items", )); } // Remove array elements in reverse order to avoid shifts. Ok(Self { other: { if a.len() == 3 { Some(a.remove(2).try_as_bytes()?) } else { None } }, protected: ProtectedHeader::from_cbor_bstr(a.remove(1))?, key_data_length: a.remove(0).try_as_integer()?.try_into()?, }) } fn to_cbor_value(self) -> Result { let mut v = vec![ Value::from(self.key_data_length), self.protected.cbor_bstr()?, ]; if let Some(other) = self.other { v.push(Value::Bytes(other)); } Ok(Value::Array(v)) } } /// Builder for [`SuppPubInfo`] objects. #[derive(Debug, Default)] pub struct SuppPubInfoBuilder(SuppPubInfo); impl SuppPubInfoBuilder { builder! {SuppPubInfo} builder_set! {key_data_length: u64} builder_set_protected! {protected} builder_set_optional! {other: Vec} } /// Structure representing a a key derivation context. /// ```cdl /// COSE_KDF_Context = [ /// AlgorithmID : int / tstr, /// PartyUInfo : [ PartyInfo ], /// PartyVInfo : [ PartyInfo ], /// SuppPubInfo : [ /// keyDataLength : uint, /// protected : empty_or_serialized_map, /// ? other : bstr /// ], /// ? SuppPrivInfo : bstr /// ] /// ``` #[derive(Clone, Debug, Default, PartialEq)] pub struct CoseKdfContext { algorithm_id: Algorithm, party_u_info: PartyInfo, party_v_info: PartyInfo, supp_pub_info: SuppPubInfo, supp_priv_info: Vec>, } impl crate::CborSerializable for CoseKdfContext {} impl AsCborValue for CoseKdfContext { fn from_cbor_value(value: Value) -> Result { let mut a = value.try_as_array()?; if a.len() < 4 { return Err(CoseError::UnexpectedItem( "array", "array with at least 4 items", )); } // Remove array elements in reverse order to avoid shifts. let mut supp_priv_info = Vec::with_capacity(a.len() - 4); for i in (4..a.len()).rev() { supp_priv_info.push(a.remove(i).try_as_bytes()?); } supp_priv_info.reverse(); Ok(Self { supp_priv_info, supp_pub_info: SuppPubInfo::from_cbor_value(a.remove(3))?, party_v_info: PartyInfo::from_cbor_value(a.remove(2))?, party_u_info: PartyInfo::from_cbor_value(a.remove(1))?, algorithm_id: Algorithm::from_cbor_value(a.remove(0))?, }) } fn to_cbor_value(self) -> Result { let mut v = vec![ self.algorithm_id.to_cbor_value()?, self.party_u_info.to_cbor_value()?, self.party_v_info.to_cbor_value()?, self.supp_pub_info.to_cbor_value()?, ]; for supp_priv_info in self.supp_priv_info { v.push(Value::Bytes(supp_priv_info)); } Ok(Value::Array(v)) } } /// Builder for [`CoseKdfContext`] objects. #[derive(Debug, Default)] pub struct CoseKdfContextBuilder(CoseKdfContext); impl CoseKdfContextBuilder { builder! {CoseKdfContext} builder_set! {party_u_info: PartyInfo} builder_set! {party_v_info: PartyInfo} builder_set! {supp_pub_info: SuppPubInfo} /// Set the algorithm. #[must_use] pub fn algorithm(mut self, alg: iana::Algorithm) -> Self { self.0.algorithm_id = Algorithm::Assigned(alg); self } /// Add supplemental private info. #[must_use] pub fn add_supp_priv_info(mut self, supp_priv_info: Vec) -> Self { self.0.supp_priv_info.push(supp_priv_info); self } }