pvattest: Use hybrid keys

Allow the creation of Attestation requests using hybrid (=quantum safe)
keys. This results in using the headers in version 2 (0x200).

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Timo Keller
2026-07-21 17:05:00 +02:00
committed by Steffen Eiden
parent fc853f3259
commit 9dca2d3181
6 changed files with 348 additions and 18 deletions

View File

@@ -4,6 +4,7 @@
use std::mem::size_of; use std::mem::size_of;
use pv_core::static_assert;
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32}; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE}; use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
@@ -14,7 +15,7 @@ use crate::misc::Flags;
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx}; use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize}; use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
use crate::uv::UvFlags; use crate::uv::UvFlags;
use crate::{assert_size, static_assert, Error, Result}; use crate::{assert_size, Error, Result};
#[cfg(doc)] #[cfg(doc)]
use crate::{ use crate::{
request::SymKeyType, request::SymKeyType,
@@ -62,7 +63,7 @@ use crate::{
/// let hkd = hkd.first().unwrap().public_key()?; /// let hkd = hkd.first().unwrap().public_key()?;
/// arcb.add_hostkey(HostKey::V1(hkd)); /// arcb.add_hostkey(HostKey::V1(hkd));
/// // you can add multiple hostkeys /// // you can add multiple hostkeys
/// // arcb.add_hostkey(another_hkd); /// // arcb.add_hostkey(HostKey::V1(another_hkd));
/// // encrypt it /// // encrypt it
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?; /// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
/// let arcb = arcb.encrypt(&ctx)?; /// let arcb = arcb.encrypt(&ctx)?;
@@ -106,7 +107,13 @@ impl AttestationRequest {
/// Returns a reference to the flags of this [`AttestationRequest`]. /// Returns a reference to the flags of this [`AttestationRequest`].
pub fn flags(&self) -> &AttestationFlags { pub fn flags(&self) -> &AttestationFlags {
&self.aad.flags self.aad.flags()
}
/// Returns the request version, derived from the type of added host-keys.
/// Returns [`AttestationVersion::One`] if no host-keys have been added yet.
pub fn version(&self) -> AttestationVersion {
self.version
} }
/// Returns a copy of the confidential data of this [`AttestationRequest`]. /// Returns a copy of the confidential data of this [`AttestationRequest`].
@@ -142,6 +149,7 @@ impl AttestationRequest {
let values = BinReqValues::get(arcb)?; let values = BinReqValues::get(arcb)?;
match values.version().try_into()? { match values.version().try_into()? {
AttestationVersion::One => (), AttestationVersion::One => (),
AttestationVersion::Two => (),
}; };
Ok(values) Ok(values)
@@ -246,6 +254,8 @@ impl Request for AttestationRequest {
pub enum AttestationVersion { pub enum AttestationVersion {
/// Version 1 (= 0x0100) /// Version 1 (= 0x0100)
One = 0x0100, One = 0x0100,
/// Version 2 (= 0x0200)
Two = 0x0200,
} }
impl TryFrom<u32> for AttestationVersion { impl TryFrom<u32> for AttestationVersion {
@@ -254,6 +264,8 @@ impl TryFrom<u32> for AttestationVersion {
fn try_from(value: u32) -> Result<Self> { fn try_from(value: u32) -> Result<Self> {
if value == Self::One as u32 { if value == Self::One as u32 {
Ok(Self::One) Ok(Self::One)
} else if value == Self::Two as u32 {
Ok(Self::Two)
} else { } else {
Err(Error::BinArcbInvVersion(value)) Err(Error::BinArcbInvVersion(value))
} }
@@ -412,8 +424,8 @@ impl Zeroize for ReqConfData {
mod test { mod test {
use super::*; use super::*;
use crate::get_test_asset; use crate::get_test_asset;
use crate::request::SymKey; use crate::request::{HybridPKey, SymKey};
use crate::test_utils::get_test_keys; use crate::test_utils::{get_test_keys, get_test_keys_hybrid};
const ARPK: [u8; 32] = [0x17; 32]; const ARPK: [u8; 32] = [0x17; 32];
const NONCE: [u8; 16] = [0xab; 16]; const NONCE: [u8; 16] = [0xab; 16];
@@ -421,6 +433,8 @@ mod test {
fn mk_arcb() -> Vec<u8> { fn mk_arcb() -> Vec<u8> {
let (cust_key, host_key) = get_test_keys(); let (cust_key, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256( let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]), Some([0x55; 12]),
Some(cust_key), Some(cust_key),
@@ -443,7 +457,37 @@ mod test {
arcb.conf.value_mut().nonce = NONCE; arcb.conf.value_mut().nonce = NONCE;
arcb.conf.value_mut().meas_key = MEAS; arcb.conf.value_mut().meas_key = MEAS;
arcb.add_hostkey(HostKey::V1(host_key)); arcb.add_hostkey(host_key);
arcb.encrypt(&ctx).unwrap()
}
fn mk_arcb_v2() -> Vec<u8> {
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]),
Some(cust_key),
Some(SymKey::Aes256(ARPK.into())),
)
.unwrap();
let mut flags = AttestationFlags::default();
flags.set_image_phkh();
flags.set_attest_phkh();
let mut arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
flags,
)
.unwrap();
// manually set confidential data (API does not allow this)
arcb.conf.value_mut().nonce = NONCE;
arcb.conf.value_mut().meas_key = MEAS;
arcb.add_hostkey(host_key);
arcb.encrypt(&ctx).unwrap() arcb.encrypt(&ctx).unwrap()
} }
@@ -455,6 +499,32 @@ mod test {
assert_eq!(request, exp); assert_eq!(request, exp);
} }
#[test]
fn arcb_v2() {
let request = mk_arcb_v2();
// Expected bytes for a V2 ARCB: rqvn = 0x0200 (bytes 8-11), rest deterministic.
// The first 288 bytes cover header + customer-public-key + one V2 keyslot header.
let exp: [u8; 288] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 7, 208, 85, 85, 85, 85, 85, 85, 85, 85, 85,
85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 80, 112, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199,
93, 52, 249, 22, 82, 219, 69, 123, 11, 32, 156, 70, 164, 145, 164, 78, 226, 177, 110,
35, 194, 216, 218, 241, 22, 103, 138, 98, 242, 76, 227, 50, 197, 153, 95, 8, 69, 107,
102, 177, 109, 213, 90, 146, 197, 7, 241, 227, 26, 247, 140, 100, 168, 46, 122, 84, 27,
21, 19, 80, 21, 242, 2, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 128, 88,
167, 241, 165, 195, 80, 151, 83, 58, 2, 169, 56, 121, 231, 222, 103, 186, 40, 11, 206,
131, 101, 236, 148, 178, 185, 8, 245, 137, 195, 169, 152, 216, 190, 30, 99, 7, 215, 74,
224, 26, 220, 70, 130, 95, 246, 187, 111, 160, 92, 17, 71, 207, 226, 204, 244, 162, 79,
61, 131, 61, 218, 112, 255, 94, 191, 53, 220, 196, 47, 37, 93, 227, 234, 101, 1, 174,
171, 68, 42, 136, 92, 238, 72, 6, 17, 77, 231, 225, 174, 22, 222, 188, 212, 15, 248,
145, 72, 126, 139, 17, 233, 225, 156, 46, 233, 151, 54, 2, 175, 88, 215, 254, 243, 222,
37, 81, 50, 110, 18, 76, 252, 12, 210, 146, 66, 23,
];
// only compare non-randomized part
assert_eq!(request[..288], exp[..288]);
}
#[test] #[test]
fn auth_bin() { fn auth_bin() {
let request = mk_arcb(); let request = mk_arcb();
@@ -464,6 +534,15 @@ mod test {
assert_eq!(exp, auth_bin.as_bytes()); assert_eq!(exp, auth_bin.as_bytes());
} }
#[test]
fn auth_bin_v2() {
let request = mk_arcb_v2();
let auth_bin = AttestationRequest::auth_bin(&request).unwrap();
let exp = &request[0x30..0x40];
assert_eq!(exp, auth_bin.as_bytes());
}
#[test] #[test]
fn decrypt_bin() { fn decrypt_bin() {
let request = mk_arcb(); let request = mk_arcb();
@@ -473,6 +552,99 @@ mod test {
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE); assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
} }
#[test]
fn decrypt_bin_v2() {
let request = mk_arcb_v2();
let arpk = SymKey::Aes256(ARPK.into());
let (_, conf) = AttestationRequest::decrypt_bin(&request, &arpk).unwrap();
assert_eq!(conf.measurement_key(), &MEAS);
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
}
#[test]
fn arcb_v1_version() {
// Without any host-keys, version defaults to One
let arcb = AttestationRequest::new(
AttestationVersion::One,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
assert_eq!(arcb.version(), AttestationVersion::One);
}
#[test]
fn arcb_v2_version() {
// After adding a V2 host-key, version is Two
let mut arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
let (_, host_key1, host_key2) = get_test_keys_hybrid();
arcb.add_hostkey(HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()));
assert_eq!(arcb.version(), AttestationVersion::Two);
}
#[test]
fn attestation_version_try_from() {
// Test version conversion from u32
assert_eq!(
AttestationVersion::try_from(0x0100).unwrap(),
AttestationVersion::One
);
assert_eq!(
AttestationVersion::try_from(0x0200).unwrap(),
AttestationVersion::Two
);
// Invalid version should error
assert!(AttestationVersion::try_from(0x0300).is_err());
}
#[test]
fn attestation_flags_expected_size() {
// Test expected additional data size calculation for V1
let mut flags = AttestationFlags::default();
// Image PHKH flag - should be 32 bytes
flags.set_image_phkh();
assert_eq!(flags.expected_additional_size(), 32);
// Add attest PHKH flag - should be 64 bytes (32 + 32)
flags.set_attest_phkh();
assert_eq!(flags.expected_additional_size(), 64);
// Add secret store hash - should be 128 bytes (64 + 64)
flags.set_secret_store_hash();
assert_eq!(flags.expected_additional_size(), 128);
// Add firmware state - should be 448 bytes (128 + 320)
flags.set_firmware_state();
assert_eq!(flags.expected_additional_size(), 448);
}
#[test]
fn confidential_data_v2() {
// Test confidential data extraction (version-independent)
let arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
let conf = arcb.confidential_data();
// Should have measurement key and nonce
assert_eq!(conf.measurement_key().len(), 64);
assert!(conf.nonce().is_some());
assert_eq!(conf.nonce().as_ref().unwrap().value().len(), 16);
}
#[test] #[test]
fn decrypt_bin_fail_magic() { fn decrypt_bin_fail_magic() {
let arpk = SymKey::Aes256(ARPK.into()); let arpk = SymKey::Aes256(ARPK.into());
@@ -484,6 +656,17 @@ mod test {
assert!(matches!(ret, Err(Error::NoArcb))); assert!(matches!(ret, Err(Error::NoArcb)));
} }
#[test]
fn decrypt_bin_fail_magic_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper magic
tamp_arcb[0] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::NoArcb)));
}
#[test] #[test]
fn decrypt_bin_fail_mai() { fn decrypt_bin_fail_mai() {
let arpk = SymKey::Aes256(ARPK.into()); let arpk = SymKey::Aes256(ARPK.into());
@@ -499,6 +682,21 @@ mod test {
)); ));
} }
#[test]
fn decrypt_bin_fail_mai_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper MAI
tamp_arcb[0x3b] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
println!("{ret:?}");
assert!(matches!(
ret,
Err(Error::PvCore(pv_core::Error::BinArcbInvAlgorithm(17)))
));
}
#[test] #[test]
fn decrypt_bin_fail_aad() { fn decrypt_bin_fail_aad() {
let arpk = SymKey::Aes256(ARPK.into()); let arpk = SymKey::Aes256(ARPK.into());
@@ -509,4 +707,15 @@ mod test {
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk); let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::GcmTagMismatch))); assert!(matches!(ret, Err(Error::GcmTagMismatch)));
} }
#[test]
fn decrypt_bin_fail_aad_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper AAD
tamp_arcb[0x3c] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
}
} }

