mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 15:13:56 +02:00
Merge pull request #302 from matter-labs/intel-dcap-api
feat(api): add Intel DCAP API client module
This commit is contained in:
commit
9b9acfc0c6
18 changed files with 2026 additions and 1 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -2603,6 +2603,21 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "intel-dcap-api"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"hex",
|
||||||
|
"percent-encoding",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.11",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"x509-cert",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "intel-tee-quote-verification-sys"
|
name = "intel-tee-quote-verification-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -31,6 +31,7 @@ enumset = { version = "1.1", features = ["serde"] }
|
||||||
getrandom = { version = "0.3.1", features = ["std"] }
|
getrandom = { version = "0.3.1", features = ["std"] }
|
||||||
gpt = "4.0.0"
|
gpt = "4.0.0"
|
||||||
hex = { version = "0.4.3", features = ["std"], default-features = false }
|
hex = { version = "0.4.3", features = ["std"], default-features = false }
|
||||||
|
intel-dcap-api = { path = "crates/intel-dcap-api" }
|
||||||
num-integer = "0.1.46"
|
num-integer = "0.1.46"
|
||||||
num-traits = "0.2.18"
|
num-traits = "0.2.18"
|
||||||
opentelemetry = { version = "0.28.0", features = ["default", "logs"] }
|
opentelemetry = { version = "0.28.0", features = ["default", "logs"] }
|
||||||
|
@ -65,5 +66,5 @@ tracing-log = "0.2"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "ansi"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "ansi"] }
|
||||||
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
x509-cert = { version = "0.2", features = ["builder", "signature"] }
|
x509-cert = { version = "0.2", features = ["builder", "signature", "default"] }
|
||||||
zeroize = { version = "1.7.0", features = ["serde"] }
|
zeroize = { version = "1.7.0", features = ["serde"] }
|
||||||
|
|
27
crates/intel-dcap-api/Cargo.toml
Normal file
27
crates/intel-dcap-api/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[package]
|
||||||
|
name = "intel-dcap-api"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
keywords = ["sgx", "tdx", "intel", "attestation", "confidential"]
|
||||||
|
categories = ["api-bindings", "cryptography", "authentication"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
percent-encoding = "2.3.1"
|
||||||
|
reqwest = { workspace = true, features = ["json"] }
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
url.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex.workspace = true
|
||||||
|
x509-cert.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["reqwest/default-tls"]
|
||||||
|
rustls = ["reqwest/rustls-tls"]
|
78
crates/intel-dcap-api/examples/example.rs
Normal file
78
crates/intel-dcap-api/examples/example.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
use intel_dcap_api::{
|
||||||
|
ApiClient, ApiVersion, CaType, CrlEncoding, EnclaveIdentityResponse, IntelApiError,
|
||||||
|
PckCrlResponse, PlatformFilter, TcbInfoResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), IntelApiError> {
|
||||||
|
for api_version in [ApiVersion::V3, ApiVersion::V4] {
|
||||||
|
println!("Using API version: {}", api_version);
|
||||||
|
|
||||||
|
let client = ApiClient::new_with_version(api_version)?;
|
||||||
|
|
||||||
|
// Example: Get SGX TCB Info
|
||||||
|
let fmspc_example = "00606A000000"; // Example FMSPC from docs
|
||||||
|
match client.get_sgx_tcb_info(fmspc_example, None, None).await {
|
||||||
|
Ok(TcbInfoResponse {
|
||||||
|
tcb_info_json,
|
||||||
|
issuer_chain,
|
||||||
|
}) => println!(
|
||||||
|
"SGX TCB Info for {}:\n{}\nIssuer Chain: {}",
|
||||||
|
fmspc_example, tcb_info_json, issuer_chain
|
||||||
|
),
|
||||||
|
Err(e) => eprintln!("Error getting SGX TCB info: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Get FMSPCs
|
||||||
|
match client.get_fmspcs(Some(PlatformFilter::E3)).await {
|
||||||
|
// Filter for E3 platform type [cite: 230]
|
||||||
|
Ok(fmspc_list) => println!("\nE3 FMSPCs:\n{}", fmspc_list),
|
||||||
|
Err(e) => eprintln!("Error getting FMSPCs: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Get SGX QE Identity
|
||||||
|
match client.get_sgx_qe_identity(None, None).await {
|
||||||
|
Ok(EnclaveIdentityResponse {
|
||||||
|
enclave_identity_json,
|
||||||
|
issuer_chain,
|
||||||
|
}) => {
|
||||||
|
println!(
|
||||||
|
"\nSGX QE Identity:\n{}\nIssuer Chain: {}",
|
||||||
|
enclave_identity_json, issuer_chain
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error getting SGX QE Identity: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Get PCK CRL (Platform CA, PEM encoding)
|
||||||
|
match client
|
||||||
|
.get_pck_crl(CaType::Platform, Some(CrlEncoding::Pem))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
// [cite: 118, 119]
|
||||||
|
Ok(PckCrlResponse {
|
||||||
|
crl_data,
|
||||||
|
issuer_chain,
|
||||||
|
}) => {
|
||||||
|
// Attempt to decode PEM for display, otherwise show byte count
|
||||||
|
match String::from_utf8(crl_data.clone()) {
|
||||||
|
Ok(pem_string) => println!(
|
||||||
|
"\nPlatform PCK CRL (PEM):\n{}\nIssuer Chain: {}",
|
||||||
|
pem_string, issuer_chain
|
||||||
|
),
|
||||||
|
Err(_) => println!(
|
||||||
|
"\nPlatform PCK CRL ({} bytes, likely DER):\nIssuer Chain: {}",
|
||||||
|
crl_data.len(),
|
||||||
|
issuer_chain
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error getting PCK CRL: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
75
crates/intel-dcap-api/examples/get_pck_crl.rs
Normal file
75
crates/intel-dcap-api/examples/get_pck_crl.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
use intel_dcap_api::{ApiClient, CaType, CrlEncoding, IntelApiError, PckCrlResponse};
|
||||||
|
use x509_cert::{
|
||||||
|
der::{oid::AssociatedOid, Decode, SliceReader},
|
||||||
|
ext::pkix::{
|
||||||
|
crl::dp::DistributionPoint,
|
||||||
|
name::{DistributionPointName, GeneralName},
|
||||||
|
CrlDistributionPoints,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), IntelApiError> {
|
||||||
|
let client = ApiClient::new()?;
|
||||||
|
|
||||||
|
let PckCrlResponse {
|
||||||
|
crl_data,
|
||||||
|
issuer_chain,
|
||||||
|
} = client
|
||||||
|
.get_pck_crl(CaType::Platform, Some(CrlEncoding::Der))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let certs = x509_cert::certificate::CertificateInner::<
|
||||||
|
x509_cert::certificate::Rfc5280
|
||||||
|
>::load_pem_chain(issuer_chain.as_bytes()).map_err(
|
||||||
|
|_| IntelApiError::InvalidParameter("Could not load a PEM chain")
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for cert in certs {
|
||||||
|
println!("Issuer: {}", cert.tbs_certificate.issuer);
|
||||||
|
println!("Subject: {}", cert.tbs_certificate.subject);
|
||||||
|
println!("Serial Number: {}", cert.tbs_certificate.serial_number);
|
||||||
|
println!("Not Before: {}", cert.tbs_certificate.validity.not_before);
|
||||||
|
println!("Not After: {}", cert.tbs_certificate.validity.not_after);
|
||||||
|
|
||||||
|
// Extract and print CRL distribution points
|
||||||
|
if let Some(extensions) = &cert.tbs_certificate.extensions {
|
||||||
|
for ext in extensions.iter() {
|
||||||
|
if ext.extn_id == CrlDistributionPoints::OID {
|
||||||
|
// Create a SliceReader from the byte slice
|
||||||
|
let mut reader = SliceReader::new(ext.extn_value.as_bytes()).map_err(|_| {
|
||||||
|
IntelApiError::InvalidParameter(
|
||||||
|
"Could not create reader from extension value",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Now pass the reader to decode_value
|
||||||
|
if let Ok(dist_points) = Vec::<DistributionPoint>::decode(&mut reader) {
|
||||||
|
for point in dist_points {
|
||||||
|
if let Some(DistributionPointName::FullName(names)) =
|
||||||
|
point.distribution_point
|
||||||
|
{
|
||||||
|
for name in names {
|
||||||
|
if let GeneralName::UniformResourceIdentifier(uri) = name {
|
||||||
|
let uri = uri.as_str();
|
||||||
|
let crl_bytes = reqwest::get(uri).await?.bytes().await?;
|
||||||
|
println!("CRL bytes (hex): {}", hex::encode(&crl_bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Could not decode CRL distribution points");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("CRL bytes (hex): {}", hex::encode(&crl_data));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
229
crates/intel-dcap-api/src/client/enclave_identity.rs
Normal file
229
crates/intel-dcap-api/src/client/enclave_identity.rs
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Enclave Identity
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::IntelApiError,
|
||||||
|
responses::EnclaveIdentityResponse,
|
||||||
|
types::{ApiVersion, UpdateType},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// Retrieves the SGX QE Identity from the Intel API.
|
||||||
|
///
|
||||||
|
/// Returns Enclave Identity JSON string (Appendix B) and Issuer Chain header.
|
||||||
|
/// Supports both v3 and v4. The `update` and `tcb_evaluation_data_number`
|
||||||
|
/// parameters are only valid in API v4. Returns the enclave identity JSON
|
||||||
|
/// and an issuer chain header.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `update` - Optional [`UpdateType`] (v4 only).
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An [`EnclaveIdentityResponse`] containing the JSON identity and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails, if conflicting v4 parameters are used,
|
||||||
|
/// or if the desired identity resource is not found.
|
||||||
|
pub async fn get_sgx_qe_identity(
|
||||||
|
&self,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<EnclaveIdentityResponse, IntelApiError> {
|
||||||
|
self.get_sgx_enclave_identity("qe", update, tcb_evaluation_data_number)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the TDX QE Identity from the Intel API (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `update` - Optional [`UpdateType`] (v4 only).
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An [`EnclaveIdentityResponse`] containing the JSON identity and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used,
|
||||||
|
/// if conflicting parameters are provided, or if the identity resource is not found.
|
||||||
|
/// GET /tdx/certification/v4/qe/identity - V4 ONLY
|
||||||
|
pub async fn get_tdx_qe_identity(
|
||||||
|
&self,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<EnclaveIdentityResponse, IntelApiError> {
|
||||||
|
// Ensure V4 API
|
||||||
|
self.ensure_v4_api("get_tdx_qe_identity")?;
|
||||||
|
// Check conflicting parameters (only relevant for V4, checked inside helper)
|
||||||
|
self.check_conflicting_update_params(update, tcb_evaluation_data_number)?;
|
||||||
|
|
||||||
|
let path = self.build_api_path("tdx", "qe", "identity")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
|
||||||
|
if let Some(upd) = update {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("update", &upd.to_string());
|
||||||
|
}
|
||||||
|
if let Some(tedn) = tcb_evaluation_data_number {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("tcbEvaluationDataNumber", &tedn.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
// Special handling for 404/410 when tcbEvaluationDataNumber is specified
|
||||||
|
if let Some(tedn_val) = tcb_evaluation_data_number {
|
||||||
|
// Use the helper function to check status before proceeding
|
||||||
|
self.check_tcb_evaluation_status(&request_builder, tedn_val, "TDX QE Identity")
|
||||||
|
.await?;
|
||||||
|
// If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JSON and header (TDX only exists in V4)
|
||||||
|
let (enclave_identity_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(
|
||||||
|
request_builder,
|
||||||
|
"SGX-Enclave-Identity-Issuer-Chain",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(EnclaveIdentityResponse {
|
||||||
|
enclave_identity_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the SGX QVE Identity from the Intel API.
|
||||||
|
///
|
||||||
|
/// Supports API v3 and v4. The `update` and `tcb_evaluation_data_number` parameters
|
||||||
|
/// are v4 only. Returns the QVE identity JSON and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `update` - Optional [`UpdateType`] (v4 only).
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An [`EnclaveIdentityResponse`] containing the QVE identity JSON and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails, if conflicting parameters are used,
|
||||||
|
/// or if the identity resource is not found.
|
||||||
|
/// GET /sgx/certification/{v3,v4}/qve/identity
|
||||||
|
pub async fn get_sgx_qve_identity(
|
||||||
|
&self,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<EnclaveIdentityResponse, IntelApiError> {
|
||||||
|
self.get_sgx_enclave_identity("qve", update, tcb_evaluation_data_number)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the SGX QAE Identity from the Intel API (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `update` - Optional [`UpdateType`] (v4 only).
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An [`EnclaveIdentityResponse`] containing the QAE identity JSON and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used,
|
||||||
|
/// if conflicting parameters are provided, or if the QAE identity is not found.
|
||||||
|
/// GET /sgx/certification/v4/qae/identity - V4 ONLY
|
||||||
|
pub async fn get_sgx_qae_identity(
|
||||||
|
&self,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<EnclaveIdentityResponse, IntelApiError> {
|
||||||
|
// QAE endpoint requires V4
|
||||||
|
if self.api_version != ApiVersion::V4 {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"QAE Identity endpoint requires API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Call the generic helper, it will handle V4 params and 404/410 checks
|
||||||
|
self.get_sgx_enclave_identity("qae", update, tcb_evaluation_data_number)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves generic SGX enclave identity (QE, QVE, QAE) data.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `identity_path_segment` - String slice representing the identity path segment (e.g., "qe", "qve", "qae").
|
||||||
|
/// * `update` - Optional [`UpdateType`] for API v4.
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number for API v4.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An [`EnclaveIdentityResponse`] containing the JSON identity data and issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or the specified resource
|
||||||
|
/// is unavailable.
|
||||||
|
async fn get_sgx_enclave_identity(
|
||||||
|
&self,
|
||||||
|
identity_path_segment: &str,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<EnclaveIdentityResponse, IntelApiError> {
|
||||||
|
self.check_v4_only_param(update, "update")?;
|
||||||
|
self.check_v4_only_param(tcb_evaluation_data_number, "tcbEvaluationDataNumber")?;
|
||||||
|
self.check_conflicting_update_params(update, tcb_evaluation_data_number)?;
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", identity_path_segment, "identity")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
if let Some(upd) = update {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("update", &upd.to_string());
|
||||||
|
}
|
||||||
|
if let Some(tedn) = tcb_evaluation_data_number {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("tcbEvaluationDataNumber", &tedn.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
if let Some(tedn_val) = tcb_evaluation_data_number {
|
||||||
|
let description = format!("SGX {} Identity", identity_path_segment.to_uppercase());
|
||||||
|
self.check_tcb_evaluation_status(&request_builder, tedn_val, &description)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (enclave_identity_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(
|
||||||
|
request_builder,
|
||||||
|
"SGX-Enclave-Identity-Issuer-Chain",
|
||||||
|
Some("SGX-Enclave-Identity-Issuer-Chain"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(EnclaveIdentityResponse {
|
||||||
|
enclave_identity_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
134
crates/intel-dcap-api/src/client/fmspc.rs
Normal file
134
crates/intel-dcap-api/src/client/fmspc.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! FMSPCs & TCB Evaluation Data Numbers
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::{check_status, IntelApiError},
|
||||||
|
responses::TcbEvaluationDataNumbersResponse,
|
||||||
|
types::{ApiVersion, PlatformFilter},
|
||||||
|
FmspcJsonResponse,
|
||||||
|
};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// GET /sgx/certification/{v3,v4}/fmspcs
|
||||||
|
/// Retrieves a list of FMSPC values for SGX and TDX platforms (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `platform_filter` - An optional filter specifying SGX or TDX platforms.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Optional 'platform' filter.
|
||||||
|
/// A `String` containing the JSON array of objects, each containing `fmspc` and `platform`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used or if the request fails.
|
||||||
|
pub async fn get_fmspcs(
|
||||||
|
&self,
|
||||||
|
platform_filter: Option<PlatformFilter>,
|
||||||
|
) -> Result<FmspcJsonResponse, IntelApiError> {
|
||||||
|
if self.api_version == ApiVersion::V3 {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"API v4 only function".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let path = self.build_api_path("sgx", "", "fmspcs")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
|
||||||
|
if let Some(pf) = platform_filter {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("platform", &pf.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
let response = request_builder.send().await?;
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
let fmspcs_json = response.text().await?;
|
||||||
|
|
||||||
|
Ok(fmspcs_json)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /sgx/certification/v4/tcbevaluationdatanumbers - V4 ONLY
|
||||||
|
/// Retrieves the currently supported SGX TCB Evaluation Data Numbers (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`TcbEvaluationDataNumbersResponse`] containing the JSON structure of TCB Evaluation
|
||||||
|
/// Data Numbers and an issuer chain header.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used or if the request fails.
|
||||||
|
pub async fn get_sgx_tcb_evaluation_data_numbers(
|
||||||
|
&self,
|
||||||
|
) -> Result<TcbEvaluationDataNumbersResponse, IntelApiError> {
|
||||||
|
// Endpoint requires V4
|
||||||
|
if self.api_version != ApiVersion::V4 {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"SGX TCB Evaluation Data Numbers endpoint requires API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", "", "tcbevaluationdatanumbers")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
let (tcb_evaluation_data_numbers_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(
|
||||||
|
request_builder,
|
||||||
|
"TCB-Evaluation-Data-Numbers-Issuer-Chain",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(TcbEvaluationDataNumbersResponse {
|
||||||
|
tcb_evaluation_data_numbers_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /tdx/certification/v4/tcbevaluationdatanumbers - V4 ONLY
|
||||||
|
/// Retrieves the currently supported TDX TCB Evaluation Data Numbers (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`TcbEvaluationDataNumbersResponse`] containing the JSON structure of TCB Evaluation
|
||||||
|
/// Data Numbers and an issuer chain header.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used or if the request fails.
|
||||||
|
pub async fn get_tdx_tcb_evaluation_data_numbers(
|
||||||
|
&self,
|
||||||
|
) -> Result<TcbEvaluationDataNumbersResponse, IntelApiError> {
|
||||||
|
// Endpoint requires V4
|
||||||
|
if self.api_version != ApiVersion::V4 {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"TDX TCB Evaluation Data Numbers endpoint requires API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = self.build_api_path("tdx", "", "tcbevaluationdatanumbers")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
let (tcb_evaluation_data_numbers_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(
|
||||||
|
request_builder,
|
||||||
|
"TCB-Evaluation-Data-Numbers-Issuer-Chain",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(TcbEvaluationDataNumbersResponse {
|
||||||
|
tcb_evaluation_data_numbers_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
232
crates/intel-dcap-api/src/client/helpers.rs
Normal file
232
crates/intel-dcap-api/src/client/helpers.rs
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Internal helper methods
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::{check_status, extract_api_error_details, IntelApiError},
|
||||||
|
responses::{PckCertificateResponse, PckCertificatesResponse},
|
||||||
|
types::{ApiVersion, UpdateType},
|
||||||
|
};
|
||||||
|
use percent_encoding::percent_decode_str;
|
||||||
|
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// Helper to construct API paths dynamically based on version and technology (SGX/TDX).
|
||||||
|
pub(super) fn build_api_path(
|
||||||
|
&self,
|
||||||
|
technology: &str,
|
||||||
|
service: &str,
|
||||||
|
endpoint: &str,
|
||||||
|
) -> Result<String, IntelApiError> {
|
||||||
|
let api_segment = self.api_version.path_segment();
|
||||||
|
|
||||||
|
if technology == "tdx" && self.api_version == ApiVersion::V3 {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(format!(
|
||||||
|
"TDX endpoint /{}/{}/{} requires API v4",
|
||||||
|
service, endpoint, technology
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if technology == "sgx" && service == "registration" {
|
||||||
|
// Registration paths are fixed at v1 regardless of client's api_version
|
||||||
|
return Ok(format!("/sgx/registration/v1/{}", endpoint).replace("//", "/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"/{}/certification/{}/{}/{}",
|
||||||
|
technology, api_segment, service, endpoint
|
||||||
|
)
|
||||||
|
.replace("//", "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to add an optional header if the string is non-empty.
|
||||||
|
pub(super) fn maybe_add_header(
|
||||||
|
builder: RequestBuilder,
|
||||||
|
header_name: &'static str,
|
||||||
|
header_value: Option<&str>,
|
||||||
|
) -> RequestBuilder {
|
||||||
|
match header_value {
|
||||||
|
Some(value) if !value.is_empty() => builder.header(header_name, value),
|
||||||
|
_ => builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to extract a required header string value, handling potential v3/v4 differences.
|
||||||
|
pub(super) fn get_required_header(
|
||||||
|
&self,
|
||||||
|
response: &Response,
|
||||||
|
v4_header_name: &'static str,
|
||||||
|
v3_header_name: Option<&'static str>,
|
||||||
|
) -> Result<String, IntelApiError> {
|
||||||
|
let header_name = match self.api_version {
|
||||||
|
ApiVersion::V4 => v4_header_name,
|
||||||
|
ApiVersion::V3 => v3_header_name.unwrap_or(v4_header_name),
|
||||||
|
};
|
||||||
|
let value = response
|
||||||
|
.headers()
|
||||||
|
.get(header_name)
|
||||||
|
.ok_or(IntelApiError::MissingOrInvalidHeader(header_name))?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|e| IntelApiError::HeaderValueParse(header_name, e.to_string()))?;
|
||||||
|
|
||||||
|
if value.contains('%') {
|
||||||
|
percent_decode_str(value)
|
||||||
|
.decode_utf8()
|
||||||
|
.map_err(|e| IntelApiError::HeaderValueParse(header_name, e.to_string()))
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
} else {
|
||||||
|
Ok(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to execute a request that returns a single PCK certificate and associated headers.
|
||||||
|
pub(super) async fn fetch_pck_certificate(
|
||||||
|
&self,
|
||||||
|
request_builder: RequestBuilder,
|
||||||
|
) -> Result<PckCertificateResponse, IntelApiError> {
|
||||||
|
let response = request_builder.send().await?;
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
let issuer_chain = self.get_required_header(
|
||||||
|
&response,
|
||||||
|
"SGX-PCK-Certificate-Issuer-Chain",
|
||||||
|
Some("SGX-PCK-Certificate-Issuer-Chain"),
|
||||||
|
)?;
|
||||||
|
let tcbm = self.get_required_header(&response, "SGX-TCBm", Some("SGX-TCBm"))?;
|
||||||
|
let fmspc = self.get_required_header(&response, "SGX-FMSPC", Some("SGX-FMSPC"))?;
|
||||||
|
let pck_cert_pem = response.text().await?;
|
||||||
|
|
||||||
|
Ok(PckCertificateResponse {
|
||||||
|
pck_cert_pem,
|
||||||
|
issuer_chain,
|
||||||
|
tcbm,
|
||||||
|
fmspc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to execute a request that returns a PCK certificates JSON array and associated headers.
|
||||||
|
pub(super) async fn fetch_pck_certificates(
|
||||||
|
&self,
|
||||||
|
request_builder: RequestBuilder,
|
||||||
|
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||||
|
let response = request_builder.send().await?;
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
let issuer_chain = self.get_required_header(
|
||||||
|
&response,
|
||||||
|
"SGX-PCK-Certificate-Issuer-Chain",
|
||||||
|
Some("SGX-PCK-Certificate-Issuer-Chain"),
|
||||||
|
)?;
|
||||||
|
let fmspc = self.get_required_header(&response, "SGX-FMSPC", Some("SGX-FMSPC"))?;
|
||||||
|
let pck_certs_json = response.text().await?;
|
||||||
|
|
||||||
|
Ok(PckCertificatesResponse {
|
||||||
|
pck_certs_json,
|
||||||
|
issuer_chain,
|
||||||
|
fmspc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to execute a request expected to return JSON plus an Issuer-Chain header.
|
||||||
|
pub(super) async fn fetch_json_with_issuer_chain(
|
||||||
|
&self,
|
||||||
|
request_builder: RequestBuilder,
|
||||||
|
v4_issuer_chain_header: &'static str,
|
||||||
|
v3_issuer_chain_header: Option<&'static str>,
|
||||||
|
) -> Result<(String, String), IntelApiError> {
|
||||||
|
let response = request_builder.send().await?;
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
let issuer_chain =
|
||||||
|
self.get_required_header(&response, v4_issuer_chain_header, v3_issuer_chain_header)?;
|
||||||
|
let json_body = response.text().await?;
|
||||||
|
|
||||||
|
Ok((json_body, issuer_chain))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for HTTP 404 or 410 status when querying TCB Evaluation Data Number based resources.
|
||||||
|
pub(super) async fn check_tcb_evaluation_status(
|
||||||
|
&self,
|
||||||
|
request_builder: &RequestBuilder,
|
||||||
|
tcb_evaluation_data_number_val: u64,
|
||||||
|
resource_description: &str,
|
||||||
|
) -> Result<(), IntelApiError> {
|
||||||
|
let builder_clone = request_builder.try_clone().ok_or_else(|| {
|
||||||
|
IntelApiError::Io(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Failed to clone request builder for status check",
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let response = builder_clone.send().await?;
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if status == StatusCode::NOT_FOUND || status == StatusCode::GONE {
|
||||||
|
let (request_id, _, _) = extract_api_error_details(&response);
|
||||||
|
return Err(IntelApiError::ApiError {
|
||||||
|
status,
|
||||||
|
request_id,
|
||||||
|
error_code: None,
|
||||||
|
error_message: Some(format!(
|
||||||
|
"{} for TCB Evaluation Data Number {} {}",
|
||||||
|
resource_description,
|
||||||
|
tcb_evaluation_data_number_val,
|
||||||
|
if status == StatusCode::NOT_FOUND {
|
||||||
|
"not found"
|
||||||
|
} else {
|
||||||
|
"is no longer available"
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures the client is configured for API v4, otherwise returns an error.
|
||||||
|
pub(super) fn ensure_v4_api(&self, function_name: &str) -> Result<(), IntelApiError> {
|
||||||
|
if self.api_version != ApiVersion::V4 {
|
||||||
|
Err(IntelApiError::UnsupportedApiVersion(format!(
|
||||||
|
"{} requires API v4",
|
||||||
|
function_name
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a V4-only parameter is provided with a V3 API version.
|
||||||
|
pub(super) fn check_v4_only_param<T>(
|
||||||
|
&self,
|
||||||
|
param_value: Option<T>,
|
||||||
|
param_name: &str,
|
||||||
|
) -> Result<(), IntelApiError> {
|
||||||
|
if self.api_version == ApiVersion::V3 && param_value.is_some() {
|
||||||
|
Err(IntelApiError::UnsupportedApiVersion(format!(
|
||||||
|
"'{}' parameter requires API v4",
|
||||||
|
param_name
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for conflicting `update` and `tcb_evaluation_data_number` parameters when using V4.
|
||||||
|
pub(super) fn check_conflicting_update_params(
|
||||||
|
&self,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<(), IntelApiError> {
|
||||||
|
if self.api_version == ApiVersion::V4
|
||||||
|
&& update.is_some()
|
||||||
|
&& tcb_evaluation_data_number.is_some()
|
||||||
|
{
|
||||||
|
Err(IntelApiError::ConflictingParameters(
|
||||||
|
"'update' and 'tcbEvaluationDataNumber'",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
crates/intel-dcap-api/src/client/mod.rs
Normal file
119
crates/intel-dcap-api/src/client/mod.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
mod enclave_identity;
|
||||||
|
mod fmspc;
|
||||||
|
mod helpers;
|
||||||
|
mod pck_cert;
|
||||||
|
mod pck_crl;
|
||||||
|
mod registration;
|
||||||
|
mod tcb_info;
|
||||||
|
|
||||||
|
use crate::{error::IntelApiError, types::ApiVersion};
|
||||||
|
use reqwest::Client;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
// Base URL for the Intel Trusted Services API
|
||||||
|
const BASE_URL: &str = "https://api.trustedservices.intel.com";
|
||||||
|
|
||||||
|
/// Client for interacting with Intel Trusted Services API.
|
||||||
|
///
|
||||||
|
/// Provides methods to access both SGX and TDX certification services,
|
||||||
|
/// supporting API versions V3 and V4. This client offers functionality
|
||||||
|
/// to register platforms, retrieve PCK certificates and CRLs, fetch TCB
|
||||||
|
/// information, enclave identities, as well as TCB evaluation data numbers.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use intel_dcap_api::ApiClient;
|
||||||
|
///
|
||||||
|
/// #[tokio::main]
|
||||||
|
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// // Create a client with default settings (V4 API)
|
||||||
|
/// let client = ApiClient::new()?;
|
||||||
|
///
|
||||||
|
/// // Retrieve TCB info for a specific FMSPC
|
||||||
|
/// let tcb_info = client.get_sgx_tcb_info("00606A000000", None, None).await?;
|
||||||
|
/// println!("TCB Info: {}", tcb_info.tcb_info_json);
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ApiClient {
|
||||||
|
client: Client,
|
||||||
|
base_url: Url,
|
||||||
|
api_version: ApiVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// Creates a new client targeting the latest supported API version (V4).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A result containing the newly created `ApiClient` or an `IntelApiError` if there
|
||||||
|
/// was an issue building the underlying HTTP client.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function may fail if the provided TLS version or base URL
|
||||||
|
/// cannot be used to build a `reqwest` client.
|
||||||
|
pub fn new() -> Result<Self, IntelApiError> {
|
||||||
|
// Default to V4
|
||||||
|
Self::new_with_options(BASE_URL, ApiVersion::V4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new client targeting a specific API version.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `api_version` - The desired API version to use (V3 or V4).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the `reqwest` client cannot be built
|
||||||
|
/// with the specified options.
|
||||||
|
pub fn new_with_version(api_version: ApiVersion) -> Result<Self, IntelApiError> {
|
||||||
|
Self::new_with_options(BASE_URL, api_version)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new client with a custom base URL, targeting the latest supported API version (V4).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `base_url` - The custom base URL for the Intel Trusted Services API.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the `reqwest` client cannot be built
|
||||||
|
/// or if the provided base URL is invalid.
|
||||||
|
pub fn new_with_base_url(base_url: impl reqwest::IntoUrl) -> Result<Self, IntelApiError> {
|
||||||
|
// Default to V4
|
||||||
|
Self::new_with_options(base_url, ApiVersion::V4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new client with a custom base URL and specific API version.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `base_url` - The custom base URL for the Intel Trusted Services API.
|
||||||
|
/// * `api_version` - The desired API version (V3 or V4).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the `reqwest` client cannot be built
|
||||||
|
/// or if the provided base URL is invalid.
|
||||||
|
pub fn new_with_options(
|
||||||
|
base_url: impl reqwest::IntoUrl,
|
||||||
|
api_version: ApiVersion,
|
||||||
|
) -> Result<Self, IntelApiError> {
|
||||||
|
Ok(ApiClient {
|
||||||
|
client: Client::builder()
|
||||||
|
.min_tls_version(reqwest::tls::Version::TLS_1_2)
|
||||||
|
.build()?,
|
||||||
|
base_url: base_url.into_url()?,
|
||||||
|
api_version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
353
crates/intel-dcap-api/src/client/pck_cert.rs
Normal file
353
crates/intel-dcap-api/src/client/pck_cert.rs
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Provisioning Certification Service
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::IntelApiError,
|
||||||
|
requests::{PckCertRequest, PckCertsConfigRequest, PckCertsRequest},
|
||||||
|
responses::{PckCertificateResponse, PckCertificatesResponse},
|
||||||
|
types::ApiVersion,
|
||||||
|
};
|
||||||
|
use reqwest::header;
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// GET /sgx/certification/{v3,v4}/pckcert
|
||||||
|
/// Retrieves a single SGX PCK certificate using encrypted PPID and SVNs.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter
|
||||||
|
/// is only valid for API v4 and allows specifying the PPID encryption key type (e.g. "RSA-3072").
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `encrypted_ppid` - Hex-encoded encrypted PPID.
|
||||||
|
/// * `cpusvn` - Hex-encoded CPUSVN value.
|
||||||
|
/// * `pcesvn` - Hex-encoded PCESVN value.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `subscription_key` - Optional subscription key if the Intel API requires it.
|
||||||
|
/// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificateResponse`] containing the PEM-encoded certificate, issuer chain,
|
||||||
|
/// TCBm, and FMSPC.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the API call fails or the response contains an invalid status.
|
||||||
|
/// Returns PEM Cert, Issuer Chain, TCBm, FMSPC.
|
||||||
|
pub async fn get_pck_certificate_by_ppid(
|
||||||
|
&self,
|
||||||
|
encrypted_ppid: &str,
|
||||||
|
cpusvn: &str,
|
||||||
|
pcesvn: &str,
|
||||||
|
pceid: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
ppid_encryption_key_type: Option<&str>,
|
||||||
|
) -> Result<PckCertificateResponse, IntelApiError> {
|
||||||
|
// Check V4-only parameter
|
||||||
|
self.check_v4_only_param(ppid_encryption_key_type, "PPID-Encryption-Key")?;
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcert")?; // service is empty
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("encrypted_ppid", encrypted_ppid)
|
||||||
|
.append_pair("cpusvn", cpusvn)
|
||||||
|
.append_pair("pcesvn", pcesvn)
|
||||||
|
.append_pair("pceid", pceid);
|
||||||
|
|
||||||
|
let mut request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only add for V4
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"PPID-Encryption-Key",
|
||||||
|
ppid_encryption_key_type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fetch_pck_certificate(request_builder).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /sgx/certification/{v3,v4}/pckcert
|
||||||
|
/// Retrieves a single SGX PCK certificate using a platform manifest and SVNs.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `platform_manifest` - Hex-encoded platform manifest.
|
||||||
|
/// * `cpusvn` - Hex-encoded CPUSVN value.
|
||||||
|
/// * `pcesvn` - Hex-encoded PCESVN value.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `subscription_key` - Optional subscription key if the Intel API requires it.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificateResponse`] containing the PEM-encoded certificate, issuer chain,
|
||||||
|
/// TCBm, and FMSPC.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or if the response is invalid.
|
||||||
|
/// Returns PEM Cert, Issuer Chain, TCBm, FMSPC.
|
||||||
|
pub async fn get_pck_certificate_by_manifest(
|
||||||
|
&self,
|
||||||
|
platform_manifest: &str,
|
||||||
|
cpusvn: &str,
|
||||||
|
pcesvn: &str,
|
||||||
|
pceid: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
) -> Result<PckCertificateResponse, IntelApiError> {
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcert")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
let request_body = PckCertRequest {
|
||||||
|
platform_manifest,
|
||||||
|
cpusvn,
|
||||||
|
pcesvn,
|
||||||
|
pceid,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut request_builder = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(header::CONTENT_TYPE, "application/json")
|
||||||
|
.json(&request_body);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.fetch_pck_certificate(request_builder).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /sgx/certification/{v3,v4}/pckcerts
|
||||||
|
/// Retrieves all SGX PCK certificates for a platform using encrypted PPID.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter
|
||||||
|
/// is only valid for API v4.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `encrypted_ppid` - Hex-encoded encrypted PPID.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `subscription_key` - Optional subscription key if the Intel API requires it.
|
||||||
|
/// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificatesResponse`] containing JSON with `{tcb, tcbm, cert}` entries,
|
||||||
|
/// as well as the issuer chain and FMSPC headers.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the API call fails or the response status is invalid.
|
||||||
|
pub async fn get_pck_certificates_by_ppid(
|
||||||
|
&self,
|
||||||
|
encrypted_ppid: &str,
|
||||||
|
pceid: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
ppid_encryption_key_type: Option<&str>,
|
||||||
|
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||||
|
// Check V4-only parameter
|
||||||
|
self.check_v4_only_param(ppid_encryption_key_type, "PPID-Encryption-Key")?;
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcerts")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("encrypted_ppid", encrypted_ppid)
|
||||||
|
.append_pair("pceid", pceid);
|
||||||
|
|
||||||
|
let mut request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only add for V4
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"PPID-Encryption-Key",
|
||||||
|
ppid_encryption_key_type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fetch_pck_certificates(request_builder).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /sgx/certification/{v3,v4}/pckcerts
|
||||||
|
/// Retrieves all SGX PCK certificates for a platform using a platform manifest.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `platform_manifest` - Hex-encoded platform manifest.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `subscription_key` - Optional subscription key if the Intel API requires it.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificatesResponse`] containing JSON with `{tcb, tcbm, cert}` entries,
|
||||||
|
/// as well as the issuer chain and FMSPC headers.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the API call fails or the response status is invalid.
|
||||||
|
pub async fn get_pck_certificates_by_manifest(
|
||||||
|
&self,
|
||||||
|
platform_manifest: &str,
|
||||||
|
pceid: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcerts")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
let request_body = PckCertsRequest {
|
||||||
|
platform_manifest,
|
||||||
|
pceid,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut request_builder = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(header::CONTENT_TYPE, "application/json")
|
||||||
|
.json(&request_body);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.fetch_pck_certificates(request_builder).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /sgx/certification/{v3,v4}/pckcerts/config (using PPID)
|
||||||
|
/// Retrieves SGX PCK certificates for a specific configuration (CPUSVN) using encrypted PPID.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter
|
||||||
|
/// is only valid for API v4. Returns JSON with `{tcb, tcbm, cert}` entries,
|
||||||
|
/// as well as the issuer chain and FMSPC headers.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `encrypted_ppid` - Hex-encoded encrypted PPID.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `cpusvn` - Hex-encoded CPUSVN value for the requested configuration.
|
||||||
|
/// * `subscription_key` - Optional subscription key if the Intel API requires it.
|
||||||
|
/// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificatesResponse`] with the requested config's certificate data.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or if the response status
|
||||||
|
/// is not `200 OK`.
|
||||||
|
pub async fn get_pck_certificates_config_by_ppid(
|
||||||
|
&self,
|
||||||
|
encrypted_ppid: &str,
|
||||||
|
pceid: &str,
|
||||||
|
cpusvn: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
ppid_encryption_key_type: Option<&str>,
|
||||||
|
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||||
|
// V3 does not support PPID-Encryption-Key header/type
|
||||||
|
if self.api_version == ApiVersion::V3 && ppid_encryption_key_type.is_some() {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"PPID-Encryption-Key header is only supported in API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcerts/config")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("encrypted_ppid", encrypted_ppid)
|
||||||
|
.append_pair("pceid", pceid)
|
||||||
|
.append_pair("cpusvn", cpusvn);
|
||||||
|
|
||||||
|
let mut request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only add for V4
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"PPID-Encryption-Key",
|
||||||
|
ppid_encryption_key_type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fetch_pck_certificates(request_builder).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /sgx/certification/{v3,v4}/pckcerts/config (using Manifest)
|
||||||
|
/// Retrieves SGX PCK certificates for a specific configuration (CPUSVN) using a platform manifest.
|
||||||
|
///
|
||||||
|
/// Optionally requires a subscription key. Returns JSON with `{tcb, tcbm, cert}` entries,
|
||||||
|
/// as well as the issuer chain and FMSPC headers.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `platform_manifest` - Hex-encoded platform manifest.
|
||||||
|
/// * `pceid` - Hex-encoded PCEID value.
|
||||||
|
/// * `cpusvn` - Hex-encoded CPUSVN value for the requested configuration.
|
||||||
|
/// * `subscription_key` - Optional subscription key if needed by the Intel API.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCertificatesResponse`] with the requested config's certificate data.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or if the response status
|
||||||
|
/// is not `200 OK`.
|
||||||
|
pub async fn get_pck_certificates_config_by_manifest(
|
||||||
|
&self,
|
||||||
|
platform_manifest: &str,
|
||||||
|
pceid: &str,
|
||||||
|
cpusvn: &str,
|
||||||
|
subscription_key: Option<&str>,
|
||||||
|
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcerts/config")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
let request_body = PckCertsConfigRequest {
|
||||||
|
platform_manifest,
|
||||||
|
pceid,
|
||||||
|
cpusvn,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut request_builder = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(header::CONTENT_TYPE, "application/json")
|
||||||
|
.json(&request_body);
|
||||||
|
|
||||||
|
request_builder = Self::maybe_add_header(
|
||||||
|
request_builder,
|
||||||
|
"Ocp-Apim-Subscription-Key",
|
||||||
|
subscription_key,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.fetch_pck_certificates(request_builder).await
|
||||||
|
}
|
||||||
|
}
|
69
crates/intel-dcap-api/src/client/pck_crl.rs
Normal file
69
crates/intel-dcap-api/src/client/pck_crl.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! PCK Certificate Revocation List
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::{check_status, IntelApiError},
|
||||||
|
responses::PckCrlResponse,
|
||||||
|
types::{CaType, CrlEncoding},
|
||||||
|
};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// GET /sgx/certification/{v3,v4}/pckcrl
|
||||||
|
/// Retrieves the PCK Certificate Revocation List (CRL) for a specified CA type.
|
||||||
|
///
|
||||||
|
/// Optionally takes an `encoding` parameter indicating whether the CRL should be
|
||||||
|
/// returned as PEM or DER. Defaults to PEM if not specified.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ca_type` - The type of CA to retrieve the CRL for (e.g., "processor" or "platform").
|
||||||
|
/// * `encoding` - An optional [`CrlEncoding`] (PEM or DER).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`PckCrlResponse`] containing the CRL data and the issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or if the response status
|
||||||
|
/// is not `200 OK`.
|
||||||
|
/// Optional 'encoding' parameter ("pem" or "der").
|
||||||
|
/// Returns CRL data (PEM or DER) and Issuer Chain header.
|
||||||
|
pub async fn get_pck_crl(
|
||||||
|
&self,
|
||||||
|
ca_type: CaType,
|
||||||
|
encoding: Option<CrlEncoding>,
|
||||||
|
) -> Result<PckCrlResponse, IntelApiError> {
|
||||||
|
let path = self.build_api_path("sgx", "", "pckcrl")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("ca", &ca_type.to_string());
|
||||||
|
|
||||||
|
if let Some(enc) = encoding {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("encoding", &enc.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
let response = request_builder.send().await?;
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
let issuer_chain = self.get_required_header(
|
||||||
|
&response,
|
||||||
|
"SGX-PCK-CRL-Issuer-Chain",
|
||||||
|
Some("SGX-PCK-CRL-Issuer-Chain"),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Response body is PEM or DER CRL
|
||||||
|
let crl_data = response.bytes().await?.to_vec();
|
||||||
|
|
||||||
|
Ok(PckCrlResponse {
|
||||||
|
crl_data,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
108
crates/intel-dcap-api/src/client/registration.rs
Normal file
108
crates/intel-dcap-api/src/client/registration.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Registration
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::{check_status, IntelApiError},
|
||||||
|
responses::AddPackageResponse,
|
||||||
|
};
|
||||||
|
use reqwest::{header, StatusCode};
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// POST /sgx/registration/v1/platform
|
||||||
|
/// Registers a multi-package SGX platform with the Intel Trusted Services API.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `platform_manifest` - Binary data representing the platform manifest.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Request body is binary Platform Manifest
|
||||||
|
/// Returns the hex-encoded PPID as a `String` upon success.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails or if the response status
|
||||||
|
/// is not HTTP `201 CREATED`.
|
||||||
|
pub async fn register_platform(
|
||||||
|
&self,
|
||||||
|
platform_manifest: Vec<u8>,
|
||||||
|
) -> Result<String, IntelApiError> {
|
||||||
|
// Registration paths are fixed, use the helper with "registration" service
|
||||||
|
let path = self.build_api_path("sgx", "registration", "platform")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(header::CONTENT_TYPE, "application/octet-stream")
|
||||||
|
.body(platform_manifest)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response = check_status(response, &[StatusCode::CREATED]).await?;
|
||||||
|
|
||||||
|
// Response body is hex-encoded PPID
|
||||||
|
let ppid_hex = response.text().await?;
|
||||||
|
Ok(ppid_hex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /sgx/registration/v1/package
|
||||||
|
/// Adds new package(s) to an already registered SGX platform instance.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `add_package_request` - Binary data for the "Add Package" request body.
|
||||||
|
/// * `subscription_key` - The subscription key required by the Intel API.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`AddPackageResponse`] containing the Platform Membership Certificates and
|
||||||
|
/// the count of them extracted from the response header.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the request fails, if the subscription key is invalid,
|
||||||
|
/// or if the response status is not HTTP `200 OK`.
|
||||||
|
pub async fn add_package(
|
||||||
|
&self,
|
||||||
|
add_package_request: Vec<u8>,
|
||||||
|
subscription_key: &str,
|
||||||
|
) -> Result<AddPackageResponse, IntelApiError> {
|
||||||
|
if subscription_key.is_empty() {
|
||||||
|
return Err(IntelApiError::InvalidSubscriptionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration paths are fixed
|
||||||
|
let path = self.build_api_path("sgx", "registration", "package")?;
|
||||||
|
let url = self.base_url.join(&path)?;
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header("Ocp-Apim-Subscription-Key", subscription_key)
|
||||||
|
.header(header::CONTENT_TYPE, "application/octet-stream")
|
||||||
|
.body(add_package_request)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||||
|
|
||||||
|
// Use the generic header helper, assuming header name is stable across reg versions
|
||||||
|
let cert_count_str = self.get_required_header(&response, "Certificate-Count", None)?;
|
||||||
|
let pck_cert_count: usize = cert_count_str.parse().map_err(|e: ParseIntError| {
|
||||||
|
IntelApiError::HeaderValueParse("Certificate-Count", e.to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Response body is a binary array of certificates
|
||||||
|
let pck_certs = response.bytes().await?.to_vec();
|
||||||
|
Ok(AddPackageResponse {
|
||||||
|
pck_certs,
|
||||||
|
pck_cert_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
167
crates/intel-dcap-api/src/client/tcb_info.rs
Normal file
167
crates/intel-dcap-api/src/client/tcb_info.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! TCB Info
|
||||||
|
|
||||||
|
use super::ApiClient; // Import from parent module
|
||||||
|
use crate::{
|
||||||
|
error::IntelApiError,
|
||||||
|
responses::TcbInfoResponse,
|
||||||
|
types::{ApiVersion, UpdateType},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ApiClient {
|
||||||
|
/// GET /sgx/certification/{v3,v4}/tcb
|
||||||
|
/// Retrieves SGX TCB information for a given FMSPC.
|
||||||
|
///
|
||||||
|
/// Returns TCB Info JSON string (Appendix A) and Issuer Chain header.
|
||||||
|
/// This function supports both API v3 and v4. The `update` and `tcbEvaluationDataNumber`
|
||||||
|
/// parameters are only supported by API v4. If both are provided at the same time (for v4),
|
||||||
|
/// a conflict error is returned.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `fmspc` - Hex-encoded FMSPC value.
|
||||||
|
/// * `update` - Optional [`UpdateType`] for API v4.
|
||||||
|
/// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`TcbInfoResponse`] containing the TCB info JSON and the issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if the API request fails, if conflicting parameters are used,
|
||||||
|
/// or if the requested TCB data is not found.
|
||||||
|
pub async fn get_sgx_tcb_info(
|
||||||
|
&self,
|
||||||
|
fmspc: &str,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<TcbInfoResponse, IntelApiError> {
|
||||||
|
// V3 does not support 'update' or 'tcbEvaluationDataNumber'
|
||||||
|
if self.api_version == ApiVersion::V3 && update.is_some() {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"'update' parameter requires API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if self.api_version == ApiVersion::V3 && tcb_evaluation_data_number.is_some() {
|
||||||
|
return Err(IntelApiError::UnsupportedApiVersion(
|
||||||
|
"'tcbEvaluationDataNumber' parameter requires API v4".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if self.api_version == ApiVersion::V4
|
||||||
|
&& update.is_some()
|
||||||
|
&& tcb_evaluation_data_number.is_some()
|
||||||
|
{
|
||||||
|
return Err(IntelApiError::ConflictingParameters(
|
||||||
|
"'update' and 'tcbEvaluationDataNumber'",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = self.build_api_path("sgx", "", "tcb")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut().append_pair("fmspc", fmspc);
|
||||||
|
|
||||||
|
// Add V4-specific parameters
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
if let Some(upd) = update {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("update", &upd.to_string());
|
||||||
|
}
|
||||||
|
if let Some(tedn) = tcb_evaluation_data_number {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("tcbEvaluationDataNumber", &tedn.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
// Special handling for 404/410 when tcbEvaluationDataNumber is specified (V4 only)
|
||||||
|
if self.api_version == ApiVersion::V4 {
|
||||||
|
if let Some(tedn_val) = tcb_evaluation_data_number {
|
||||||
|
// Use the helper function to check status before proceeding
|
||||||
|
self.check_tcb_evaluation_status(&request_builder, tedn_val, "SGX TCB Info")
|
||||||
|
.await?;
|
||||||
|
// If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JSON and header (header name seems same for v3/v4)
|
||||||
|
let (tcb_info_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(
|
||||||
|
request_builder,
|
||||||
|
"TCB-Info-Issuer-Chain",
|
||||||
|
Some("SGX-TCB-Info-Issuer-Chain"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(TcbInfoResponse {
|
||||||
|
tcb_info_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /tdx/certification/v4/tcb
|
||||||
|
/// Retrieves TDX TCB information for a given FMSPC (API v4 only).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `fmspc` - Hex-encoded FMSPC value.
|
||||||
|
/// * `update` - An optional [`UpdateType`] (v4 only).
|
||||||
|
/// * `tcb_evaluation_data_number` - An optional TCB Evaluation Data Number (v4 only).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A [`TcbInfoResponse`] containing TDX TCB info JSON and the issuer chain.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `IntelApiError` if an unsupported API version is used,
|
||||||
|
/// if there are conflicting parameters, or if the TDX TCB data is not found.
|
||||||
|
/// Returns TCB Info JSON string (Appendix A) and Issuer Chain header.
|
||||||
|
pub async fn get_tdx_tcb_info(
|
||||||
|
&self,
|
||||||
|
fmspc: &str,
|
||||||
|
update: Option<UpdateType>,
|
||||||
|
tcb_evaluation_data_number: Option<u64>,
|
||||||
|
) -> Result<TcbInfoResponse, IntelApiError> {
|
||||||
|
// Ensure V4 API
|
||||||
|
self.ensure_v4_api("get_tdx_tcb_info")?;
|
||||||
|
// Check conflicting parameters (only relevant for V4, checked inside helper)
|
||||||
|
self.check_conflicting_update_params(update, tcb_evaluation_data_number)?;
|
||||||
|
|
||||||
|
let path = self.build_api_path("tdx", "", "tcb")?;
|
||||||
|
let mut url = self.base_url.join(&path)?;
|
||||||
|
url.query_pairs_mut().append_pair("fmspc", fmspc);
|
||||||
|
|
||||||
|
if let Some(upd) = update {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("update", &upd.to_string());
|
||||||
|
}
|
||||||
|
if let Some(tedn) = tcb_evaluation_data_number {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("tcbEvaluationDataNumber", &tedn.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_builder = self.client.get(url);
|
||||||
|
|
||||||
|
// Special handling for 404/410 when tcbEvaluationDataNumber is specified
|
||||||
|
if let Some(tedn_val) = tcb_evaluation_data_number {
|
||||||
|
// Use the helper function to check status before proceeding
|
||||||
|
self.check_tcb_evaluation_status(&request_builder, tedn_val, "TDX TCB Info")
|
||||||
|
.await?;
|
||||||
|
// If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JSON and header (TDX only exists in V4)
|
||||||
|
let (tcb_info_json, issuer_chain) = self
|
||||||
|
.fetch_json_with_issuer_chain(request_builder, "TCB-Info-Issuer-Chain", None)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(TcbInfoResponse {
|
||||||
|
tcb_info_json,
|
||||||
|
issuer_chain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
104
crates/intel-dcap-api/src/error.rs
Normal file
104
crates/intel-dcap-api/src/error.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
use reqwest::{Response, StatusCode};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Represents all possible errors that can occur when interacting with Intel's DCAP API.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum IntelApiError {
|
||||||
|
/// Indicates that the requested API version or feature is unsupported.
|
||||||
|
#[error("Unsupported API version or feature: {0}")]
|
||||||
|
UnsupportedApiVersion(String),
|
||||||
|
|
||||||
|
/// Wraps an underlying reqwest error.
|
||||||
|
#[error("Reqwest error: {0}")]
|
||||||
|
Reqwest(#[from] reqwest::Error),
|
||||||
|
|
||||||
|
/// Wraps a URL parsing error.
|
||||||
|
#[error("URL parsing error: {0}")]
|
||||||
|
UrlParse(#[from] url::ParseError),
|
||||||
|
|
||||||
|
/// Wraps a Serde JSON error.
|
||||||
|
#[error("Serde JSON error: {0}")]
|
||||||
|
JsonError(#[from] serde_json::Error),
|
||||||
|
|
||||||
|
/// Represents a general API error, capturing the HTTP status and optional error details.
|
||||||
|
#[error("API Error: Status={status}, Request-ID={request_id}, Code={error_code:?}, Message={error_message:?}")]
|
||||||
|
ApiError {
|
||||||
|
/// HTTP status code returned by the API.
|
||||||
|
status: StatusCode,
|
||||||
|
/// The unique request identifier for tracing errors.
|
||||||
|
request_id: String,
|
||||||
|
/// An optional server-provided error code.
|
||||||
|
error_code: Option<String>,
|
||||||
|
/// An optional server-provided error message.
|
||||||
|
error_message: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Indicates that a header is missing or invalid.
|
||||||
|
#[error("Header missing or invalid: {0}")]
|
||||||
|
MissingOrInvalidHeader(&'static str),
|
||||||
|
|
||||||
|
/// Represents an invalid subscription key.
|
||||||
|
#[error("Invalid Subscription Key format")]
|
||||||
|
InvalidSubscriptionKey,
|
||||||
|
|
||||||
|
/// Indicates that conflicting parameters were supplied.
|
||||||
|
#[error("Cannot provide conflicting parameters: {0}")]
|
||||||
|
ConflictingParameters(&'static str),
|
||||||
|
|
||||||
|
/// Wraps a standard I/O error.
|
||||||
|
#[error("I/O Error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
/// Represents an error while parsing a header's value.
|
||||||
|
#[error("Header value parse error for '{0}': {1}")]
|
||||||
|
HeaderValueParse(&'static str, String),
|
||||||
|
|
||||||
|
/// Indicates an invalid parameter was provided.
|
||||||
|
#[error("Invalid parameter value: {0}")]
|
||||||
|
InvalidParameter(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts common API error details from response headers.
|
||||||
|
pub(crate) fn extract_api_error_details(
|
||||||
|
response: &Response,
|
||||||
|
) -> (String, Option<String>, Option<String>) {
|
||||||
|
let request_id = response
|
||||||
|
.headers()
|
||||||
|
.get("Request-ID")
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.unwrap_or("Unknown")
|
||||||
|
.to_string();
|
||||||
|
let error_code = response
|
||||||
|
.headers()
|
||||||
|
.get("Error-Code")
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.map(String::from);
|
||||||
|
let error_message = response
|
||||||
|
.headers()
|
||||||
|
.get("Error-Message")
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.map(String::from);
|
||||||
|
(request_id, error_code, error_message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the response status and returns an ApiError if it's not one of the expected statuses.
|
||||||
|
pub(crate) async fn check_status(
|
||||||
|
response: Response,
|
||||||
|
expected_statuses: &[StatusCode],
|
||||||
|
) -> Result<Response, IntelApiError> {
|
||||||
|
let status = response.status();
|
||||||
|
if expected_statuses.contains(&status) {
|
||||||
|
Ok(response)
|
||||||
|
} else {
|
||||||
|
let (request_id, error_code, error_message) = extract_api_error_details(&response);
|
||||||
|
Err(IntelApiError::ApiError {
|
||||||
|
status,
|
||||||
|
request_id,
|
||||||
|
error_code,
|
||||||
|
error_message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
53
crates/intel-dcap-api/src/lib.rs
Normal file
53
crates/intel-dcap-api/src/lib.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Intel API Client
|
||||||
|
//!
|
||||||
|
//! This module provides an API client for interacting with the Intel API for Trusted Services.
|
||||||
|
//! The API follows the documentation found at [Intel API Documentation](https://api.portal.trustedservices.intel.com/content/documentation.html).
|
||||||
|
//!
|
||||||
|
//! Create an [`ApiClient`] to interface with the Intel API.
|
||||||
|
//!
|
||||||
|
//! Example
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use intel_dcap_api::{ApiClient, IntelApiError, TcbInfoResponse};
|
||||||
|
//!
|
||||||
|
//! #[tokio::main]
|
||||||
|
//! async fn main() -> Result<(), IntelApiError> {
|
||||||
|
//! let client = ApiClient::new()?;
|
||||||
|
//!
|
||||||
|
//! // Example: Get SGX TCB Info
|
||||||
|
//! let fmspc_example = "00606A000000"; // Example FMSPC from docs
|
||||||
|
//! match client.get_sgx_tcb_info(fmspc_example, None, None).await {
|
||||||
|
//! Ok(TcbInfoResponse {
|
||||||
|
//! tcb_info_json,
|
||||||
|
//! issuer_chain,
|
||||||
|
//! }) => println!(
|
||||||
|
//! "SGX TCB Info for {}:\n{}\nIssuer Chain: {}",
|
||||||
|
//! fmspc_example, tcb_info_json, issuer_chain
|
||||||
|
//! ),
|
||||||
|
//! Err(e) => eprintln!("Error getting SGX TCB info: {}", e),
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
|
mod client;
|
||||||
|
mod error;
|
||||||
|
mod requests;
|
||||||
|
mod responses;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
// Re-export public items
|
||||||
|
pub use client::ApiClient;
|
||||||
|
pub use error::IntelApiError;
|
||||||
|
pub use responses::{
|
||||||
|
AddPackageResponse, EnclaveIdentityJson, EnclaveIdentityResponse, FmspcJsonResponse,
|
||||||
|
PckCertificateResponse, PckCertificatesResponse, PckCrlResponse, TcbEvaluationDataNumbersJson,
|
||||||
|
TcbEvaluationDataNumbersResponse, TcbInfoJson, TcbInfoResponse,
|
||||||
|
};
|
||||||
|
pub use types::{ApiVersion, CaType, CrlEncoding, PlatformFilter, UpdateType};
|
28
crates/intel-dcap-api/src/requests.rs
Normal file
28
crates/intel-dcap-api/src/requests.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct PckCertRequest<'a> {
|
||||||
|
#[serde(rename = "platformManifest")]
|
||||||
|
pub(crate) platform_manifest: &'a str,
|
||||||
|
pub(crate) cpusvn: &'a str,
|
||||||
|
pub(crate) pcesvn: &'a str,
|
||||||
|
pub(crate) pceid: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct PckCertsRequest<'a> {
|
||||||
|
#[serde(rename = "platformManifest")]
|
||||||
|
pub(crate) platform_manifest: &'a str,
|
||||||
|
pub(crate) pceid: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct PckCertsConfigRequest<'a> {
|
||||||
|
#[serde(rename = "platformManifest")]
|
||||||
|
pub(crate) platform_manifest: &'a str,
|
||||||
|
pub(crate) cpusvn: &'a str,
|
||||||
|
pub(crate) pceid: &'a str,
|
||||||
|
}
|
111
crates/intel-dcap-api/src/responses.rs
Normal file
111
crates/intel-dcap-api/src/responses.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
/// JSON structure as defined in Appendix A of the API spec.
|
||||||
|
/// Content may vary slightly between API v3 and v4.
|
||||||
|
pub type TcbInfoJson = String;
|
||||||
|
|
||||||
|
/// JSON structure as defined in Appendix B of the API spec.
|
||||||
|
/// Content may vary slightly between API v3 and v4.
|
||||||
|
pub type EnclaveIdentityJson = String;
|
||||||
|
|
||||||
|
/// JSON Array of {tcb, tcbm, cert}.
|
||||||
|
/// Content structure expected to be consistent between v3 and v4.
|
||||||
|
pub type PckCertsJsonResponse = String;
|
||||||
|
|
||||||
|
/// JSON Array of {fmspc, platform}.
|
||||||
|
/// Content structure expected to be consistent between v3 and v4.
|
||||||
|
pub type FmspcJsonResponse = String;
|
||||||
|
|
||||||
|
/// JSON structure as defined in Appendix C of the API spec (V4 ONLY).
|
||||||
|
pub type TcbEvaluationDataNumbersJson = String;
|
||||||
|
|
||||||
|
/// Response structure for a PCK (Platform Configuration Key) Certificate.
|
||||||
|
///
|
||||||
|
/// Contains the PCK certificate, its issuer chain, TCB measurement, and FMSPC value.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PckCertificateResponse {
|
||||||
|
/// PEM-encoded PCK certificate.
|
||||||
|
pub pck_cert_pem: String,
|
||||||
|
/// PEM-encoded certificate chain for the PCK certificate issuer.
|
||||||
|
/// Header name differs between v3 ("PCS-Certificate-Issuer-Chain") and v4 ("SGX-PCK-Certificate-Issuer-Chain").
|
||||||
|
pub issuer_chain: String,
|
||||||
|
/// TCBm value associated with the certificate (Hex-encoded).
|
||||||
|
pub tcbm: String,
|
||||||
|
/// FMSPC value associated with the certificate (Hex-encoded).
|
||||||
|
pub fmspc: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for multiple PCK (Platform Configuration Key) Certificates.
|
||||||
|
///
|
||||||
|
/// Contains a JSON array of PCK certificates, their issuer chain, and the associated FMSPC value.
|
||||||
|
/// This struct represents the response for retrieving multiple PCK certificates from the Intel SGX API.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PckCertificatesResponse {
|
||||||
|
/// JSON array containing PCK certificates and their associated TCB levels.
|
||||||
|
pub pck_certs_json: PckCertsJsonResponse, // String alias for now
|
||||||
|
/// PEM-encoded certificate chain for the PCK certificate issuer.
|
||||||
|
/// Header name differs between v3 ("PCS-Certificate-Issuer-Chain") and v4 ("SGX-PCK-Certificate-Issuer-Chain").
|
||||||
|
pub issuer_chain: String,
|
||||||
|
/// FMSPC value associated with the certificates (Hex-encoded).
|
||||||
|
pub fmspc: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for TCB (Trusted Computing Base) Information.
|
||||||
|
///
|
||||||
|
/// Contains the JSON representation of TCB information for a specific platform,
|
||||||
|
/// along with the certificate chain of the TCB Info signer.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TcbInfoResponse {
|
||||||
|
/// JSON containing TCB information for a specific platform (FMSPC).
|
||||||
|
pub tcb_info_json: TcbInfoJson, // String alias for now
|
||||||
|
/// PEM-encoded certificate chain for the TCB Info signer.
|
||||||
|
/// Header name differs slightly between v3 ("SGX-TCB-Info-Issuer-Chain") and v4 ("TCB-Info-Issuer-Chain" - check spec).
|
||||||
|
pub issuer_chain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for Enclave Identity Information.
|
||||||
|
///
|
||||||
|
/// Contains the JSON representation of enclave identity details for QE, QvE, or QAE,
|
||||||
|
/// along with its issuer chain.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EnclaveIdentityResponse {
|
||||||
|
/// JSON containing information about the QE, QvE, or QAE.
|
||||||
|
pub enclave_identity_json: EnclaveIdentityJson, // String alias for now
|
||||||
|
/// PEM-encoded certificate chain for the Enclave Identity signer.
|
||||||
|
/// Header name seems consistent ("SGX-Enclave-Identity-Issuer-Chain").
|
||||||
|
pub issuer_chain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for TCB Evaluation Data Numbers (V4 ONLY).
|
||||||
|
///
|
||||||
|
/// Contains the JSON representation of supported TCB Evaluation Data Numbers
|
||||||
|
/// and its corresponding issuer chain.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TcbEvaluationDataNumbersResponse {
|
||||||
|
/// JSON containing the list of supported TCB Evaluation Data Numbers (V4 ONLY).
|
||||||
|
pub tcb_evaluation_data_numbers_json: TcbEvaluationDataNumbersJson, // String alias for now
|
||||||
|
/// PEM-encoded certificate chain for the TCB Evaluation Data Numbers signer (V4 ONLY).
|
||||||
|
/// Header: "TCB-Evaluation-Data-Numbers-Issuer-Chain".
|
||||||
|
pub issuer_chain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for Platform Configuration Key Certificate Revocation List (PCK CRL).
|
||||||
|
///
|
||||||
|
/// Contains the CRL data and its issuer chain for validating platform configuration keys.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PckCrlResponse {
|
||||||
|
/// CRL data (PEM or DER encoded).
|
||||||
|
pub crl_data: Vec<u8>,
|
||||||
|
/// PEM-encoded certificate chain for the CRL issuer.
|
||||||
|
/// Header name differs between v3 ("PCS-CRL-Issuer-Chain") and v4 ("SGX-PCK-CRL-Issuer-Chain").
|
||||||
|
pub issuer_chain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response structure for the request to add a package.
|
||||||
|
pub struct AddPackageResponse {
|
||||||
|
/// Platform Membership Certificates
|
||||||
|
pub pck_certs: Vec<u8>,
|
||||||
|
/// The certificate count extracted from the response header.
|
||||||
|
pub pck_cert_count: usize,
|
||||||
|
}
|
122
crates/intel-dcap-api/src/types.rs
Normal file
122
crates/intel-dcap-api/src/types.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Represents the type of Certificate Authority (CA) for Intel Trusted Services.
|
||||||
|
///
|
||||||
|
/// This enum defines the different types of Certificate Authorities used in the Intel DCAP API,
|
||||||
|
/// specifically distinguishing between processor and platform CAs.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CaType {
|
||||||
|
/// Represents a processor-specific Certificate Authority.
|
||||||
|
Processor,
|
||||||
|
/// Represents a platform-wide Certificate Authority.
|
||||||
|
Platform,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CaType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
CaType::Processor => write!(f, "processor"),
|
||||||
|
CaType::Platform => write!(f, "platform"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the encoding format for Certificate Revocation Lists (CRLs).
|
||||||
|
///
|
||||||
|
/// This enum defines the supported encoding formats for CRLs in the Intel DCAP API,
|
||||||
|
/// distinguishing between PEM (Privacy Enhanced Mail) and DER (Distinguished Encoding Rules) formats.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CrlEncoding {
|
||||||
|
/// Represents the PEM (Privacy Enhanced Mail) encoding format.
|
||||||
|
Pem,
|
||||||
|
/// Represents the DER (Distinguished Encoding Rules) encoding format.
|
||||||
|
Der,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CrlEncoding {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
CrlEncoding::Pem => write!(f, "pem"),
|
||||||
|
CrlEncoding::Der => write!(f, "der"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the type of update for Intel Trusted Services.
|
||||||
|
///
|
||||||
|
/// This enum defines different update types, distinguishing between early and standard updates
|
||||||
|
/// in the Intel DCAP (Data Center Attestation Primitives) API.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum UpdateType {
|
||||||
|
/// Represents early updates, typically used for preview or beta releases.
|
||||||
|
Early,
|
||||||
|
/// Represents standard updates, which are the regular release cycle.
|
||||||
|
Standard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UpdateType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
UpdateType::Early => write!(f, "early"),
|
||||||
|
UpdateType::Standard => write!(f, "standard"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the platform filter options for Intel DCAP (Data Center Attestation Primitives) API.
|
||||||
|
///
|
||||||
|
/// This enum allows filtering platforms based on different criteria,
|
||||||
|
/// such as selecting all platforms, client-specific platforms, or specific Intel processor generations.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum PlatformFilter {
|
||||||
|
/// Represents a selection of all available platforms.
|
||||||
|
All,
|
||||||
|
/// Represents a selection of client-specific platforms.
|
||||||
|
Client,
|
||||||
|
/// Represents platforms with Intel E3 processors.
|
||||||
|
E3,
|
||||||
|
/// Represents platforms with Intel E5 processors.
|
||||||
|
E5,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PlatformFilter {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PlatformFilter::All => write!(f, "all"),
|
||||||
|
PlatformFilter::Client => write!(f, "client"),
|
||||||
|
PlatformFilter::E3 => write!(f, "E3"),
|
||||||
|
PlatformFilter::E5 => write!(f, "E5"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the version of the Intel Trusted Services API to target.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ApiVersion {
|
||||||
|
/// Represents version 3 of the Intel Trusted Services API.
|
||||||
|
V3,
|
||||||
|
/// Represents version 4 of the Intel Trusted Services API.
|
||||||
|
V4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiVersion {
|
||||||
|
/// Returns the string representation of the version for URL paths.
|
||||||
|
pub fn path_segment(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ApiVersion::V3 => "v3",
|
||||||
|
ApiVersion::V4 => "v4",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ApiVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ApiVersion::V3 => write!(f, "v3"),
|
||||||
|
ApiVersion::V4 => write!(f, "v4"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue