Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 20 additions & 55 deletions attestation-agent/attester/src/se/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -14,7 +14,10 @@ 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
/// The size for SE attestation report data (SHA-512 digest size in bytes).
/// Only the first `SE_REPORT_DATA_SIZE` bytes of the `runtime_data_digest` field
/// from `SeAttestationRequest` are used in the attestation command.
const SE_REPORT_DATA_SIZE: usize = 64;

/// Structured user data for IBM SEL attestation
/// Currently, only contains runtime data digest bound to the attestation measurement
Expand Down Expand Up @@ -105,25 +108,7 @@ 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)?;

let user_data = UserData {
runtime_data_digest: runtime_digest,
Expand Down Expand Up @@ -165,9 +150,9 @@ impl Attester for SeAttester {
mod tests {
use super::*;

// Mock runtime digest (SHA-384 size: 48 bytes) for tests
// Mock runtime digest (SHA-512 size: 64 bytes) for tests
// This simulates the digest computed from runtime data in production
const MOCK_RUNTIME_DIGEST: [u8; 48] = [0xBB; 48];
const MOCK_RUNTIME_DIGEST: [u8; 64] = [0xBB; 64];

// Helper to create a dummy BootHdrTags for testing
fn create_dummy_boot_hdr_tags() -> BootHdrTags {
Expand Down Expand Up @@ -224,64 +209,44 @@ 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<u8>) -> Result<Vec<u8>> {
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);
let exact_digest = vec![0xAA; SE_REPORT_DATA_SIZE];
let result = validate_and_pad_data(exact_digest.clone(), SE_REPORT_DATA_SIZE).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).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);

// Verify error is returned
assert!(
Expand Down
37 changes: 37 additions & 0 deletions attestation-agent/attester/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const T: usize>(input: &[u8]) -> [u8; T] {
let mut output = [0; T];
Expand All @@ -20,6 +21,42 @@ pub fn pad<const T: usize>(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
///
/// # Returns
///
/// * `Ok(Vec<u8>)` - The validated and padded data
/// * `Err(anyhow::Error)` - If the data exceeds the expected size
pub fn validate_and_pad_data(data: Vec<u8>, expected_size: usize) -> Result<Vec<u8>> {
match data.len() {
len if len > expected_size => {
bail!(
"Invalid data length: expected {} bytes, got {} (too large)",
expected_size,
len
)
}
len if len < expected_size => {
debug!(
"Padding data from {} to {} bytes with zeros",
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.
///
Expand Down
Loading