View File

@@ -6,6 +6,7 @@ use std::mem::size_of;
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
use openssl::pkey::{PKeyRef, Private}; use openssl::pkey::{PKeyRef, Private};
use pv_core::misc::write_file;
use zerocopy::{BigEndian, IntoBytes, U16, U32}; use zerocopy::{BigEndian, IntoBytes, U16, U32};
use super::AttNonce; use super::AttNonce;
@@ -101,6 +102,7 @@ impl AttestationItems {
} }
items.extend_from_slice(additional); items.extend_from_slice(additional);
assert!(items.len() == size); assert!(items.len() == size);
write_file("additional_data_parsed", &items, "blah").unwrap();
Self(items.into()) Self(items.into())
} }
} }

View File

@@ -5,7 +5,10 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint}; use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint};
use utils::{CertificateOptions, DeprecatedVerbosityOptions}; use utils::{
AutoOrExplicit, AutoOrExplicitParser, CertificateOptions, DeprecatedVerbosityOptions,
HkdVersion, ValueEnumDisplay, ValueEnumFromStr,
};
/// create, perform, and verify attestation measurements /// create, perform, and verify attestation measurements
/// ///
@@ -67,6 +70,29 @@ pub enum Command {
Version, Version,
} }
/// Secure Execution attestation version for CLI
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, ValueEnumDisplay, ValueEnumFromStr)]
pub enum AttVersion {
#[value(name = "1")]
/// Version 1 - uses traditional cryptographic keys
V1,
#[value(name = "2")]
/// Version 2 - uses hybrid (post-quantum) cryptographic keys
V2,
}
pub type AttVersionSelection = AutoOrExplicit<AttVersion>;
pub type AttVersionSelectionParser = AutoOrExplicitParser<AttVersion>;
impl From<AttVersion> for HkdVersion {
fn from(val: AttVersion) -> Self {
match val {
AttVersion::V1 => Self::Classical,
AttVersion::V2 => Self::Hybrid,
}
}
}
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub struct CreateAttOpt { pub struct CreateAttOpt {
#[command(flatten)] #[command(flatten)]
@@ -94,6 +120,10 @@ pub struct CreateAttOpt {
value_delimiter = ',' value_delimiter = ','
)] )]
pub add_data: Vec<AttAddFlags>, pub add_data: Vec<AttAddFlags>,
/// Specify the Attestation Request version to use.
#[arg(long = "att-version", value_name = "VERSION", default_value_t = AttVersionSelection::Explicit(AttVersion::V1), value_parser = AttVersionSelectionParser::default())]
pub att_version: AttVersionSelection,
} }
#[derive(Debug, ValueEnum, Clone, Copy)] #[derive(Debug, ValueEnum, Clone, Copy)]

