blob: b4ed0b6dea3962997c77c65588ae76099a61c688 [file] [log] [blame] [edit]
// Copyright 2025 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.
//! Tests for the encode module.
use crate::args::{ArgMap, ArgTypeMap, ArgTypeSelector, ArgValue};
use crate::cbor::tokenize_cbor_for_debug;
use crate::cbor::{
cbor_decoder_from_message, cbor_encoder_from_message, encode_bytes_prefix,
};
use crate::constants::*;
use crate::crypto::Hash;
use crate::dice::{
Cdi, Certificate, DiceInput, DiceInputAuthority, DiceInputCode,
DiceInputConfig, DiceInputMode, InternalInputType, Uds,
};
use crate::encode::*;
use crate::error::{DpeResult, ErrCode};
use crate::memory::{Message, SmallMessage};
use heapless::Vec;
use log::debug;
use minicbor::Decoder;
/// Encodes and logs input args.
pub(crate) fn encode_and_log_args(
args: &ArgMap,
encoded_args: &mut Message,
) -> DpeResult<()> {
encode_args(args, encoded_args)?;
debug!(
"Raw input args: {:?}",
tokenize_cbor_for_debug(encoded_args.as_slice())
);
Ok(())
}
/// Encodes and inserts a command message header containing `command_id`.
///
/// This function is for testing and panics on failure.
#[allow(clippy::unwrap_used)]
pub(crate) fn encode_and_insert_command_header(
command_id: CommandSelector,
message: &mut Message,
) {
let mut prefix = SmallMessage::new();
let _ = cbor_encoder_from_message(&mut prefix)
.array(MESSAGE_ARRAY_SIZE)
.unwrap()
.u32(command_id as u32)
.unwrap();
encode_bytes_prefix(&mut prefix, message.len()).unwrap();
message.insert_prefix(prefix.as_slice()).unwrap();
}
/// Decodes a command response.
///
/// This function is for testing and panics on failure to decode.
#[allow(clippy::unwrap_used)]
pub(crate) fn decode_response<'a>(
message: &'a Message,
arg_types: &ArgTypeMap,
) -> DpeResult<ArgMap<'a>> {
debug!("Raw response: {:?}", tokenize_cbor_for_debug(message.as_slice()));
let mut decoder = cbor_decoder_from_message(message);
assert_eq!(decoder.array().unwrap(), Some(MESSAGE_ARRAY_SIZE));
let err_code = decoder.u32().unwrap();
if err_code != 0 {
return Err(err_code.into());
}
assert_eq!(decoder.datatype().unwrap(), minicbor::data::Type::Bytes);
debug!(
"Raw output args: {:?}",
tokenize_cbor_for_debug(decoder.probe().bytes().unwrap())
);
Ok(decode_args_internal(decoder.bytes().unwrap(), arg_types).unwrap())
}
/// Decodes an error response.
///
/// This function is for testing and panics on failure to decode.
#[allow(clippy::unwrap_used)]
pub(crate) fn decode_error_response(message: &[u8]) -> DpeResult<()> {
let mut decoder = Decoder::new(message);
assert_eq!(decoder.array().unwrap(), Some(MESSAGE_ARRAY_SIZE));
let err_code = decoder.u32().unwrap();
assert_ne!(err_code, 0);
Err(err_code.into())
}
/// Decodes a certificate chain.
///
/// This function is for testing and panics on failure to decode.
#[allow(clippy::unwrap_used)]
pub(crate) fn decode_certificate_chain(encoded: &Message) -> CertificateChain {
let mut decoder = cbor_decoder_from_message(encoded);
let num_certs = decoder.array().unwrap().unwrap();
let mut chain: CertificateChain = Default::default();
for _ in 0..num_certs {
chain
.0
.push(Certificate(
Vec::from_slice(decoder.bytes().unwrap()).unwrap(),
))
.unwrap();
}
chain
}
/// Encodes a `seed` argument for the `InitializeContext` command.
///
/// This function is for testing and panics on failure to encode.
#[allow(unused_results, clippy::unwrap_used)]
pub(crate) fn encode_init_seed(
init_type: Option<InitTypeSelector>,
external_uds_seed: Option<&[u8]>,
cdi_sign: Option<&[u8]>,
cdi_seal: Option<&[u8]>,
) -> SmallMessage {
let mut map_size = 0;
if init_type.is_some() {
map_size += 1;
}
if external_uds_seed.is_some() {
map_size += 1;
}
if cdi_sign.is_some() {
map_size += 1;
}
if cdi_seal.is_some() {
map_size += 1;
}
let mut encoded_seed = SmallMessage::new();
let mut encoder = cbor_encoder_from_message(&mut encoded_seed);
encoder.map(map_size).unwrap();
if let Some(init_type) = init_type {
encoder.u8(InitTypeMapKey::InitType as u8).unwrap();
encoder.u8(init_type as u8).unwrap();
}
if let Some(seed) = external_uds_seed {
encoder.u8(InitTypeMapKey::ExternalSeed as u8).unwrap();
encoder.bytes(seed).unwrap();
}
if let Some(cdi) = cdi_sign {
encoder.u8(InitTypeMapKey::CdiSign as u8).unwrap();
encoder.bytes(cdi).unwrap();
}
if let Some(cdi) = cdi_seal {
encoder.u8(InitTypeMapKey::CdiSeal as u8).unwrap();
encoder.bytes(cdi).unwrap();
}
encoded_seed
}
/// Encodes a set of [`InternalInputType`]s for the `DeriveContext` command.
///
/// This function is for testing and panics on failure to encode.
#[allow(unused_results, clippy::unwrap_used)]
pub(crate) fn encode_internal_inputs(
inputs: &[InternalInputType],
) -> SmallMessage {
let mut encoded_inputs = SmallMessage::new();
let mut encoder = cbor_encoder_from_message(&mut encoded_inputs);
encoder.array(inputs.len() as u64).unwrap();
for input in inputs {
encoder.u32(*input as u32).unwrap();
}
encoded_inputs
}
/// Encodes a [`LocalityId`].
///
/// This function is for testing and panics on failure to encode.
#[allow(unused_results, clippy::unwrap_used)]
pub(crate) fn encode_locality(locality_id: LocalityId) -> SmallMessage {
let mut encoded_locality = SmallMessage::new();
cbor_encoder_from_message(&mut encoded_locality)
.u16(locality_id.try_into().unwrap())
.unwrap();
encoded_locality
}
/// Encodes an `input` argument for the `DeriveContext` command.
///
/// The version info indicates a slot and value to populate when processing
/// this input. This function is for testing and panics on failure to
/// encode.
pub(crate) fn encode_dice_input(
version_info: Option<(usize, u64)>,
dice_input: &DiceInput,
) -> Message {
encode_dice_input_with_error(
version_info,
dice_input,
DiceInputEncodingError::NoError,
)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
enum DiceInputEncodingError {
NoError,
OmitVersionSlot,
OmitVersionValue,
OmitCodeValue,
OmitConfigValue,
OmitModeValue,
MultipleVersionSlots,
MultipleVersionValues,
MultipleCodeValues,
MultipleConfigValues,
MultipleAuthorityValues,
MultipleModeValues,
MultipleHiddenValues,
ShortCodeHash,
ShortConfigInline,
ShortAuthorityHash,
ShortHidden,
LongCodeHash,
LongConfigInline,
LongAuthorityHash,
LongHidden,
InvalidVersionSlotType,
InvalidVersionValueType,
InvalidCodeType,
InvalidConfigType,
InvalidAuthorityType,
InvalidModeType,
InvalidHiddenType,
}
fn predict_dice_input_map_size(
has_version: bool,
has_authority: bool,
has_hidden: bool,
error: DiceInputEncodingError,
) -> u64 {
let mut map_size: u64 = 7;
if !has_version {
map_size -= 2;
} else if error == DiceInputEncodingError::OmitVersionSlot
|| error == DiceInputEncodingError::OmitVersionValue
{
map_size -= 1;
} else if error == DiceInputEncodingError::MultipleVersionSlots
|| error == DiceInputEncodingError::MultipleVersionValues
{
map_size += 1;
}
if !has_authority {
map_size -= 1;
} else if error == DiceInputEncodingError::MultipleAuthorityValues {
map_size += 1;
}
if !has_hidden {
map_size -= 1;
} else if error == DiceInputEncodingError::MultipleHiddenValues {
map_size += 1;
}
match error {
DiceInputEncodingError::OmitCodeValue
| DiceInputEncodingError::OmitConfigValue
| DiceInputEncodingError::OmitModeValue => {
map_size -= 1;
}
DiceInputEncodingError::MultipleCodeValues
| DiceInputEncodingError::MultipleConfigValues
| DiceInputEncodingError::MultipleModeValues => {
map_size += 1;
}
_ => {}
};
map_size
}
#[allow(unused_results, clippy::unwrap_used)]
fn encode_dice_input_with_error(
version_info: Option<(usize, u64)>,
dice_input: &DiceInput,
error: DiceInputEncodingError,
) -> Message {
let mut cbor = Message::new();
let mut encoder = cbor_encoder_from_message(&mut cbor);
let map_size = predict_dice_input_map_size(
version_info.is_some(),
dice_input.authority.is_some(),
dice_input.hidden.is_some(),
error,
);
let short_hash: [u8; 63] = [0; 63];
let long_hash: [u8; 65] = [0; 65];
encoder.map(map_size).unwrap();
if let Some(version_info) = version_info {
if error != DiceInputEncodingError::OmitVersionSlot {
encoder.u32(DiceInputMapKey::VersionSlot as u32).unwrap();
if error == DiceInputEncodingError::InvalidVersionSlotType {
encoder.bytes(&[version_info.0 as u8]).unwrap();
} else {
encoder.u8(version_info.0 as u8).unwrap();
}
if error == DiceInputEncodingError::MultipleVersionSlots {
encoder.u32(DiceInputMapKey::VersionSlot as u32).unwrap();
encoder.u8(version_info.0 as u8).unwrap();
}
}
if error != DiceInputEncodingError::OmitVersionValue {
encoder.u32(DiceInputMapKey::VersionValue as u32).unwrap();
if error == DiceInputEncodingError::InvalidVersionValueType {
encoder.bytes(&[version_info.1 as u8]).unwrap();
} else {
encoder.u64(version_info.1).unwrap();
}
if error == DiceInputEncodingError::MultipleVersionValues {
encoder.u32(DiceInputMapKey::VersionValue as u32).unwrap();
encoder.u64(version_info.1).unwrap();
}
}
}
if error != DiceInputEncodingError::OmitCodeValue {
match &dice_input.code {
DiceInputCode::CodeHash(hash) => {
encoder.u32(DiceInputMapKey::CodeHash as u32).unwrap();
if error == DiceInputEncodingError::InvalidCodeType {
encoder.u32(0).unwrap();
} else {
let value = match error {
DiceInputEncodingError::ShortCodeHash => {
short_hash.as_slice()
}
DiceInputEncodingError::LongCodeHash => {
long_hash.as_slice()
}
_ => hash.as_slice(),
};
encoder.bytes(value).unwrap();
if error == DiceInputEncodingError::MultipleCodeValues {
encoder
.u32(DiceInputMapKey::CodeDescriptor as u32)
.unwrap();
encoder.bytes(value).unwrap();
}
}
}
DiceInputCode::CodeDescriptor(descriptor) => {
encoder.u32(DiceInputMapKey::CodeDescriptor as u32).unwrap();
if error == DiceInputEncodingError::InvalidCodeType {
encoder.u32(0).unwrap();
} else {
encoder.bytes(descriptor).unwrap();
if error == DiceInputEncodingError::MultipleCodeValues {
encoder.u32(DiceInputMapKey::CodeHash as u32).unwrap();
encoder.bytes(descriptor).unwrap();
}
}
}
};
}
if error != DiceInputEncodingError::OmitConfigValue {
match &dice_input.config {
DiceInputConfig::ConfigInlineValue(hash) => {
encoder.u32(DiceInputMapKey::ConfigInlineValue as u32).unwrap();
if error == DiceInputEncodingError::InvalidConfigType {
encoder.u32(0).unwrap();
} else {
let value = match error {
DiceInputEncodingError::ShortConfigInline => {
short_hash.as_slice()
}
DiceInputEncodingError::LongConfigInline => {
long_hash.as_slice()
}
_ => hash.as_slice(),
};
encoder.bytes(value).unwrap();
if error == DiceInputEncodingError::MultipleConfigValues {
encoder
.u32(DiceInputMapKey::ConfigDescriptor as u32)
.unwrap();
encoder.bytes(value).unwrap();
}
}
}
DiceInputConfig::ConfigDescriptor(descriptor) => {
encoder.u32(DiceInputMapKey::ConfigDescriptor as u32).unwrap();
if error == DiceInputEncodingError::InvalidConfigType {
encoder.u32(0).unwrap();
} else {
encoder.bytes(descriptor).unwrap();
if error == DiceInputEncodingError::MultipleConfigValues {
encoder
.u32(DiceInputMapKey::ConfigInlineValue as u32)
.unwrap();
encoder.bytes(descriptor).unwrap();
}
}
}
}
}
if let Some(authority) = &dice_input.authority {
match authority {
DiceInputAuthority::AuthorityHash(hash) => {
encoder.u32(DiceInputMapKey::AuthorityHash as u32).unwrap();
if error == DiceInputEncodingError::InvalidAuthorityType {
encoder.u32(0).unwrap();
} else {
let value = match error {
DiceInputEncodingError::ShortAuthorityHash => {
short_hash.as_slice()
}
DiceInputEncodingError::LongAuthorityHash => {
long_hash.as_slice()
}
_ => hash.as_slice(),
};
encoder.bytes(value).unwrap();
if error == DiceInputEncodingError::MultipleAuthorityValues
{
encoder
.u32(DiceInputMapKey::AuthorityDescriptor as u32)
.unwrap();
encoder.bytes(value).unwrap();
}
}
}
DiceInputAuthority::AuthorityDescriptor(descriptor) => {
encoder
.u32(DiceInputMapKey::AuthorityDescriptor as u32)
.unwrap();
if error == DiceInputEncodingError::InvalidAuthorityType {
encoder.u32(0).unwrap();
} else {
encoder.bytes(descriptor).unwrap();
if error == DiceInputEncodingError::MultipleAuthorityValues
{
encoder
.u32(DiceInputMapKey::AuthorityHash as u32)
.unwrap();
encoder.bytes(descriptor).unwrap();
}
}
}
}
}
if error != DiceInputEncodingError::OmitModeValue {
encoder.u32(DiceInputMapKey::Mode as u32).unwrap();
if error == DiceInputEncodingError::InvalidModeType {
encoder.bytes(short_hash.as_slice()).unwrap();
} else {
encoder.u8(dice_input.mode as u8).unwrap();
if error == DiceInputEncodingError::MultipleModeValues {
encoder.u32(DiceInputMapKey::Mode as u32).unwrap();
encoder.u8(dice_input.mode as u8).unwrap();
}
}
}
if let Some(hidden) = &dice_input.hidden {
encoder.u32(DiceInputMapKey::Hidden as u32).unwrap();
if error == DiceInputEncodingError::InvalidHiddenType {
encoder.u32(0).unwrap();
} else {
let value = match error {
DiceInputEncodingError::ShortHidden => short_hash.as_slice(),
DiceInputEncodingError::LongHidden => long_hash.as_slice(),
_ => hidden.as_slice(),
};
encoder.bytes(value).unwrap();
if error == DiceInputEncodingError::MultipleHiddenValues {
encoder.u32(DiceInputMapKey::Hidden as u32).unwrap();
encoder.bytes(value).unwrap();
}
}
}
cbor
}
/// Encodes an unseal policy argument for the sealing commands.
#[allow(clippy::unwrap_used)]
pub(crate) fn encode_unseal_policy(
versions: &[u64; DPE_MAX_VERSION_SLOTS],
) -> SmallMessage {
let mut encoded_policy = SmallMessage::new();
let mut encoder = cbor_encoder_from_message(&mut encoded_policy);
let count = versions.iter().filter(|&&v| v != 0).count();
let _ = encoder.map(count as u64).unwrap();
for (slot, &value) in
versions.iter().enumerate().take(DPE_MAX_VERSION_SLOTS)
{
if value != 0 {
let _ = encoder.u16(slot as u16).unwrap();
let _ = encoder.u64(value).unwrap();
}
}
encoded_policy
}
fn test_init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn context_handle_decode() {
test_init();
assert_eq!(None, ContextHandle::from_slice_to_option(&[]).unwrap());
assert_ne!(
None,
ContextHandle::from_slice_to_option(&[0; DPE_HANDLE_SIZE]).unwrap()
);
assert_eq!(
ErrCode::InvalidArgument,
ContextHandle::from_slice_to_option(&[0; DPE_HANDLE_SIZE - 1])
.unwrap_err()
);
}
#[test]
fn internal_input_decode() {
test_init();
assert_eq!(InternalInputType::DpeInfo, 1u32.try_into().unwrap());
assert_eq!(
ErrCode::InvalidArgument,
<InternalInputType as TryFrom<u32>>::try_from(0).unwrap_err()
);
}
#[test]
fn command_selector_decode() {
test_init();
assert_eq!(CommandSelector::GetProfile, 1u32.try_into().unwrap());
assert_eq!(
ErrCode::InvalidCommand,
<CommandSelector as TryFrom<u32>>::try_from(0).unwrap_err()
);
}
#[test]
fn init_type_map_key_decode() {
test_init();
assert_eq!(InitTypeMapKey::InitType, 1u32.try_into().unwrap());
assert_eq!(
ErrCode::InvalidArgument,
<InitTypeMapKey as TryFrom<u32>>::try_from(0).unwrap_err()
);
}
#[test]
fn init_type_selector_decode() {
test_init();
assert_eq!(InitTypeSelector::Uds, 1u32.try_into().unwrap());
assert_eq!(
ErrCode::InvalidArgument,
<InitTypeSelector as TryFrom<u32>>::try_from(0).unwrap_err()
);
}
#[test]
fn dice_input_mode_decode() {
test_init();
assert_eq!(DiceInputMode::Normal, 1u8.try_into().unwrap());
assert_eq!(
ErrCode::InvalidArgument,
<DiceInputMode as TryFrom<u8>>::try_from(10).unwrap_err()
);
}
#[test]
fn dice_input_map_key_decode() {
test_init();
assert_eq!(DiceInputMapKey::VersionSlot, 1u32.try_into().unwrap());
assert_eq!(
ErrCode::InvalidArgument,
<DiceInputMapKey as TryFrom<u32>>::try_from(0).unwrap_err()
);
}
#[test]
fn internal_inputs_decode() {
test_init();
let before =
[InternalInputType::DpeInfo, InternalInputType::MonotonicCounter];
let after =
decode_internal_inputs(encode_internal_inputs(&before).as_slice())
.unwrap();
assert_eq!(before, after);
}
#[test]
fn locality_decode() {
test_init();
let default = LocalityId(0);
let before = LocalityId(0xFFFF);
let after =
decode_locality(encode_locality(before).as_slice(), default).unwrap();
assert_eq!(before, after);
assert_eq!(default, decode_locality(&[], default).unwrap());
}
#[test]
fn init_seed_decode() {
test_init();
let uds_value = Uds::from_array(&[0; DICE_UDS_SIZE]);
let cdi_sign = Cdi::from_array(&[1; DICE_CDI_SIZE]);
let cdi_seal = Cdi::from_array(&[2; DICE_CDI_SIZE]);
let invalid_seed = encode_init_seed(None, None, None, None);
let uds_internal_init = InitType::InternalUds;
let uds_internal_seed =
encode_init_seed(Some(InitTypeSelector::Uds), None, None, None);
let uds_external_init =
InitType::Uds { external_uds_seed: uds_value.clone() };
let uds_external_seed = encode_init_seed(
Some(InitTypeSelector::Uds),
Some(uds_value.as_slice()),
None,
None,
);
let cdi_internal_init = InitType::InternalCdis;
let cdi_internal_seed =
encode_init_seed(Some(InitTypeSelector::Cdi), None, None, None);
let cdi_external_init = InitType::Cdis {
cdi_sign: cdi_sign.clone(),
cdi_seal: cdi_seal.clone(),
};
let cdi_external_seed = encode_init_seed(
Some(InitTypeSelector::Cdi),
None,
Some(cdi_sign.as_slice()),
Some(cdi_seal.as_slice()),
);
let cdi_external_sign_init = InitType::Cdis {
cdi_sign: cdi_sign.clone(),
cdi_seal: cdi_sign.clone(),
};
let cdi_external_sign_seed = encode_init_seed(
Some(InitTypeSelector::Cdi),
None,
Some(cdi_sign.as_slice()),
None,
);
assert_eq!(
ErrCode::InvalidArgument,
decode_init_seed(invalid_seed.as_slice()).unwrap_err()
);
assert_eq!(
uds_internal_init,
decode_init_seed(uds_internal_seed.as_slice()).unwrap()
);
assert_eq!(
uds_external_init,
decode_init_seed(uds_external_seed.as_slice()).unwrap()
);
assert_eq!(
cdi_internal_init,
decode_init_seed(cdi_internal_seed.as_slice()).unwrap()
);
assert_eq!(
cdi_external_init,
decode_init_seed(cdi_external_seed.as_slice()).unwrap()
);
assert_eq!(
cdi_external_sign_init,
decode_init_seed(cdi_external_sign_seed.as_slice()).unwrap()
);
}
#[test]
fn dice_input_decode() {
test_init();
let version_info = (0, 1);
let invalid_version_info = (DPE_MAX_VERSION_SLOTS, 2);
let hash_value = Hash::from_array(&[0; HASH_SIZE]);
let descriptor_value = Message::new();
let hash_dice_input = DiceInput {
code: DiceInputCode::CodeHash(hash_value.clone()),
config: DiceInputConfig::ConfigInlineValue(hash_value.clone()),
authority: Some(DiceInputAuthority::AuthorityHash(hash_value.clone())),
mode: DiceInputMode::Normal,
hidden: Some(hash_value.clone()),
};
let descriptor_dice_input = DiceInput {
code: DiceInputCode::CodeDescriptor(descriptor_value.as_slice()),
config: DiceInputConfig::ConfigDescriptor(descriptor_value.as_slice()),
authority: Some(DiceInputAuthority::AuthorityDescriptor(
descriptor_value.as_slice(),
)),
mode: DiceInputMode::Normal,
hidden: Some(hash_value.clone()),
};
for dice_input in [&hash_dice_input, &descriptor_dice_input] {
// Success case
let encoded_dice_input = encode_dice_input(None, dice_input);
let (decoded_version_info, decoded_dice_input) =
decode_dice_input(encoded_dice_input.as_slice()).unwrap();
assert_eq!(*dice_input, decoded_dice_input);
assert_eq!(None, decoded_version_info);
let encoded_dice_input =
encode_dice_input(Some(version_info), dice_input);
let (decoded_version_info, decoded_dice_input) =
decode_dice_input(encoded_dice_input.as_slice()).unwrap();
assert_eq!(*dice_input, decoded_dice_input);
assert_eq!(Some(version_info), decoded_version_info);
let mut input_without_authority = dice_input.clone();
input_without_authority.authority = None;
let encoded_dice_input =
encode_dice_input(Some(version_info), dice_input);
let (decoded_version_info, decoded_dice_input) =
decode_dice_input(encoded_dice_input.as_slice()).unwrap();
assert_eq!(*dice_input, decoded_dice_input);
assert_eq!(Some(version_info), decoded_version_info);
let mut input_without_hidden = dice_input.clone();
input_without_hidden.hidden = None;
let encoded_dice_input =
encode_dice_input(Some(version_info), dice_input);
let (decoded_version_info, decoded_dice_input) =
decode_dice_input(encoded_dice_input.as_slice()).unwrap();
assert_eq!(*dice_input, decoded_dice_input);
assert_eq!(Some(version_info), decoded_version_info);
// Invalid version case
let encoded_dice_input =
encode_dice_input(Some(invalid_version_info), dice_input);
assert_eq!(
ErrCode::InvalidArgument,
decode_dice_input(encoded_dice_input.as_slice(),).unwrap_err()
);
// Encoding error cases
for error in [
DiceInputEncodingError::OmitVersionSlot,
DiceInputEncodingError::OmitVersionValue,
DiceInputEncodingError::OmitCodeValue,
DiceInputEncodingError::OmitConfigValue,
DiceInputEncodingError::OmitModeValue,
DiceInputEncodingError::MultipleVersionSlots,
DiceInputEncodingError::MultipleVersionValues,
DiceInputEncodingError::MultipleCodeValues,
DiceInputEncodingError::MultipleConfigValues,
DiceInputEncodingError::MultipleAuthorityValues,
DiceInputEncodingError::MultipleModeValues,
DiceInputEncodingError::MultipleHiddenValues,
DiceInputEncodingError::InvalidVersionSlotType,
DiceInputEncodingError::InvalidVersionValueType,
DiceInputEncodingError::InvalidCodeType,
DiceInputEncodingError::InvalidConfigType,
DiceInputEncodingError::InvalidAuthorityType,
DiceInputEncodingError::InvalidModeType,
DiceInputEncodingError::InvalidHiddenType,
] {
let encoded_dice_input = encode_dice_input_with_error(
Some(version_info),
dice_input,
error,
);
assert_eq!(
ErrCode::InvalidArgument,
decode_dice_input(encoded_dice_input.as_slice()).unwrap_err()
);
}
}
// Hash length error cases
for error in [
DiceInputEncodingError::ShortCodeHash,
DiceInputEncodingError::ShortConfigInline,
DiceInputEncodingError::ShortAuthorityHash,
DiceInputEncodingError::ShortHidden,
DiceInputEncodingError::LongCodeHash,
DiceInputEncodingError::LongConfigInline,
DiceInputEncodingError::LongAuthorityHash,
DiceInputEncodingError::LongHidden,
] {
let encoded_dice_input = encode_dice_input_with_error(
Some(version_info),
&hash_dice_input,
error,
);
assert_eq!(
ErrCode::InvalidArgument,
decode_dice_input(encoded_dice_input.as_slice()).unwrap_err()
);
}
}
#[test]
fn unseal_policy_decode() {
test_init();
let empty_versions = [0; DPE_MAX_VERSION_SLOTS];
let mut one_version = empty_versions.clone();
one_version[0] = 1;
let full_versions = [2; DPE_MAX_VERSION_SLOTS];
assert_eq!(
empty_versions,
decode_unseal_policy(encode_unseal_policy(&empty_versions).as_slice())
.unwrap()
);
assert_eq!(
one_version,
decode_unseal_policy(encode_unseal_policy(&one_version).as_slice())
.unwrap()
);
assert_eq!(
full_versions,
decode_unseal_policy(encode_unseal_policy(&full_versions).as_slice())
.unwrap()
);
let mut buffer = SmallMessage::new();
let mut encoder = cbor_encoder_from_message(&mut buffer);
let _ = encoder.begin_map().unwrap().end().unwrap();
assert_eq!(
ErrCode::InvalidArgument,
decode_unseal_policy(buffer.as_slice()).unwrap_err()
);
buffer.clear();
let mut encoder = cbor_encoder_from_message(&mut buffer);
let _ = encoder.map(DPE_MAX_VERSION_SLOTS as u64 + 1);
for i in 0..DPE_MAX_VERSION_SLOTS as u16 + 1 {
let _ = encoder.u16(i).unwrap().u64(100).unwrap();
}
assert_eq!(
ErrCode::InvalidArgument,
decode_unseal_policy(buffer.as_slice()).unwrap_err()
);
}
#[test]
fn check_error_response() {
let mut buffer = Message::new();
create_error_response(ErrCode::OutOfMemory, &mut buffer);
assert_eq!(
ErrCode::OutOfMemory,
decode_error_response(buffer.as_slice()).unwrap_err()
);
create_plaintext_session_error_response(ErrCode::Canceled, &mut buffer);
let mut decoder = cbor_decoder_from_message(&buffer);
assert_eq!(2, decoder.array().unwrap().unwrap());
assert_eq!(0, decoder.u16().unwrap());
assert_eq!(
ErrCode::Canceled,
decode_error_response(decoder.bytes().unwrap()).unwrap_err()
);
}
#[test]
fn encode_decode_args() {
let mut buffer = Message::new();
let empty: [u8; 0] = Default::default();
let small = [0xFF; 50];
let large = [0xAA; 2000];
let arg_map = ArgMap::from_iter([
(1, ArgValue::Bool(true)),
(2, ArgValue::Bytes(&empty)),
(3, ArgValue::Bytes(&small)),
(4, ArgValue::Bytes(&large)),
(5, ArgValue::Int(5)),
(6, ArgValue::Int(2)),
]);
let arg_types = ArgTypeMap::from_iter([
(4, ArgTypeSelector::Bytes),
(5, ArgTypeSelector::Int),
(6, ArgTypeSelector::Int),
(1, ArgTypeSelector::Bool(false)),
(2, ArgTypeSelector::Bytes),
(3, ArgTypeSelector::Bytes),
]);
encode_args(&arg_map, &mut buffer).unwrap();
{
let decoded_arg_map =
decode_args(buffer.as_slice(), &arg_types).unwrap();
assert_eq!(arg_map, decoded_arg_map);
// Since all fields are defined, defaults are superfluous.
let decoded_arg_map =
decode_args(buffer.as_slice(), &arg_types).unwrap();
assert_eq!(arg_map, decoded_arg_map);
}
let arg_map_empty = ArgMap::new();
let arg_map_with_defaults = ArgMap::from_iter(
arg_types
.clone()
.into_iter()
.map(|(id, arg_type)| (id, arg_type.default_value().unwrap())),
);
encode_args(&arg_map_empty, &mut buffer).unwrap();
{
let decoded_arg_map =
decode_args(buffer.as_slice(), &arg_types).unwrap();
assert_eq!(arg_map_with_defaults, decoded_arg_map);
}
}
#[test]
fn decode_invalid_args() {
// Empty bytes -> not valid CBOR map
assert_eq!(
ErrCode::InvalidCommand,
decode_args(&[], &ArgTypeMap::new()).unwrap_err()
);
let mut buffer = Message::new();
// CBOR array -> not valid CBOR map
let _ = cbor_encoder_from_message(&mut buffer)
.array(MESSAGE_ARRAY_SIZE)
.unwrap()
.bool(true)
.unwrap()
.u16(7)
.unwrap();
let arg_types = ArgTypeMap::from_iter([(1, ArgTypeSelector::Bool(false))]);
assert_eq!(
ErrCode::InvalidCommand,
decode_args(buffer.as_slice(), &arg_types).unwrap_err()
);
buffer.clear();
// CBOR indefinite map -> should be not supported
let _ = cbor_encoder_from_message(&mut buffer)
.begin_map()
.unwrap()
.end()
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_args(buffer.as_slice(), &arg_types).unwrap_err()
);
// All args must be represented in arg types
let unknown_arg = ArgMap::from_iter([(17, ArgValue::Int(17))]);
encode_args(&unknown_arg, &mut buffer).unwrap();
assert_eq!(
ErrCode::InvalidArgument,
decode_args(buffer.as_slice(), &arg_types).unwrap_err()
);
}
#[test]
fn cdi_export_encode() {
let cdi1 = Cdi::from_array(&[0xAA; DICE_CDI_SIZE]);
let cdi2 = Cdi::from_array(&[0xBB; DICE_CDI_SIZE]);
let mut buffer = SmallMessage::new();
encode_cdis_for_export(&cdi1, &cdi2, &mut buffer).unwrap();
let mut decoder = cbor_decoder_from_message(&buffer);
assert_eq!(decoder.array().unwrap().unwrap(), 2);
assert_eq!(cdi1, Cdi::from_slice(decoder.bytes().unwrap()).unwrap());
assert_eq!(cdi2, Cdi::from_slice(decoder.bytes().unwrap()).unwrap());
}
#[test]
fn certificate_chain_encode() {
let cert1 = Certificate(
Vec::from_slice(&[0x11; DPE_MAX_CERTIFICATE_SIZE]).unwrap(),
);
let cert2 = Certificate(Vec::from_slice(&[0x22; 1]).unwrap());
let cert3: Certificate = Default::default();
let chain = CertificateChain(
Vec::from_slice(&[cert1.clone(), cert2.clone(), cert3.clone()])
.unwrap(),
);
let mut buffer = Message::new();
encode_certificate_chain(&chain, &mut buffer).unwrap();
assert_eq!(chain, decode_certificate_chain(&buffer));
}
#[test]
fn message_header() {
let session_id = SessionId(25);
let content = [0u8; 100];
let mut buffer = Message::from_slice(&content).unwrap();
encode_and_insert_session_message_header(session_id, &mut buffer).unwrap();
assert!(content.len() < buffer.len());
assert_eq!(
session_id,
decode_and_remove_session_message_header(&mut buffer).unwrap()
);
assert_eq!(content, buffer.as_slice());
}
#[test]
fn decode_invalid_message_header() {
let mut buffer = Message::new();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_session_message_header(&mut buffer).unwrap_err()
);
let mut buffer = Message::from_slice(&[0; 14]).unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_session_message_header(&mut buffer).unwrap_err()
);
// Add an unexpected array element.
buffer.clear();
let _ = cbor_encoder_from_message(&mut buffer)
.array(3)
.unwrap()
.u32(1)
.unwrap()
.u32(2)
.unwrap()
.u32(3)
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_session_message_header(&mut buffer).unwrap_err()
);
// Use an invalid id type.
buffer.clear();
let _ = cbor_encoder_from_message(&mut buffer)
.array(MESSAGE_ARRAY_SIZE)
.unwrap()
.bytes(&[])
.unwrap()
.u32(2)
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_session_message_header(&mut buffer).unwrap_err()
);
}
#[test]
fn command_header() {
let command_id = CommandSelector::RotateContextHandle;
let content = [0u8; 100];
let mut buffer = Message::from_slice(&content).unwrap();
encode_and_insert_command_header(command_id, &mut buffer);
assert!(content.len() < buffer.len());
assert_eq!(
command_id,
decode_and_remove_command_header(&mut buffer).unwrap()
);
assert_eq!(content, buffer.as_slice());
}
#[test]
fn decode_invalid_command_header() {
let mut buffer = Message::new();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_command_header(&mut buffer).unwrap_err()
);
let mut buffer = Message::from_slice(&[0; 14]).unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_command_header(&mut buffer).unwrap_err()
);
// Add an unexpected array element.
buffer.clear();
let _ = cbor_encoder_from_message(&mut buffer)
.array(3)
.unwrap()
.u32(1)
.unwrap()
.u32(2)
.unwrap()
.u32(3)
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_command_header(&mut buffer).unwrap_err()
);
// Use an invalid id type.
buffer.clear();
let _ = cbor_encoder_from_message(&mut buffer)
.array(MESSAGE_ARRAY_SIZE)
.unwrap()
.bytes(&[])
.unwrap()
.u32(2)
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_command_header(&mut buffer).unwrap_err()
);
// Use an invalid command id.
buffer.clear();
let _ = cbor_encoder_from_message(&mut buffer)
.array(MESSAGE_ARRAY_SIZE)
.unwrap()
.u32(1000)
.unwrap()
.u32(2)
.unwrap();
assert_eq!(
ErrCode::InvalidCommand,
decode_and_remove_command_header(&mut buffer).unwrap_err()
);
}
#[test]
fn response_header() {
let content = [0u8; 100];
let mut buffer = Message::from_slice(&content).unwrap();
encode_and_insert_response_header(&mut buffer).unwrap();
assert!(content.len() < buffer.len());
let mut decoder = cbor_decoder_from_message(&buffer);
assert_eq!(2, decoder.array().unwrap().unwrap());
assert_eq!(0, decoder.u32().unwrap());
assert_eq!(content, decoder.bytes().unwrap());
let overflow_content = [0u8; MAX_MESSAGE_SIZE - 2];
let mut buffer = Message::from_slice(&overflow_content).unwrap();
assert_eq!(
ErrCode::OutOfMemory,
encode_and_insert_response_header(&mut buffer).unwrap_err()
);
}
#[test]
fn profile_descriptor() {
let name = "test";
let mut buffer = Message::new();
encode_profile_descriptor_from_name(name, &mut buffer).unwrap();
let mut decoder = cbor_decoder_from_message(&buffer);
assert_eq!(1, decoder.map().unwrap().unwrap());
assert_eq!(1, decoder.u32().unwrap());
assert_eq!(name, decoder.str().unwrap());
}
#[test]
fn handshake_payload() {
let session_id = SessionId(11);
let payload = encode_handshake_payload(session_id).unwrap();
let mut decoder = cbor_decoder_from_message(&payload);
assert_eq!(session_id.0 as u16, decoder.u16().unwrap());
}