diff --git a/attestation-agent/attester/src/se/mod.rs b/attestation-agent/attester/src/se/mod.rs index 648a11640..c2cf347fa 100644 --- a/attestation-agent/attester/src/se/mod.rs +++ b/attestation-agent/attester/src/se/mod.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -use super::{Attester, TeeEvidence}; +use super::{utils::validate_and_pad_data, Attester, TeeEvidence}; use anyhow::*; use pv::{ misc, @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use serde_with::{base64::Base64, serde_as}; use tracing::debug; -const RUNTIME_DIGEST_SIZE: usize = 48; // SHA-384 digest size in bytes +const SE_REPORT_DATA_SIZE: usize = 64; // SHA-512 digest size in bytes /// Structured user data for IBM SEL attestation /// Currently, only contains runtime data digest bound to the attestation measurement @@ -105,25 +105,11 @@ impl Attester for SeAttester { runtime_digest ); - let runtime_digest = match runtime_digest.len() { - len if len > RUNTIME_DIGEST_SIZE => { - bail!( - "Invalid runtime_data_digest length: expected {} bytes (SHA-384), got {} (too large)", - RUNTIME_DIGEST_SIZE, - len - ); - } - len if len < RUNTIME_DIGEST_SIZE => { - debug!( - "Padding runtime_data_digest from {} to {} bytes with zeros", - len, RUNTIME_DIGEST_SIZE - ); - let mut padded = runtime_digest; - padded.resize(RUNTIME_DIGEST_SIZE, 0); - padded - } - _ => runtime_digest, // Exact match, use as-is - }; + let runtime_digest = validate_and_pad_data( + runtime_digest, + SE_REPORT_DATA_SIZE, + "runtime_data_digest (SHA-512)", + )?; let user_data = UserData { runtime_data_digest: runtime_digest, @@ -224,64 +210,55 @@ mod tests { #[tokio::test] async fn test_runtime_data_digest_with_various_sizes() { - // Helper function that mimics the validation/padding logic from lines 110-129 - fn process_runtime_digest(runtime_digest: Vec) -> Result> { - match runtime_digest.len() { - len if len > RUNTIME_DIGEST_SIZE => { - bail!( - "Invalid runtime_data_digest length: expected {} bytes (SHA-384), got {} (too large)", - RUNTIME_DIGEST_SIZE, - len - ) - } - len if len < RUNTIME_DIGEST_SIZE => { - let mut padded = runtime_digest; - padded.resize(RUNTIME_DIGEST_SIZE, 0); - Ok(padded) - } - _ => Ok(runtime_digest), // Exact match, use as-is - } - } - - // Case 1: Exact size (48 bytes) - should use as-is - let exact_digest = vec![0xAA; RUNTIME_DIGEST_SIZE]; - let result = process_runtime_digest(exact_digest.clone()).unwrap(); - assert_eq!(result.len(), RUNTIME_DIGEST_SIZE); + // Case 1: Exact size (64 bytes) - should use as-is + let exact_digest = vec![0xAA; SE_REPORT_DATA_SIZE]; + let result = validate_and_pad_data( + exact_digest.clone(), + SE_REPORT_DATA_SIZE, + "runtime_data_digest", + ) + .unwrap(); + assert_eq!(result.len(), SE_REPORT_DATA_SIZE); assert_eq!(result, exact_digest); - // Case 2: Too small (< 48 bytes) - should be padded with zeros + // Case 2: Small size (< 64 bytes) - should be padded with zeros let small_test_cases = vec![ (vec![], 0), // Empty digest (vec![0xAA], 1), // Single byte (vec![0xAA, 0xBB, 0xCC, 0xDD], 4), // 4 bytes (vec![0xFF; 32], 32), // 32 bytes (SHA-256 size) - (vec![0x11; 47], 47), // 47 bytes (just under limit) + (vec![0x11; 63], 63), // 63 bytes (just under limit) ]; for (small_digest, original_len) in small_test_cases { - let result = process_runtime_digest(small_digest.clone()).unwrap(); + let result = validate_and_pad_data( + small_digest.clone(), + SE_REPORT_DATA_SIZE, + "runtime_data_digest", + ) + .unwrap(); - // Verify result is padded to 48 bytes - assert_eq!(result.len(), RUNTIME_DIGEST_SIZE); + // Verify result is padded to 64 bytes + assert_eq!(result.len(), SE_REPORT_DATA_SIZE); // Verify original data is preserved at the beginning assert_eq!(&result[..original_len], &small_digest[..]); // Verify zeros are padded at the end - for i in original_len..RUNTIME_DIGEST_SIZE { + for i in original_len..SE_REPORT_DATA_SIZE { assert_eq!(result[i], 0, "Byte at index {} should be 0 (padded)", i); } } - // Case 3: Too large (> 48 bytes) - should return error + // Case 3: Large size (> 64 bytes) - should return error let large_test_cases = vec![ - (vec![0xFF; 49], 49), // 49 bytes (just over limit) - (vec![0xFF; 64], 64), // 64 bytes (SHA-512 size) + (vec![0xFF; 65], 65), // 65 bytes (just over limit) (vec![0xFF; 100], 100), // 100 bytes (way over limit) ]; for (large_digest, len) in large_test_cases { - let result = process_runtime_digest(large_digest); + let result = + validate_and_pad_data(large_digest, SE_REPORT_DATA_SIZE, "runtime_data_digest"); // Verify error is returned assert!( diff --git a/attestation-agent/attester/src/utils.rs b/attestation-agent/attester/src/utils.rs index 8cb9059b2..5eec212aa 100644 --- a/attestation-agent/attester/src/utils.rs +++ b/attestation-agent/attester/src/utils.rs @@ -8,6 +8,7 @@ use std::{env, path::Path}; use anyhow::{bail, Result}; use base64::{engine::general_purpose::STANDARD, Engine}; use tokio::{fs::File, io::AsyncReadExt}; +use tracing::debug; pub fn pad(input: &[u8]) -> [u8; T] { let mut output = [0; T]; @@ -20,6 +21,48 @@ pub fn pad(input: &[u8]) -> [u8; T] { output } +/// Validates and pads data to a specified size. +/// +/// This function ensures that the input data does not exceed the expected size, +/// and pads it with zeros if it's smaller than the expected size. +/// +/// # Arguments +/// +/// * `data` - The input data to validate and pad +/// * `expected_size` - The target size for the data +/// * `data_name` - A descriptive name for the data (used in error messages) +/// +/// # Returns +/// +/// * `Ok(Vec)` - The validated and padded data +/// * `Err(anyhow::Error)` - If the data exceeds the expected size +pub fn validate_and_pad_data( + data: Vec, + expected_size: usize, + data_name: &str, +) -> Result> { + match data.len() { + len if len > expected_size => { + bail!( + "Invalid {} length: expected {} bytes, got {} (too large)", + data_name, + expected_size, + len + ) + } + len if len < expected_size => { + debug!( + "Padding {} from {} to {} bytes with zeros", + data_name, len, expected_size + ); + let mut padded = data; + padded.resize(expected_size, 0); + Ok(padded) + } + _ => Ok(data), // Exact match, use as-is + } +} + /// This is a fixed eventlog header. If no CCEL is found, we will use this header ahead /// of aael eventlog. /// diff --git a/attestation-agent/kbs_protocol/src/client/rcar_client.rs b/attestation-agent/kbs_protocol/src/client/rcar_client.rs index 949b97f81..f45e24154 100644 --- a/attestation-agent/kbs_protocol/src/client/rcar_client.rs +++ b/attestation-agent/kbs_protocol/src/client/rcar_client.rs @@ -180,10 +180,6 @@ impl KbsClient> { // - injects runtime_data_digest into the request // The injected data will be used in SE attester's get_evidence method. Tee::Se => { - if !additional_evidence.is_empty() { - bail!("Cannot attest multiple devices on s390x platform.") - } - #[cfg(feature = "se-attester")] { use attester::se::SeAttestationRequest;