View File

@@ -12,7 +12,7 @@ pub use check::check;
pub use create::create; pub use create::create;
pub use verify::verify; pub use verify::verify;
pub const CMD_FN: &[&str] = &["+create", "+verify"]; pub const CMD_FN: &[&str] = &["+create", "+verify", "+quantumsafe"];
// s390 branch // s390 branch
#[cfg(target_arch = "s390x")] #[cfg(target_arch = "s390x")]
mod uv_cmd { mod uv_cmd {

View File

@@ -10,7 +10,7 @@ use pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, Attes
use pv::misc::{create_file, write_file}; use pv::misc::{create_file, write_file};
use pv::request::{HostKey, ReqEncrCtx, Request, SymKey, SymKeyType}; use pv::request::{HostKey, ReqEncrCtx, Request, SymKey, SymKeyType};
use crate::cli::{AttAddFlags, CreateAttOpt}; use crate::cli::{AttAddFlags, AttVersion, AttVersionSelection, CreateAttOpt};
use crate::exchange::{ExchangeFormatRequest, ExchangeFormatVersion}; use crate::exchange::{ExchangeFormatRequest, ExchangeFormatVersion};
fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags { fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
@@ -26,18 +26,54 @@ fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
att_flags att_flags
} }
/// Auto-detect the attestation version based on the host keys.
///
/// Returns Two if any host key is a hybrid key, otherwise returns V1.
fn auto_detect_version(host_keys: &[HostKey]) -> AttestationVersion {
let use_hybrid_keys = host_keys.iter().any(|k: &HostKey| k.is_hybrid());
if use_hybrid_keys {
AttestationVersion::Two
} else {
AttestationVersion::One
}
}
impl From<AttVersion> for AttestationVersion {
fn from(value: AttVersion) -> Self {
match value {
AttVersion::V1 => Self::One,
AttVersion::V2 => Self::Two,
}
}
}
/// Determine the attestation version to use.
///
/// If an explicit version is provided via CLI, use that.
/// Otherwise, auto-detect based on the host key types.
fn determine_version(
cli_version: AttVersionSelection,
host_keys: &[HostKey],
) -> AttestationVersion {
match cli_version {
AttVersionSelection::Auto => auto_detect_version(host_keys),
AttVersionSelection::Explicit(att_version) => att_version.into(),
}
}
pub fn create(opt: &CreateAttOpt) -> Result<ExitCode> { pub fn create(opt: &CreateAttOpt) -> Result<ExitCode> {
let att_version = AttestationVersion::One; let hkds = opt
.certificate_args
.get_verified_hkds_new("attestation request", opt.att_version.map(|v| v.into()))?;
let att_version = determine_version(opt.att_version, &hkds);
let meas_alg = AttestationMeasAlg::HmacSha512; let meas_alg = AttestationMeasAlg::HmacSha512;
let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?; let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?;
debug!("Generated Attestation request"); debug!("Generated Attestation request");
// Add host-key documents // Add host-key documents
opt.certificate_args hkds.into_iter().for_each(|k| arcb.add_hostkey(k));
.get_verified_hkds("attestation request")?
.into_iter()
.for_each(|k| arcb.add_hostkey(HostKey::V1(k)));
debug!("Added all host-keys"); debug!("Added all host-keys");
let encr_ctx = let encr_ctx =

View File

@@ -11,6 +11,8 @@ use pv::request::MagicValue;
use pv::uv::{AttestationCmd, ConfigUid}; use pv::uv::{AttestationCmd, ConfigUid};
use zerocopy::{BigEndian, ByteOrder, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64}; use zerocopy::{BigEndian, ByteOrder, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
use crate::additional;
const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:"; const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:";
#[repr(C)] #[repr(C)]
@@ -99,10 +101,18 @@ impl ExchangeFormatV1Hdr {
let measurement_entry = Entry::from_exp(Some(measurement)); let measurement_entry = Entry::from_exp(Some(measurement));
let exp_add = match additional { let exp_add = match additional {
0 => None, 0 => None,
size => Some(size), size => {
if size > AttestationCmd::ADDITIONAL_MAX_SIZE {
bail!(
"Additional data size ({}) exceeds maximum allowed size ({})",
size,
AttestationCmd::ADDITIONAL_MAX_SIZE
);
}
Some(size)
}
}; };
// TODO min and max size check? let additional_entry = Entry::from_exp(exp_add);
let additional_entry = Entry::from_exp(exp_add); //, AttestationCmd::ADDITIONAL_MAX_SIZE, &mut offset);
let user_entry = Entry::from_none(); let user_entry = Entry::from_none();
let cuid_entry = Entry::from_none(); let cuid_entry = Entry::from_none();
@@ -530,7 +540,6 @@ impl ExchangeFormatResponse {
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.", "{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.",
))?; ))?;
// TODO remove unwrap
let measurement = hdr.measurement.read(reader)?.data().ok_or(anyhow!( let measurement = hdr.measurement.read(reader)?.data().ok_or(anyhow!(
"{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (Measurement missing).", "{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (Measurement missing).",
))?; ))?;
@@ -693,6 +702,50 @@ mod test {
ExchangeFormatRequest::new(ARCB.to_vec(), 0, ADDITIONAL.len() as u32).unwrap_err(); ExchangeFormatRequest::new(ARCB.to_vec(), 0, ADDITIONAL.len() as u32).unwrap_err();
} }
#[test]
fn test_additional_data_size_validation() {
// Test for TODO 1 fix: Additional data size validation
let arcb = ARCB.to_vec();
// Test with valid size at maximum
let result = ExchangeFormatV1Hdr::new_request(
&arcb,
MEASUREMENT.len() as u32,
AttestationCmd::ADDITIONAL_MAX_SIZE,
);
assert!(
result.is_ok(),
"Maximum additional data size should be accepted"
);
// Test with size exceeding maximum
let result = ExchangeFormatV1Hdr::new_request(
&arcb,
MEASUREMENT.len() as u32,
AttestationCmd::ADDITIONAL_MAX_SIZE + 1,
);
assert!(
result.is_err(),
"Additional data size exceeding maximum should fail"
);
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("exceeds maximum"),
"Error should mention exceeding maximum: {}",
error_msg
);
}
// Test with zero size (no additional data)
let result = ExchangeFormatV1Hdr::new_request(&arcb, MEASUREMENT.len() as u32, 0);
assert!(
result.is_ok(),
"Zero additional data size should be accepted"
);
}
#[test] #[test]
fn min_req() { fn min_req() {
test_read_write_request( test_read_write_request(