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
31 changes: 20 additions & 11 deletions deps/verifier/src/se/ibmse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//

use crate::{ReportData, TeeEvidence, TeeEvidenceParsedClaim, ToHex};
use crate::{regularize_data, ReportData, TeeEvidence, TeeEvidenceParsedClaim, ToHex};
use anyhow::{anyhow, Context, Result};
use core::result::Result::Ok;
use openssl::encrypt::{Decrypter, Encrypter};
Expand All @@ -24,6 +24,9 @@ use tracing::{debug, info, warn};

const DEFAULT_CERTS_OFFLINE_VERIFICATION: &str = "false";

/// Size of report data in IBM SE attestation (64 bytes)
const SE_REPORT_DATA_SIZE: usize = 64;

const DEFAULT_SE_HOST_KEY_DOCUMENTS_ROOT: &str = "/run/confidential-containers/ibmse/hkds";

const DEFAULT_SE_CERTIFICATES_ROOT: &str = "/run/confidential-containers/ibmse/certs";
Expand Down Expand Up @@ -230,13 +233,19 @@ impl SeVerifierImpl {

// Validate runtime_data_digest if provided
if let ReportData::Value(expected_report_data) = expected_report_data {
let expected_report_data = regularize_data(
expected_report_data,
SE_REPORT_DATA_SIZE,
"USER_DATA",
"IBM SE",
);
let report_data = se_response
.user_data
.get(..48)
.get(..expected_report_data.len())
.context("Failed to get report_data section from USER_DATA")?;
if report_data != *expected_report_data {
if report_data != expected_report_data {
return Err(SeError::UserDataMismatch {
expected: expected_report_data.to_vec(),
expected: expected_report_data,
actual: report_data.to_vec(),
}
.into());
Expand Down Expand Up @@ -438,8 +447,8 @@ mod tests {
fn test_user_data_validation_success() {
let verifier = create_test_verifier();

// Create test data - SHA-384 is 48 bytes
let report_data = vec![0x05; 48];
// Create test data - SHA-512 is 64 bytes
let report_data = vec![0x05; SE_REPORT_DATA_SIZE];

// Build user_data: first 48 bytes are report_data, rest can be anything
let mut user_data = report_data.clone();
Expand Down Expand Up @@ -486,11 +495,11 @@ mod tests {
fn test_user_data_validation_mismatch() {
let verifier = create_test_verifier();

// Create test data - SHA-384 is 48 bytes
let report_data = vec![0x05; 48];
// Create test data - SE report data is 64 bytes
let report_data = vec![0x05; SE_REPORT_DATA_SIZE];

// Build user_data with WRONG report_data in first 48 bytes
let mut user_data = vec![0xFF; 48]; // Wrong report data
// Build user_data with WRONG report_data
let mut user_data = vec![0xFF; SE_REPORT_DATA_SIZE]; // Wrong report data
user_data.extend_from_slice(&[0xAA; 16]); // Add some extra data

let nonce = vec![0x09; 16];
Expand Down Expand Up @@ -534,7 +543,7 @@ mod tests {
let verifier = create_test_verifier();

// Build user_data with some report_data
let report_data = vec![0x05; 48];
let report_data = vec![0x05; SE_REPORT_DATA_SIZE];
let mut user_data = report_data.clone();
user_data.extend_from_slice(&[0xBB; 16]);

Expand Down
17 changes: 11 additions & 6 deletions kbs/src/attestation/coco/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use crate::attestation::generate_extra_params;
use anyhow::*;
use async_trait::async_trait;
use attestation_service::{
Expand Down Expand Up @@ -62,11 +63,16 @@ impl Attest for BuiltInCoCoAs {
let mut verification_requests = vec![];

for evidence in evidence_to_verify {
let runtime_data_hash_algorithm = match evidence.tee {
Tee::Se => HashAlgorithm::Sha512,
_ => HashAlgorithm::Sha384,
};
Comment on lines +66 to +69
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we need to think about bringing a mechanism to include this parameter via the header parsing from RCAR protocol than harding coding here. This should be a TODO for me and not for this PR. This place in the PR looks good now.


let mut request = VerificationRequest {
evidence: evidence.tee_evidence,
tee: evidence.tee,
runtime_data: Some(RuntimeData::Structured(evidence.runtime_data)),
runtime_data_hash_algorithm: HashAlgorithm::Sha384,
runtime_data_hash_algorithm,
init_data: None,
};
if let Some(init_data) = evidence.init_data {
Expand Down Expand Up @@ -102,13 +108,12 @@ impl Attest for BuiltInCoCoAs {
}
_ => make_nonce().await?,
};
let extra_params = generate_extra_params(tee, &tee_parameters)?;

let challenge = Challenge {
Ok(Challenge {
nonce,
extra_params: serde_json::Value::String(String::new()),
};

Ok(challenge)
extra_params,
})
}

async fn register_reference_value(&self, message: &str) -> anyhow::Result<()> {
Expand Down
15 changes: 10 additions & 5 deletions kbs/src/attestation/coco/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use attestation::{
ReferenceValueQueryRequest, ReferenceValueQueryResponse, ReferenceValueRegisterRequest,
};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use kbs_types::{Challenge, Tee};
use kbs_types::{Challenge, HashAlgorithm, Tee};
use mobc::{Manager, Pool};
use serde::Deserialize;
use std::collections::HashMap;
Expand All @@ -32,8 +32,6 @@ mod attestation {
pub const DEFAULT_AS_ADDR: &str = "http://127.0.0.1:50004";
pub const DEFAULT_POOL_SIZE: u64 = 100;

pub const COCO_AS_HASH_ALGORITHM: &str = "sha384";

#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct GrpcConfig {
#[serde(default = "default_as_addr")]
Expand Down Expand Up @@ -106,10 +104,15 @@ impl Attest for GrpcClientPool {
.trim_start_matches('"')
.to_string();

let runtime_data_hash_algorithm = match evidence.tee {
Tee::Se => HashAlgorithm::Sha512.as_ref().to_string().to_lowercase(),
_ => HashAlgorithm::Sha384.as_ref().to_string().to_lowercase(),
};

let mut request = IndividualAttestationRequest {
tee,
evidence: URL_SAFE_NO_PAD.encode(evidence.tee_evidence.to_string()),
runtime_data_hash_algorithm: COCO_AS_HASH_ALGORITHM.into(),
runtime_data_hash_algorithm,
runtime_data: Some(RuntimeData::StructuredRuntimeData(
evidence.runtime_data.to_string(),
)),
Expand Down Expand Up @@ -165,9 +168,11 @@ impl Attest for GrpcClientPool {
_ => make_nonce().await?,
};

let extra_params = crate::attestation::generate_extra_params(tee, &tee_parameters)?;

let challenge = Challenge {
nonce,
extra_params: serde_json::Value::String(String::new()),
extra_params,
};

Ok(challenge)
Expand Down
8 changes: 4 additions & 4 deletions kbs/src/attestation/intel_trust_authority/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
// SPDX-License-Identifier: Apache-2.0

use crate::{
attestation::backend::{generic_generate_challenge, make_nonce, Attest, IndependentEvidence},
attestation::{
backend::{generic_generate_challenge, make_nonce, Attest, IndependentEvidence},
SELECTED_HASH_ALGORITHM_JSON_KEY, SUPPORTED_HASH_ALGORITHMS_JSON_KEY,
},
token::{jwk::JwkAttestationTokenVerifier, AttestationTokenVerifierConfig},
};
use anyhow::*;
Expand All @@ -21,9 +24,6 @@ use sha2::{Digest, Sha512};
use std::result::Result::Ok;
use tracing::{debug, info, warn};

const SUPPORTED_HASH_ALGORITHMS_JSON_KEY: &str = "supported-hash-algorithms";
const SELECTED_HASH_ALGORITHM_JSON_KEY: &str = "selected-hash-algorithm";

const ERR_NO_TEE_ALGOS: &str = "ITA: TEE does not support any hash algorithms";
const ERR_INVALID_TEE: &str = "ITA: Unknown TEE specified";

Expand Down
62 changes: 62 additions & 0 deletions kbs/src/attestation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,67 @@ pub mod session;

pub use backend::AttestationService;

use anyhow::bail;
use kbs_types::{HashAlgorithm, Tee};
use serde_json::json;
use tracing::info;

/// JSON key for supported hash algorithms in TEE parameters
pub const SUPPORTED_HASH_ALGORITHMS_JSON_KEY: &str = "supported-hash-algorithms";

/// JSON key for selected hash algorithm in extra parameters
pub const SELECTED_HASH_ALGORITHM_JSON_KEY: &str = "selected-hash-algorithm";

/// Generate extra parameters for TEE hash algorithm negotiation.
///
/// This function checks if the provided TEE parameters contain supported hash algorithms
/// and selects a hash algorithm based on the TEE type if available.
/// Returns a JSON value with the selected algorithm,
/// or an empty string if negotiation is not applicable.
///
/// Currently only applies to SE (Secure Execution) TEE type.
///
/// # Errors
///
/// Returns an error if:
/// - The hash algorithms field is not an array
/// - The required hash algorithm is not supported by the TEE
pub fn generate_extra_params(
tee: Tee,
tee_parameters: &serde_json::Value,
) -> anyhow::Result<serde_json::Value> {
let extra_params = match tee {
Tee::Se if !tee_parameters.is_null() => {
if let Some(hash_algorithms_found) =
tee_parameters.get(SUPPORTED_HASH_ALGORITHMS_JSON_KEY)
{
let Some(algorithms) = hash_algorithms_found.as_array() else {
bail!("SE expected hash algorithm array, found {hash_algorithms_found:?}");
};

let supported_hash_algorithms: Vec<String> = algorithms
.iter()
.filter_map(|value| Some(value.as_str()?.to_lowercase()))
.collect();

let needed_algorithm = HashAlgorithm::Sha512.as_ref().to_string().to_lowercase();

if !supported_hash_algorithms.contains(&needed_algorithm) {
bail!("SE TEE does not support {needed_algorithm}");
}

json!({
SELECTED_HASH_ALGORITHM_JSON_KEY: needed_algorithm,
})
} else {
info!("SE TEE parameters missing supported hash algorithms");
serde_json::Value::String(String::new())
}
}
_ => serde_json::Value::String(String::new()),
};

Ok(extra_params)
}
pub mod error;
pub use error::*;
Loading