| // Copyright 2024 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 |
| // |
| // https://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. |
| |
| //! Types and functions for encoding and decoding command/response messages. |
| |
| use crate::args::{ArgMap, ArgTypeMap, ArgTypeSelector, ArgValue}; |
| use crate::byte_array_wrapper; |
| use crate::cbor::{ |
| cbor_decoder_from_message, cbor_encoder_from_message, encode_bytes_prefix, |
| DecoderExt, |
| }; |
| use crate::constants::*; |
| use crate::crypto::{HandshakePayload, Hash}; |
| use crate::dice::{ |
| Cdi, Certificate, DiceInput, DiceInputAuthority, DiceInputCode, |
| DiceInputConfig, DiceInputMode, InternalInputType, Uds, |
| }; |
| use crate::error::{DpeResult, ErrCode}; |
| use crate::memory::{Message, SmallMessage}; |
| use heapless::Vec; |
| use log::{debug, error}; |
| use minicbor::Decoder; |
| use num_derive::{FromPrimitive, ToPrimitive}; |
| use zeroize::ZeroizeOnDrop; |
| |
| // Both session and command messages are encoded as a CBOR array with two |
| // elements. See the following snippets from the DPE specification. |
| // |
| // session-message = [ |
| // session-id: uint, |
| // message: bytes, ; Ciphertext, unless using the plaintext session. |
| // ] |
| // |
| // command-message = [ |
| // command-id: $command-id, |
| // input-args: $input-args, |
| // ] |
| // |
| // response-message = [ |
| // error-code: $error-code, |
| // output-args: $output-args, |
| // ] |
| pub(crate) const MESSAGE_ARRAY_SIZE: u64 = 2; |
| |
| byte_array_wrapper!(ContextHandle, DPE_HANDLE_SIZE, "context handle"); |
| |
| impl ContextHandle { |
| /// Creates a `ContextHandle` from the given slice and returns an option |
| /// which is `None` if the slice is empty. If the slice is not empty, the |
| /// behavior is similar to [`ContextHandle::from_slice`]. |
| pub(crate) fn from_slice_to_option(s: &[u8]) -> DpeResult<Option<Self>> { |
| if s.is_empty() { |
| Ok(None) |
| } else { |
| Ok(Some(Self::from_slice(s)?)) |
| } |
| } |
| } |
| |
| /// A Vec wrapper to represent a certificate chain. |
| #[derive(Clone, Debug, Default, Eq, PartialEq, Hash)] |
| pub(crate) struct CertificateChain( |
| pub(crate) Vec<Certificate, DPE_MAX_CERTIFICATES_PER_CHAIN>, |
| ); |
| |
| /// A usize wrapper to represent a LocalityId. |
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] |
| pub(crate) struct LocalityId(pub(crate) usize); |
| |
| impl TryFrom<LocalityId> for u16 { |
| type Error = ErrCode; |
| fn try_from(value: LocalityId) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_usize(value.0).ok_or_else(|| { |
| error!("Invalid locality ID"); |
| ErrCode::InternalError |
| }) |
| } |
| } |
| |
| /// A usize wrapper to represent a SessionId. |
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] |
| pub(crate) struct SessionId(pub(crate) usize); |
| |
| impl TryFrom<SessionId> for u16 { |
| type Error = ErrCode; |
| fn try_from(value: SessionId) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_usize(value.0).ok_or_else(|| { |
| error!("Invalid session ID"); |
| ErrCode::InternalError |
| }) |
| } |
| } |
| |
| /// Defines the possible initialization types and their associated data. |
| #[derive(Clone, Debug, Eq, PartialEq, Hash, ZeroizeOnDrop)] |
| pub(crate) enum InitType { |
| /// Used when a DPE context is initialized from a UDS using a seed provided |
| /// by the client. |
| Uds { |
| /// The UDS seed provided by the client. |
| external_uds_seed: Uds, |
| }, |
| /// Used when a DPE context is initialized from a UDS using only values |
| /// available to the DPE but not available to the client. |
| InternalUds, |
| /// Used when a DPE context is initialized from a pair of CDIs provided by |
| /// the client. |
| Cdis { |
| /// The signing CDI value provided by the client. |
| cdi_sign: Cdi, |
| /// The sealing CDI value provided by the client. |
| cdi_seal: Cdi, |
| }, |
| /// Used when a DPE context is initialized from CDI values available to the |
| /// DPE but not available to the client. |
| InternalCdis, |
| } |
| |
| impl TryFrom<u32> for InternalInputType { |
| type Error = ErrCode; |
| fn try_from(value: u32) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u32(value).ok_or_else(|| { |
| error!("Unknown internal input type"); |
| ErrCode::InvalidArgument |
| }) |
| } |
| } |
| |
| /// A command selector type with discriminants that match the encoded CBOR |
| /// values. See the DPE specification for details on each of the commands. |
| #[derive( |
| Clone, Copy, Debug, Eq, PartialEq, Hash, FromPrimitive, ToPrimitive, |
| )] |
| pub(crate) enum CommandSelector { |
| /// The GetProfile command. |
| GetProfile = 1, |
| /// The OpenSession command. |
| OpenSession = 2, |
| /// The CloseSession command. |
| CloseSession = 3, |
| /// The SyncSession command. |
| SyncSession = 4, |
| /// The InitializeContext command. |
| InitializeContext = 7, |
| /// The DeriveContext command. |
| DeriveContext = 8, |
| /// The GetCertificateChain command. |
| GetCertificateChain = 16, |
| /// The CertifyKey command. |
| CertifyKey = 9, |
| /// The Sign command. |
| Sign = 10, |
| /// The Seal command. |
| Seal = 11, |
| /// The Unseal command. |
| Unseal = 12, |
| /// The DeriveSealingPublicKey command. |
| DeriveSealingPublicKey = 13, |
| /// The RotateContextHandle command. |
| RotateContextHandle = 14, |
| /// The DestroyContext command. |
| DestroyContext = 15, |
| } |
| |
| impl TryFrom<u32> for CommandSelector { |
| type Error = ErrCode; |
| fn try_from(value: u32) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u32(value).ok_or_else(|| { |
| error!("Unknown command id"); |
| ErrCode::InvalidCommand |
| }) |
| } |
| } |
| |
| // Context initialization data is encoded as a CBOR map, these are the keys. |
| #[derive( |
| Clone, Copy, Debug, Eq, PartialEq, Hash, FromPrimitive, ToPrimitive, |
| )] |
| pub(crate) enum InitTypeMapKey { |
| InitType = 1, |
| ExternalSeed = 2, |
| CdiSign = 3, |
| CdiSeal = 4, |
| } |
| |
| impl TryFrom<u32> for InitTypeMapKey { |
| type Error = ErrCode; |
| fn try_from(value: u32) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u32(value).ok_or_else(|| { |
| error!("Unknown initialization seed field"); |
| ErrCode::InvalidArgument |
| }) |
| } |
| } |
| |
| /// Indicates the type of value encoded into an initialization seed. |
| /// |
| /// See [`decode_init_seed`]. |
| #[derive( |
| Clone, Copy, Debug, Eq, PartialEq, Hash, FromPrimitive, ToPrimitive, |
| )] |
| pub(crate) enum InitTypeSelector { |
| /// The initialization value is a UDS seed. |
| Uds = 1, |
| /// The initialization value is a CDI. |
| Cdi = 2, |
| } |
| |
| impl TryFrom<u32> for InitTypeSelector { |
| type Error = ErrCode; |
| fn try_from(value: u32) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u32(value).ok_or_else(|| { |
| error!("Unknown initialization type"); |
| ErrCode::InvalidArgument |
| }) |
| } |
| } |
| |
| impl TryFrom<u8> for DiceInputMode { |
| type Error = ErrCode; |
| fn try_from(value: u8) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u8(value).ok_or_else(|| { |
| error!("Unknown mode value"); |
| ErrCode::InvalidArgument |
| }) |
| } |
| } |
| |
| // The DICE input fields are encoded as a CBOR map, these are the keys. |
| #[derive( |
| Clone, Copy, Debug, Eq, PartialEq, Hash, FromPrimitive, ToPrimitive, |
| )] |
| pub(crate) enum DiceInputMapKey { |
| VersionSlot = 1, |
| VersionValue = 2, |
| CodeHash = 3, |
| CodeDescriptor = 4, |
| ConfigInlineValue = 5, |
| ConfigDescriptor = 6, |
| AuthorityHash = 7, |
| AuthorityDescriptor = 8, |
| Mode = 9, |
| Hidden = 10, |
| } |
| |
| impl TryFrom<u32> for DiceInputMapKey { |
| type Error = ErrCode; |
| fn try_from(value: u32) -> DpeResult<Self> { |
| num_traits::FromPrimitive::from_u32(value).ok_or_else(|| { |
| error!("Unknown input map key"); |
| ErrCode::InvalidArgument |
| }) |
| } |
| } |
| |
| /// Decodes a CBOR-encoded set of internal input selectors. |
| pub(crate) fn decode_internal_inputs( |
| cbor: &[u8], |
| ) -> DpeResult<Vec<InternalInputType, DPE_MAX_INTERNAL_INPUTS>> { |
| debug!("decode_internal_inputs"); |
| let mut values: Vec<InternalInputType, DPE_MAX_INTERNAL_INPUTS> = |
| Default::default(); |
| if !cbor.is_empty() { |
| let mut decoder = Decoder::new(cbor); |
| let size = decoder.array()?.ok_or_else(|| { |
| error!("Indefinite arrays not supported"); |
| ErrCode::InvalidArgument |
| })?; |
| for _ in 0..size { |
| values |
| .push(InternalInputType::try_from(decoder.u32()?)?) |
| .map_err(|_| ErrCode::InternalError)?; |
| } |
| } |
| Ok(values) |
| } |
| |
| /// Decodes a CBOR-encoded locality selector. |
| pub(crate) fn decode_locality( |
| cbor: &[u8], |
| current_locality: LocalityId, |
| ) -> DpeResult<LocalityId> { |
| debug!("decode_locality"); |
| if cbor.is_empty() { |
| Ok(current_locality) |
| } else { |
| Ok(LocalityId(Decoder::new(cbor).u16()?.into())) |
| } |
| } |
| |
| /// Decodes the `seed` argument of the `InitializeContext` command. |
| pub(crate) fn decode_init_seed(seed: &[u8]) -> DpeResult<InitType> { |
| let mut init_type_selector: Option<InitTypeSelector> = None; |
| let mut external_uds_seed: Option<Uds> = None; |
| let mut cdi_sign: Option<Cdi> = None; |
| let mut cdi_seal: Option<Cdi> = None; |
| |
| let mut decoder = Decoder::new(seed); |
| let num_pairs = decoder.map()?.unwrap_or(0); |
| for _ in 0..num_pairs { |
| let map_key: InitTypeMapKey = decoder.u32()?.try_into()?; |
| match map_key { |
| InitTypeMapKey::InitType => { |
| init_type_selector = Some(decoder.u32()?.try_into()?); |
| } |
| InitTypeMapKey::ExternalSeed => { |
| external_uds_seed = Some(Uds::from_slice(decoder.bytes()?)?); |
| } |
| InitTypeMapKey::CdiSign => { |
| cdi_sign = Some(Cdi::from_slice(decoder.bytes()?)?); |
| } |
| InitTypeMapKey::CdiSeal => { |
| cdi_seal = Some(Cdi::from_slice(decoder.bytes()?)?); |
| } |
| } |
| } |
| |
| let init_type_selector = match init_type_selector { |
| None => { |
| error!("No initialization type selector"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| Some(value) => value, |
| }; |
| |
| let init_type = match init_type_selector { |
| InitTypeSelector::Uds => { |
| if let Some(external_uds_seed) = external_uds_seed { |
| InitType::Uds { external_uds_seed } |
| } else { |
| InitType::InternalUds |
| } |
| } |
| InitTypeSelector::Cdi => { |
| if let Some(cdi_sign) = cdi_sign { |
| let cdi_seal = cdi_seal.unwrap_or_else(|| cdi_sign.clone()); |
| InitType::Cdis { cdi_sign, cdi_seal } |
| } else { |
| InitType::InternalCdis |
| } |
| } |
| }; |
| Ok(init_type) |
| } |
| |
| /// Decodes the `input-data` argument of the `DeriveContext` command. |
| pub(crate) fn decode_dice_input( |
| encoded_dice_input: &[u8], |
| ) -> DpeResult<(Option<(usize, u64)>, DiceInput<'_>)> { |
| debug!("decode_dice_input"); |
| let mut decoder = Decoder::new(encoded_dice_input); |
| let mut tmp_version_slot = None; |
| let mut tmp_version_value = None; |
| let mut tmp_code_value = None; |
| let mut tmp_config_value = None; |
| let mut tmp_authority_value = None; |
| let mut tmp_mode_value = None; |
| let mut tmp_hidden_value = None; |
| let num_pairs = decoder.map()?.unwrap_or(0); |
| for _ in 0..num_pairs { |
| let map_key: DiceInputMapKey = decoder.u32()?.try_into()?; |
| match map_key { |
| DiceInputMapKey::VersionSlot => { |
| let slot: usize = decoder.u8()?.into(); |
| if slot >= DPE_MAX_VERSION_SLOTS { |
| error!("Invalid version slot"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| if tmp_version_slot.replace(slot).is_some() { |
| error!("Duplicate version slots"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::VersionValue => { |
| if tmp_version_value.replace(decoder.u64()?).is_some() { |
| error!("Duplicate version values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::CodeHash => { |
| if tmp_code_value |
| .replace(DiceInputCode::CodeHash(Hash::from_slice( |
| decoder.bytes()?, |
| )?)) |
| .is_some() |
| { |
| error!("Duplicate code values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::CodeDescriptor => { |
| if tmp_code_value |
| .replace(DiceInputCode::CodeDescriptor(decoder.bytes()?)) |
| .is_some() |
| { |
| error!("Duplicate code values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::ConfigInlineValue => { |
| if tmp_config_value |
| .replace(DiceInputConfig::ConfigInlineValue( |
| decoder.bytes()?.try_into()?, |
| )) |
| .is_some() |
| { |
| error!("Duplicate config values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::ConfigDescriptor => { |
| if tmp_config_value |
| .replace(DiceInputConfig::ConfigDescriptor( |
| decoder.bytes()?, |
| )) |
| .is_some() |
| { |
| error!("Duplicate config values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::AuthorityHash => { |
| if tmp_authority_value |
| .replace(DiceInputAuthority::AuthorityHash( |
| decoder.bytes()?.try_into()?, |
| )) |
| .is_some() |
| { |
| error!("Duplicate authority values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::AuthorityDescriptor => { |
| if tmp_authority_value |
| .replace(DiceInputAuthority::AuthorityDescriptor( |
| decoder.bytes()?, |
| )) |
| .is_some() |
| { |
| error!("Duplicate authority values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::Mode => { |
| if tmp_mode_value.replace(decoder.u8()?.try_into()?).is_some() { |
| error!("Duplicate mode values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| DiceInputMapKey::Hidden => { |
| if tmp_hidden_value |
| .replace(Hash::from_slice(decoder.bytes()?)?) |
| .is_some() |
| { |
| error!("Duplicate hidden values"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| } |
| }; |
| } |
| let dice_input = DiceInput { |
| code: tmp_code_value.ok_or_else(|| { |
| error!("Missing code value"); |
| ErrCode::InvalidArgument |
| })?, |
| config: tmp_config_value.ok_or_else(|| { |
| error!("Missing config value"); |
| ErrCode::InvalidArgument |
| })?, |
| authority: tmp_authority_value, |
| mode: tmp_mode_value.ok_or_else(|| { |
| error!("Missing mode value"); |
| ErrCode::InvalidArgument |
| })?, |
| hidden: tmp_hidden_value, |
| }; |
| let version_info = match (tmp_version_slot, tmp_version_value) { |
| (None, None) => None, |
| (Some(slot), Some(value)) => Some((slot, value)), |
| _ => { |
| error!("Incomplete version info"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| }; |
| Ok((version_info, dice_input)) |
| } |
| |
| /// Decodes the `unseal-policy` argument of the `Unseal` command. |
| pub(crate) fn decode_unseal_policy( |
| encoded_policy: &[u8], |
| ) -> DpeResult<[u64; DPE_MAX_VERSION_SLOTS]> { |
| let mut target_versions = [0; DPE_MAX_VERSION_SLOTS]; |
| let mut decoder = Decoder::new(encoded_policy); |
| let map_size = decoder.map()?.ok_or_else(|| { |
| error!("Indefinite CBOR maps not supported"); |
| ErrCode::InvalidArgument |
| })?; |
| for _ in 0..map_size { |
| let i = decoder.u16()? as usize; |
| if i >= DPE_MAX_VERSION_SLOTS { |
| error!("Invalid version slot in unseal policy"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| *target_versions.get_mut(i).ok_or(ErrCode::InternalError)? = |
| decoder.u64()?; |
| } |
| Ok(target_versions) |
| } |
| |
| /// Encodes a DPE command response indicating an error occurred. |
| /// |
| /// The response will contain the given `err_code` and written to the given |
| /// `response` buffer. Any existing data in the buffer is cleared. |
| /// |
| /// # Errors |
| /// |
| /// This function is infallible given the precondition that a message buffer is |
| /// always large enough to hold an error response. It uses `unwrap()` but these |
| /// will not panic as long as the precondition holds. |
| #[allow(clippy::unwrap_used)] |
| pub(crate) fn create_error_response(err_code: ErrCode, response: &mut Message) { |
| fn encode_error_response( |
| err_code: ErrCode, |
| response: &mut Message, |
| ) -> DpeResult<()> { |
| let _ = cbor_encoder_from_message(response) |
| .array(MESSAGE_ARRAY_SIZE)? |
| .u32(err_code as u32)? |
| .bytes(&[0])?; |
| Ok(()) |
| } |
| response.clear(); |
| encode_error_response(err_code, response).unwrap(); |
| } |
| |
| /// Like [`create_error_response`] but includes a plaintext session header. |
| /// |
| /// # Errors |
| /// |
| /// This function is infallible given the precondition that a message buffer is |
| /// always large enough to hold an error response. It uses `unwrap()` but these |
| /// will not panic as long as the precondition holds. |
| #[allow(clippy::unwrap_used)] |
| pub(crate) fn create_plaintext_session_error_response( |
| err_code: ErrCode, |
| response: &mut Message, |
| ) { |
| fn encode_session_prefix(response_size: usize) -> DpeResult<SmallMessage> { |
| let mut prefix = SmallMessage::new(); |
| let _ = cbor_encoder_from_message(&mut prefix) |
| .array(MESSAGE_ARRAY_SIZE)? |
| .u16(0)?; |
| encode_bytes_prefix(&mut prefix, response_size)?; |
| Ok(prefix) |
| } |
| create_error_response(err_code, response); |
| let prefix = encode_session_prefix(response.len()).unwrap(); |
| response.insert_prefix(prefix.as_slice()).unwrap(); |
| } |
| |
| /// Encodes the given `args` into the `encoded_args` buffer. |
| /// |
| /// Any existing data in the buffer is cleared. |
| pub(crate) fn encode_args( |
| args: &ArgMap, |
| encoded_args: &mut Message, |
| ) -> DpeResult<()> { |
| encoded_args.clear(); |
| let mut encoder = cbor_encoder_from_message(encoded_args); |
| let _ = encoder.map(args.len().try_into()?)?; |
| for (arg_id, arg_value) in args { |
| let _ = encoder.u32(*arg_id)?; |
| match arg_value { |
| ArgValue::Int(value) => { |
| let _ = encoder.u64(*value)?; |
| } |
| ArgValue::Bool(value) => { |
| let _ = encoder.bool(*value)?; |
| } |
| _ => { |
| let _ = encoder.bytes(arg_value.try_into()?)?; |
| } |
| } |
| } |
| Ok(()) |
| } |
| |
| pub(crate) fn decode_args_internal<'a>( |
| encoded_args: &'a [u8], |
| arg_types: &ArgTypeMap, |
| ) -> DpeResult<ArgMap<'a>> { |
| debug!("Decoding arguments of type: {:?}", arg_types); |
| let mut decoder = Decoder::new(encoded_args); |
| let num_pairs = match decoder.map() { |
| Err(_) => { |
| error!("Arguments not encoded as CBOR map"); |
| return Err(ErrCode::InvalidCommand); |
| } |
| Ok(None) => { |
| error!("Indefinite argument maps not supported"); |
| return Err(ErrCode::InvalidCommand); |
| } |
| Ok(Some(num)) => num, |
| }; |
| let mut args: ArgMap = Default::default(); |
| for _ in 0..num_pairs { |
| let arg_id = decoder.u32()?; |
| match arg_types.get(&arg_id).cloned().unwrap_or_default() { |
| ArgTypeSelector::Unknown => { |
| error!("Unknown argument id"); |
| return Err(ErrCode::InvalidArgument); |
| } |
| ArgTypeSelector::Bytes => { |
| let _ = args |
| .insert(arg_id, ArgValue::Bytes(decoder.bytes()?)) |
| .map_err(|_| ErrCode::OutOfMemory)?; |
| } |
| ArgTypeSelector::Int => { |
| let _ = args |
| .insert(arg_id, ArgValue::Int(decoder.u64()?)) |
| .map_err(|_| ErrCode::OutOfMemory)?; |
| } |
| ArgTypeSelector::Bool(_) => { |
| let _ = args |
| .insert(arg_id, ArgValue::Bool(decoder.bool()?)) |
| .map_err(|_| ErrCode::OutOfMemory)?; |
| } |
| ArgTypeSelector::Other => { |
| let start = decoder.position(); |
| decoder.skip()?; |
| let end = decoder.position(); |
| let _ = args |
| .insert( |
| arg_id, |
| encoded_args |
| .get(start..end) |
| .ok_or(ErrCode::InvalidCommand)? |
| .into(), |
| ) |
| .map_err(|_| ErrCode::OutOfMemory)?; |
| } |
| } |
| } |
| Ok(args) |
| } |
| |
| /// Decodes CBOR-encoded `encoded_args` according to the given `arg_types`. |
| /// |
| /// Arguments not present will be assigned the provided `arg_defaults`. |
| /// |
| /// # Errors |
| /// |
| /// * Returns `InvalidCommand` if the arguments cannot be decoded. |
| /// * Returns `InvalidArgument` if an argument encoding is not supported. |
| /// * Returns `OutOfMemory` if an argument is too large. |
| /// * Returns `InternalError` if a default value is missing. |
| pub(crate) fn decode_args<'a>( |
| encoded_args: &'a [u8], |
| arg_types: &ArgTypeMap, |
| ) -> DpeResult<ArgMap<'a>> { |
| let mut args = decode_args_internal(encoded_args, arg_types)?; |
| for (arg_id, arg_type) in arg_types { |
| if !args.contains_key(arg_id) { |
| let _ = args |
| .insert(*arg_id, arg_type.default_value()?) |
| .map_err(|_| ErrCode::OutOfMemory)?; |
| } |
| } |
| Ok(args) |
| } |
| |
| /// Encodes the `exported-cdi` output argument of the `DeriveContext` command. |
| pub(crate) fn encode_cdis_for_export( |
| cdi_sign: &Cdi, |
| cdi_seal: &Cdi, |
| encoded_cdis: &mut SmallMessage, |
| ) -> DpeResult<()> { |
| let _ = cbor_encoder_from_message(encoded_cdis) |
| .array(2)? |
| .bytes(cdi_sign.as_slice())? |
| .bytes(cdi_seal.as_slice())?; |
| Ok(()) |
| } |
| |
| /// Encodes a certificate chain for the `GetCertificateChain` command. |
| pub(crate) fn encode_certificate_chain( |
| certificates: &CertificateChain, |
| encoded_certificate_chain: &mut Message, |
| ) -> DpeResult<()> { |
| let mut encoder = cbor_encoder_from_message(encoded_certificate_chain); |
| let _ = encoder.array(certificates.0.len() as u64)?; |
| for certificate in &certificates.0 { |
| let _ = encoder.bytes(certificate.0.as_slice())?; |
| } |
| Ok(()) |
| } |
| |
| /// Decodes and removes a session message header. |
| /// |
| /// The session ID is returned and the remainder of the message remains in the |
| /// message buffer. |
| pub(crate) fn decode_and_remove_session_message_header( |
| message: &mut Message, |
| ) -> DpeResult<SessionId> { |
| let mut decoder = cbor_decoder_from_message(message); |
| // We're expecting a CBOR array with two elements, a session ID and the |
| // message content. |
| if !decoder.array().is_ok_and(|len| len == Some(MESSAGE_ARRAY_SIZE)) { |
| error!("Failed to decode session message"); |
| return Err(ErrCode::InvalidCommand); |
| } |
| let session_id = |
| SessionId(decoder.u16().map_err(|_| ErrCode::InvalidCommand)?.into()); |
| |
| let remainder_position = decoder.decode_bytes_prefix()?; |
| message.remove_prefix(remainder_position)?; |
| Ok(session_id) |
| } |
| |
| /// Encodes and inserts a session message header containing `session_id`. |
| pub(crate) fn encode_and_insert_session_message_header( |
| session_id: SessionId, |
| message: &mut Message, |
| ) -> DpeResult<()> { |
| let mut prefix = SmallMessage::new(); |
| let _ = cbor_encoder_from_message(&mut prefix) |
| .array(MESSAGE_ARRAY_SIZE)? |
| .u16(session_id.try_into()?)?; |
| encode_bytes_prefix(&mut prefix, message.len())?; |
| message.insert_prefix(prefix.as_slice())?; |
| Ok(()) |
| } |
| |
| /// Decodes and removes a command message header. |
| /// |
| /// The command ID is returned and the remainder of the message remains in the |
| /// message buffer. |
| pub(crate) fn decode_and_remove_command_header( |
| message: &mut Message, |
| ) -> DpeResult<CommandSelector> { |
| let mut decoder = cbor_decoder_from_message(message); |
| // We're expecting a CBOR array with two elements, a command ID and the |
| // message content. |
| let command_id: CommandSelector = decoder |
| .array() |
| .ok() |
| .and_then(|len| match len { |
| Some(MESSAGE_ARRAY_SIZE) => decoder.u32().ok(), |
| _ => None, |
| }) |
| .ok_or_else(|| { |
| error!("Failed to decode command message"); |
| ErrCode::InvalidCommand |
| })? |
| .try_into() |
| .map_err(|_| { |
| error!("Unknown command id"); |
| ErrCode::InvalidCommand |
| })?; |
| let remainder_position = decoder.decode_bytes_prefix()?; |
| message.remove_prefix(remainder_position)?; |
| Ok(command_id) |
| } |
| |
| /// Encodes and inserts a success response message header. |
| /// |
| /// If an error occurs use [`create_error_response`] instead. |
| pub(crate) fn encode_and_insert_response_header( |
| message: &mut Message, |
| ) -> DpeResult<()> { |
| let mut prefix = SmallMessage::new(); |
| let _ = cbor_encoder_from_message(&mut prefix) |
| .array(MESSAGE_ARRAY_SIZE)? |
| .u32(0)?; |
| encode_bytes_prefix(&mut prefix, message.len())?; |
| message.insert_prefix(prefix.as_slice())?; |
| Ok(()) |
| } |
| |
| /// Encodes a session ID into a handshake payload. |
| pub(crate) fn encode_handshake_payload( |
| session_id: SessionId, |
| ) -> DpeResult<HandshakePayload> { |
| let mut payload = HandshakePayload::new(); |
| let _ = cbor_encoder_from_message(&mut payload) |
| .u16(session_id.0.try_into()?)?; |
| Ok(payload) |
| } |
| |
| /// Encodes a profile descriptor containing only the profile name. |
| pub(crate) fn encode_profile_descriptor_from_name( |
| name: &str, |
| message: &mut Message, |
| ) -> DpeResult<()> { |
| const PROFILE_NAME_ATTRIBUTE: u32 = 1; |
| let _ = cbor_encoder_from_message(message) |
| .map(1)? |
| .u32(PROFILE_NAME_ATTRIBUTE)? |
| .str(name)?; |
| Ok(()) |
| } |