Skip to content
Draft
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
85 changes: 31 additions & 54 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,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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<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);
// 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!(
Expand Down
43 changes: 43 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,48 @@ 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
/// * `data_name` - A descriptive name for the data (used in error messages)
///
/// # 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,
data_name: &str,
) -> Result<Vec<u8>> {
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.
///
Expand Down
4 changes: 0 additions & 4 deletions attestation-agent/kbs_protocol/src/client/rcar_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,6 @@ impl KbsClient<Box<dyn EvidenceProvider>> {
// - 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;
Expand Down
Loading