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
7 changes: 7 additions & 0 deletions crates/api-db/src/attestation/spdm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ pub async fn get_attestation_status_for_machine_id(
.await
.map_err(|e| DatabaseError::query(query, e))?;

if attestation_status_rows.is_empty() {
return Err(DatabaseError::NotFoundError {
kind: "SPDM Attestation Record",
id: machine_id.to_string(),
});
}

// if all passed - PASSED
// if all cancelled - CANCELLED
// if any failed && none not in progress - FAILED
Expand Down
7 changes: 7 additions & 0 deletions crates/api-model/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub mod spdm {
use itertools::Itertools;
use nras::{NrasError, NrasVerifierClient, ProcessedAttestationOutcome, RawAttestationOutcome};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use sqlx::Row;
use sqlx::postgres::PgRow;

Expand Down Expand Up @@ -96,6 +97,12 @@ pub mod spdm {
pub completed_at: Option<DateTime<Utc>>,
}

impl SpdmDeviceAttestation {
pub fn nonce_hex(&self) -> String {
hex::encode(Sha256::digest(self.nonce.as_bytes()))
}
}

/// Major state, associated with Machine.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SpdmAttestationState {
Expand Down
65 changes: 59 additions & 6 deletions crates/api/src/handlers/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::collections::BTreeMap;

use ::rpc::common::MachineIdList;
use ::rpc::forge::{self as rpc};
use carbide_uuid::machine::MachineId;
Expand All @@ -32,6 +34,9 @@ use tonic::{Request, Response, Status};
use crate::CarbideError;
use crate::api::{Api, log_machine_id, log_request_data};

const PRODUCT_GB200: &str = "GB200 NVL";
const PRODUCT_GB300: &str = "GB300 NVL";

pub(crate) async fn trigger_machine_attestation(
api: &Api,
request: Request<rpc::SpdmMachineAttestationTriggerRequest>,
Expand Down Expand Up @@ -130,7 +135,7 @@ pub async fn trigger_attestation(
Err(_) => {
return Err(CarbideError::Internal {
message: format!(
"redfish service_root could not finish in {} secods",
"redfish service_root could not finish in {} seconds",
redfish_timeout_duration.as_secs()
),
});
Expand All @@ -142,6 +147,28 @@ pub async fn trigger_attestation(
return Ok(0);
}

// do we support attestation for a given machine type?
// check the ServiceRoot.Product
let product = match service_root.product {
Some(product_name) => product_name,
None => {
tracing::info!(
"ServiceRoot.Product is None, not scheduling SPDM attestation for machine: {}",
machine_id
);
return Ok(0);
}
};

if !is_supported_product(&product) {
tracing::info!(
"ServiceRoot.Product - {} - is not supported, not scheduling SPDM attestation for machine: {}",
product,
machine_id
);
return Ok(0);
}

let component_integrities_future = redfish_client.get_component_integrities();

let component_integrities =
Expand All @@ -155,14 +182,14 @@ pub async fn trigger_attestation(
Err(_) => {
return Err(CarbideError::Internal {
message: format!(
"redfish get_component_integrities could not finish in {} secods",
"redfish get_component_integrities could not finish in {} seconds",
redfish_timeout_duration.as_secs()
),
});
}
};

let components = get_components_supporting_spdm(&component_integrities);
let components = get_supported_components(&product, &component_integrities);

if components.is_empty() {
// let's treat 0 devices under attestation as NotSupported
Expand All @@ -177,6 +204,7 @@ pub async fn trigger_attestation(
// Remove existing device list and over-write with this list.
let time_now = Utc::now();
let device_attestations = components
.clone()
.into_iter()
.map(|x| from_component_integrity(x.clone(), machine_id, &time_now, bmc_info))
.collect_vec();
Expand Down Expand Up @@ -204,9 +232,12 @@ pub async fn trigger_attestation(
.map_err(|e| AnnotatedSqlxError::new("trigger_attestation commit", e))?;

tracing::info!(
"SPDM attestation commenced for machine {}, scheduled {} SPDM device attestations",
"SPDM attestation commenced for machine {}, scheduled {} SPDM device attestations. Attestations scheduled are: {}",
machine_id,
records_inserted
records_inserted,
components.iter().fold("".to_string(), |acc, &elem| {
format!("{} {}", acc, elem.id)
})
);

Ok(records_inserted)
Expand Down Expand Up @@ -419,8 +450,14 @@ pub(crate) async fn attest_quote(
// ComponentIntegrityTypeVersion should be >= 1.1.0.
// ComponentIntegrityType should be SPDM.
// ComponentIntegrityEnabled should be true.
// A device must be of supported type.
// Once these all conditions are true, a device can be proceed with attestation.
fn get_components_supporting_spdm(integrities: &ComponentIntegrities) -> Vec<&ComponentIntegrity> {
fn get_supported_components<'a>(
product: &str,
integrities: &'a ComponentIntegrities,
) -> Vec<&'a ComponentIntegrity> {
let supported_devices = BTreeMap::from([(PRODUCT_GB200, ["HGX_IRoT_GPU"])]);

let supported_versions = ["1.1.0"]; // This can be configurable value.
let mut supported_components = vec![];

Expand All @@ -439,6 +476,17 @@ fn get_components_supporting_spdm(integrities: &ComponentIntegrities) -> Vec<&Co
continue;
}

let is_supported = match supported_devices.get(product) {
Some(device_id_stems) => device_id_stems
.iter()
.any(|device_id_stem| component.id.contains(device_id_stem)),
None => false,
};

if !is_supported {
continue;
}

supported_components.push(component);
}

Expand All @@ -454,6 +502,7 @@ fn from_component_integrity(
let ca_certificate_link = integrity
.spdm
.map(|x| x.identity_authentication)
.map(|x| x.responder_authentication)
.map(|x| x.component_certificate)
.map(|x| x.odata_id);

Expand Down Expand Up @@ -483,6 +532,10 @@ fn from_component_integrity(
}
}

fn is_supported_product(product: &str) -> bool {
matches!(product, PRODUCT_GB200 | PRODUCT_GB300)
}

#[cfg(not(feature = "linux-build"))]
pub(crate) async fn attest_quote(
_api: &Api,
Expand Down
5 changes: 3 additions & 2 deletions crates/api/src/state_controller/spdm/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ impl StateHandler for SpdmAttestationStateHandler {
),
));
};
let nonce = snapshot.nonce_hex();
let task = redfish_client
.trigger_evidence_collection(url.as_str(), snapshot.nonce.to_string().as_str())
.trigger_evidence_collection(url.as_str(), nonce.as_str())
.await
.map_err(|error| redfish_error("trigger measurement collection", error))?;

Expand Down Expand Up @@ -348,7 +349,7 @@ async fn perform_attestation(
firmware_version,
}],
architecture: nras::MachineArchitecture::Blackwell,
nonce: device.nonce.to_string(),
nonce: device.nonce_hex(),
};

let device_type: DeviceType = device.device_id.parse()?;
Expand Down
4 changes: 2 additions & 2 deletions crates/nras/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ impl VerifierClient for NrasVerifierClient {

if status_code != reqwest::StatusCode::OK {
return Err(NrasError::Communication(format!(
"NRAS returned status code {} and message {}",
status_code, response_text
"NRAS returned status code {} and message {}.\n Config is {:?}",
status_code, response_text, self.config
)));
}

Expand Down
90 changes: 54 additions & 36 deletions crates/redfish/src/libredfish/test_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1397,13 +1397,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/ERoT_BMC_0".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/ERoT_BMC_0/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/ERoT_BMC_0/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down Expand Up @@ -1431,13 +1434,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/HGX_IRoT_GPU_0".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_0/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_0/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down Expand Up @@ -1465,13 +1471,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/HGX_IRoT_GPU_1".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down Expand Up @@ -1499,13 +1508,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/HGX_IRoT_GPU_1".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down Expand Up @@ -1533,13 +1545,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/HGX_IRoT_GPU_1".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down Expand Up @@ -1567,13 +1582,16 @@ impl Redfish for RedfishSimClient {
target_component_uri: Some("/redfish/v1/Chassis/HGX_IRoT_GPU_1".to_string()),
spdm: Some(libredfish::model::component_integrity::SPDMData {
identity_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
libredfish::model::component_integrity::IdentityAuthentication {
responder_authentication:
libredfish::model::component_integrity::ResponderAuthentication {
component_certificate: ODataId {
odata_id:
"/redfish/v1/Chassis/HGX_IRoT_GPU_1/Certificates/CertChain"
.to_string(),
},
},
},
},
requester: ODataId {
odata_id: "/redfish/v1/Managers/BMC_0".to_string(),
},
Expand Down