mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-22 23:44:48 +02:00
Compare commits
52 commits
intel-dcap
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8773078d5a | ||
![]() |
ce9560cff0 | ||
![]() |
093d6c44ed | ||
![]() |
ddbf099e45 | ||
![]() |
18ed1aa769 | ||
![]() |
49bb4a3bef | ||
![]() |
c34ff7ad27 | ||
![]() |
b4e0014e4e | ||
![]() |
8d965aa388 | ||
![]() |
412e3b1698 | ||
![]() |
f7c3717241 | ||
![]() |
c42d692863 | ||
![]() |
626dbbf846 | ||
![]() |
8c7922ae39 | ||
![]() |
716c782e6f | ||
![]() |
e78fb22f88 | ||
![]() |
a7e2939a54 | ||
![]() |
37e7f7f8e2 | ||
![]() |
7c133c4e4b | ||
![]() |
bb9c5b195e | ||
![]() |
205113ecfa | ||
![]() |
aeff962224 | ||
![]() |
c8692df37a | ||
![]() |
7c655d151c | ||
![]() |
426e22138e | ||
![]() |
b16592ec34 | ||
![]() |
119c2abe09 | ||
![]() |
5789fdd433 | ||
![]() |
de010fd093 | ||
![]() |
e039adf158 | ||
![]() |
f2718456ef | ||
![]() |
bef406c456 | ||
![]() |
bfd895e8f7 | ||
![]() |
8b01d8d5b0 | ||
![]() |
ad26c5e9ae | ||
![]() |
336576d812 | ||
![]() |
6379e9aa9e | ||
![]() |
1536e00d63 | ||
![]() |
2a8614c08f | ||
![]() |
905487dac8 | ||
![]() |
2bbfb2415c | ||
![]() |
fca60adc1a | ||
![]() |
2118466a8a | ||
![]() |
9bd0e9c36e | ||
![]() |
d54f7b14ad | ||
![]() |
2ca0b47169 | ||
![]() |
6a9e035d19 | ||
![]() |
36afc85d38 | ||
![]() |
2ff169da9f | ||
![]() |
0768b0ad67 | ||
![]() |
2dea589c0e | ||
![]() |
71a04ad4e2 |
116 changed files with 10093 additions and 1943 deletions
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
|||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: enarx/spdx@b5bfdd4410071bf058c8333d0e70020001524b6b
|
||||
- uses: enarx/spdx@d4020ee98e3101dd487c5184f27c6a6fb4f88709
|
||||
with:
|
||||
licenses: |-
|
||||
Apache-2.0
|
||||
|
@ -28,5 +28,5 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- run: nix run nixpkgs#taplo -- fmt --check
|
||||
|
|
2
.github/workflows/nix-non-x86.yml
vendored
2
.github/workflows/nix-non-x86.yml
vendored
|
@ -34,5 +34,5 @@ jobs:
|
|||
# FIXME: this prevents it from running on macos
|
||||
# https://github.com/NixOS/nix/pull/12570
|
||||
# run: nix run github:nixos/nixpkgs/nixos-24.11#nixci -- build
|
||||
run: nix build -L .#teepot
|
||||
run: nix build -L .#teepot --no-sandbox
|
||||
|
||||
|
|
11
.github/workflows/nix.yml
vendored
11
.github/workflows/nix.yml
vendored
|
@ -16,8 +16,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.28.3/install
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ github.token }}
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= tee-pot:SS6HcrpG87S1M6HZGPsfo7d1xJccCGev7/tXc5+I4jg=
|
||||
|
@ -37,8 +38,9 @@ jobs:
|
|||
runs-on: [matterlabs-default-infra-runners]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.28.3/install
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ github.token }}
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= tee-pot:SS6HcrpG87S1M6HZGPsfo7d1xJccCGev7/tXc5+I4jg=
|
||||
|
@ -76,8 +78,9 @@ jobs:
|
|||
- { nixpackage: 'container-tdx-test' }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.28.3/install
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ github.token }}
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= tee-pot:SS6HcrpG87S1M6HZGPsfo7d1xJccCGev7/tXc5+I4jg=
|
||||
|
@ -91,7 +94,7 @@ jobs:
|
|||
token: ${{ secrets.ATTIC_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
|
2
.github/workflows/secrets_scanner.yaml
vendored
2
.github/workflows/secrets_scanner.yaml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
- name: TruffleHog OSS
|
||||
uses: trufflesecurity/trufflehog@943daae06ba9cc80437a748155c818e9e3177a30 # v3.88.6
|
||||
uses: trufflesecurity/trufflehog@c8921694a53d95ce424af6ae76dbebf3b6a83aef # v3.88.30
|
||||
with:
|
||||
path: ./
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
|
|
2302
Cargo.lock
generated
2302
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
47
Cargo.toml
47
Cargo.toml
|
@ -1,13 +1,19 @@
|
|||
[workspace]
|
||||
members = ["crates/*", "bin/*", "crates/teepot-vault/bin/*"]
|
||||
exclude = ["crates/teepot-tee-quote-verification-rs"]
|
||||
resolver = "2"
|
||||
|
||||
# exclude x86_64 only crates
|
||||
exclude = [
|
||||
"crates/teepot-tee-quote-verification-rs",
|
||||
"crates/teepot-tdx-attest-rs",
|
||||
"crates/teepot-tdx-attest-sys",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
[workspace.package]
|
||||
version = "0.3.0"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
authors = ["Harald Hoyer <hh@matterlabs.dev>"]
|
||||
# rest of the workspace, if not specified in the package section
|
||||
|
@ -17,48 +23,59 @@ repository = "https://github.com/matter-labs/teepot"
|
|||
homepage = "https://github.com/matter-labs/teepot"
|
||||
|
||||
[workspace.dependencies]
|
||||
actix-http = "3"
|
||||
actix-web = { version = "4.5", features = ["rustls-0_23"] }
|
||||
anyhow = "1.0.82"
|
||||
asn1_der = { version = "0.7", default-features = false, features = ["native_types"] }
|
||||
async-trait = "0.1.86"
|
||||
awc = { version = "3.5", features = ["rustls-0_23-webpki-roots"] }
|
||||
base64 = "0.22.0"
|
||||
bytemuck = { version = "1.15.0", features = ["derive", "min_const_generics", "extern_crate_std"] }
|
||||
bytes = "1"
|
||||
chrono = "0.4.40"
|
||||
clap = { version = "4.5", features = ["std", "derive", "env", "error-context", "help", "usage", "wrap_help"], default-features = false }
|
||||
config = { version = "0.15.8", default-features = false, features = ["yaml", "json", "toml", "async"] }
|
||||
const-oid = { version = "0.9", default-features = false }
|
||||
const-oid = { version = "0.9.6", default-features = false }
|
||||
dcap-qvl = "0.2.3"
|
||||
enumset = { version = "1.1", features = ["serde"] }
|
||||
futures = "0.3.31"
|
||||
futures-core = { version = "0.3.30", default-features = false }
|
||||
getrandom = { version = "0.3.1", features = ["std"] }
|
||||
gpt = "4.0.0"
|
||||
hex = { version = "0.4.3", features = ["std"], default-features = false }
|
||||
intel-dcap-api = { path = "crates/intel-dcap-api" }
|
||||
jsonrpsee-types = "0.25.1"
|
||||
mockito = "1.4"
|
||||
num-integer = "0.1.46"
|
||||
num-traits = "0.2.18"
|
||||
opentelemetry = { version = "0.28.0", features = ["default", "logs"] }
|
||||
opentelemetry-appender-tracing = { version = "0.28.1", features = ["experimental_metadata_attributes", "log"] }
|
||||
opentelemetry-otlp = { version = "0.28.0", features = ["grpc-tonic", "logs"] }
|
||||
opentelemetry-semantic-conventions = { version = "0.28.0", features = ["semconv_experimental"] }
|
||||
opentelemetry_sdk = { version = "0.28.0", features = ["tokio", "rt-tokio"] }
|
||||
opentelemetry = { version = "0.30", features = ["default", "logs"] }
|
||||
opentelemetry-appender-tracing = { version = "0.30", features = ["experimental_metadata_attributes", "log"] }
|
||||
opentelemetry-otlp = { version = "0.30", features = ["grpc-tonic", "logs"] }
|
||||
opentelemetry-semantic-conventions = { version = "0.30", features = ["semconv_experimental"] }
|
||||
opentelemetry_sdk = { version = "0.30", features = ["tokio", "rt-tokio"] }
|
||||
p256 = "0.13.2"
|
||||
pe-sign = "0.1.10"
|
||||
pgp = "0.15"
|
||||
percent-encoding = "2.3.1"
|
||||
pgp = { version = "0.16", default-features = false }
|
||||
pkcs8 = { version = "0.10" }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
rsa = { version = "0.9.6", features = ["sha2", "pem"] }
|
||||
rustls = { version = "0.23.20", default-features = false, features = ["std", "logging", "tls12", "ring"] }
|
||||
secp256k1 = { version = "0.30", features = ["rand", "global-context"] }
|
||||
secp256k1 = { version = "0.31", features = ["rand", "global-context"] }
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
serde_json = "1"
|
||||
serde_with = { version = "3.8", features = ["base64", "hex"] }
|
||||
serde_yaml = "0.9.33"
|
||||
sha2 = "0.10.8"
|
||||
sha3 = "0.10.8"
|
||||
signature = "2.2.0"
|
||||
tdx-attest-rs = { version = "0.1.2", git = "https://github.com/intel/SGXDataCenterAttestationPrimitives.git", rev = "aa239d25a437a28f3f4de92c38f5b6809faac842" }
|
||||
teepot = { path = "crates/teepot" }
|
||||
teepot-vault = { path = "crates/teepot-vault" }
|
||||
teepot = { version = "0.6.0", path = "crates/teepot" }
|
||||
teepot-tee-quote-verification-rs = { version = "0.6.0", path = "crates/teepot-tee-quote-verification-rs" }
|
||||
teepot-vault = { version = "0.6.0", path = "crates/teepot-vault" }
|
||||
testaso = "0.1.0"
|
||||
thiserror = "2.0.11"
|
||||
tokio = { version = "1", features = ["sync", "macros", "rt-multi-thread", "fs", "time", "signal"] }
|
||||
tokio-util = "0.7.14"
|
||||
tracing = "0.1"
|
||||
tracing-actix-web = "0.7"
|
||||
tracing-futures = { version = "0.2.5", features = ["std"] }
|
||||
|
@ -66,5 +83,9 @@ tracing-log = "0.2"
|
|||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "ansi"] }
|
||||
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
||||
url = "2.5.2"
|
||||
webpki-roots = "1.0.0"
|
||||
x509-cert = { version = "0.2", features = ["builder", "signature", "default"] }
|
||||
zeroize = { version = "1.7.0", features = ["serde"] }
|
||||
zksync_basic_types = "28.6.0-non-semver-compat"
|
||||
zksync_types = "28.6.0-non-semver-compat"
|
||||
zksync_web3_decl = "28.6.0-non-semver-compat"
|
||||
|
|
|
@ -112,3 +112,12 @@ Attributes:
|
|||
```shell
|
||||
nixos-rebuild -L --flake .#tdxtest build-vm && ./result/bin/run-tdxtest-vm
|
||||
```
|
||||
|
||||
## Release
|
||||
|
||||
```shell
|
||||
$ cargo release 0.1.0 --manifest-path crates/teepot-tdx-attest-sys/Cargo.toml --sign
|
||||
$ cargo release 0.1.2 --manifest-path crates/teepot-tdx-attest-rs/Cargo.toml --sign
|
||||
$ cargo release 0.6.0 --manifest-path crates/teepot-tee-quote-verification-rs/Cargo.toml --sign
|
||||
$ cargo release 0.6.0 --sign
|
||||
```
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[package]
|
||||
name = "rtmr-calc"
|
||||
publish = false
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
|
|
@ -7,7 +7,7 @@ use pesign::PE;
|
|||
use sha2::{Digest, Sha384};
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
io::{Error, ErrorKind, Read, Seek, SeekFrom},
|
||||
io::{Error, Read, Seek, SeekFrom},
|
||||
path::PathBuf,
|
||||
};
|
||||
use teepot::{
|
||||
|
@ -65,6 +65,8 @@ impl Display for Rtmr {
|
|||
}
|
||||
}
|
||||
|
||||
const CHUNK_SIZE: u64 = 1024 * 128;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Arguments::parse();
|
||||
tracing::subscriber::set_global_default(setup_logging(
|
||||
|
@ -123,11 +125,11 @@ fn main() -> Result<()> {
|
|||
let pstart = header
|
||||
.part_start
|
||||
.checked_mul(lb_size.as_u64())
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "partition overflow - start offset"))?;
|
||||
.ok_or_else(|| Error::other("partition overflow - start offset"))?;
|
||||
let _ = device.seek(SeekFrom::Start(pstart))?;
|
||||
|
||||
assert_eq!(header.part_size, 128);
|
||||
assert!(header.num_parts < u8::MAX as _);
|
||||
assert!(header.num_parts < u32::from(u8::MAX));
|
||||
|
||||
let empty_bytes = [0u8; 128];
|
||||
|
||||
|
@ -158,7 +160,7 @@ fn main() -> Result<()> {
|
|||
|
||||
let section_table = pe.get_section_table()?;
|
||||
|
||||
for section in section_table.iter() {
|
||||
for section in §ion_table {
|
||||
debug!(section_name = ?section.name()?);
|
||||
}
|
||||
|
||||
|
@ -175,14 +177,13 @@ fn main() -> Result<()> {
|
|||
.find(|s| s.name().unwrap().eq(sect))
|
||||
.ok_or(anyhow!("Failed to find section `{sect}`"))?;
|
||||
|
||||
let mut start = s.pointer_to_raw_data as u64;
|
||||
let end = start + s.virtual_size as u64;
|
||||
let mut start = u64::from(s.pointer_to_raw_data);
|
||||
let end = start + u64::from(s.virtual_size);
|
||||
|
||||
debug!(sect, start, end, len = (s.virtual_size));
|
||||
|
||||
let mut hasher = Sha384::new();
|
||||
|
||||
const CHUNK_SIZE: u64 = 1024 * 128;
|
||||
loop {
|
||||
if start >= end {
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[package]
|
||||
name = "sha384-extend"
|
||||
publish = false
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
|
|
@ -61,13 +61,11 @@ pub fn extend_sha384(base: &str, extend: &str) -> Result<String> {
|
|||
let mut hasher = sha2::Sha384::new();
|
||||
|
||||
hasher.update(pad::<48>(&hex::decode(base).context(format!(
|
||||
"Failed to decode base digest '{}' - expected hex string",
|
||||
base
|
||||
"Failed to decode base digest '{base}' - expected hex string",
|
||||
))?)?);
|
||||
|
||||
hasher.update(pad::<48>(&hex::decode(extend).context(format!(
|
||||
"Failed to decode extend digest '{}' - expected hex string",
|
||||
extend
|
||||
"Failed to decode extend digest '{extend}' - expected hex string",
|
||||
))?)?);
|
||||
|
||||
Ok(hex::encode(hasher.finalize()))
|
||||
|
|
|
@ -6,6 +6,7 @@ authors.workspace = true
|
|||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
|
|
|
@ -27,10 +27,9 @@ fn main_with_error() -> Result<()> {
|
|||
use anyhow::Context;
|
||||
use secp256k1::{rand, Secp256k1};
|
||||
use std::{os::unix::process::CommandExt, process::Command};
|
||||
use teepot::tdx::rtmr::TdxRtmrEvent;
|
||||
use teepot::{
|
||||
ethereum::public_key_to_ethereum_address, prover::reportdata::ReportDataV1,
|
||||
quote::get_quote,
|
||||
quote::get_quote, tdx::rtmr::TdxRtmrEvent,
|
||||
};
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
|
||||
|
@ -45,7 +44,7 @@ fn main_with_error() -> Result<()> {
|
|||
tracing::subscriber::set_global_default(subscriber).context("Failed to set logger")?;
|
||||
|
||||
let args = Args::parse();
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
let secp = Secp256k1::new();
|
||||
let (signing_key, verifying_key) = secp.generate_keypair(&mut rng);
|
||||
let ethereum_address = public_key_to_ethereum_address(&verifying_key);
|
||||
|
|
|
@ -26,7 +26,7 @@ async fn main() -> Result<()> {
|
|||
.context("failed to get quote and collateral")?;
|
||||
|
||||
let base64_string = general_purpose::STANDARD.encode(report.quote.as_ref());
|
||||
print!("{}", base64_string);
|
||||
print!("{base64_string}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ fn print_quote_verification_summary(quote_verification_result: &QuoteVerificatio
|
|||
for advisory in advisories {
|
||||
println!("\tInfo: Advisory ID: {advisory}");
|
||||
}
|
||||
println!("Quote verification result: {}", tcblevel);
|
||||
println!("Quote verification result: {tcblevel}");
|
||||
|
||||
println!("{:#}", "e.report);
|
||||
}
|
||||
|
|
|
@ -12,20 +12,20 @@ bytes.workspace = true
|
|||
clap.workspace = true
|
||||
enumset.workspace = true
|
||||
hex.workspace = true
|
||||
jsonrpsee-types = "0.24"
|
||||
jsonrpsee-types.workspace = true
|
||||
reqwest.workspace = true
|
||||
secp256k1.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_with = { workspace = true, features = ["hex"] }
|
||||
serde_yaml = "0.9.33"
|
||||
serde_yaml.workspace = true
|
||||
teepot.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util = "0.7.14"
|
||||
tokio-util.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
url.workspace = true
|
||||
zksync_basic_types = "27.0.0-non-semver-compat"
|
||||
zksync_types = "27.0.0-non-semver-compat"
|
||||
zksync_web3_decl = "27.0.0-non-semver-compat"
|
||||
zksync_basic_types.workspace = true
|
||||
zksync_types.workspace = true
|
||||
zksync_web3_decl.workspace = true
|
||||
|
|
|
@ -26,12 +26,10 @@ impl MainNodeClient {
|
|||
/// Create a new client for the main node
|
||||
pub fn new(rpc_url: Url, chain_id: u64) -> error::Result<Self> {
|
||||
let chain_id = L2ChainId::try_from(chain_id)
|
||||
.map_err(|e| error::Error::Internal(format!("Invalid chain ID: {}", e)))?;
|
||||
.map_err(|e| error::Error::Internal(format!("Invalid chain ID: {e}")))?;
|
||||
|
||||
let node_client = NodeClient::http(rpc_url.into())
|
||||
.map_err(|e| {
|
||||
error::Error::Internal(format!("Failed to create JSON-RPC client: {}", e))
|
||||
})?
|
||||
.map_err(|e| error::Error::Internal(format!("Failed to create JSON-RPC client: {e}")))?
|
||||
.for_network(chain_id.into())
|
||||
.build();
|
||||
|
||||
|
@ -46,13 +44,13 @@ impl JsonRpcClient for MainNodeClient {
|
|||
.get_l1_batch_details(batch_number)
|
||||
.rpc_context("get_l1_batch_details")
|
||||
.await
|
||||
.map_err(|e| error::Error::JsonRpc(format!("Failed to get batch details: {}", e)))?
|
||||
.map_err(|e| error::Error::JsonRpc(format!("Failed to get batch details: {e}")))?
|
||||
.ok_or_else(|| {
|
||||
error::Error::JsonRpc(format!("No details found for batch #{}", batch_number))
|
||||
error::Error::JsonRpc(format!("No details found for batch #{batch_number}"))
|
||||
})?;
|
||||
|
||||
batch_details.base.root_hash.ok_or_else(|| {
|
||||
error::Error::JsonRpc(format!("No root hash found for batch #{}", batch_number))
|
||||
error::Error::JsonRpc(format!("No root hash found for batch #{batch_number}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,15 +190,12 @@ impl VerifierConfig {
|
|||
pub fn new(args: VerifierConfigArgs) -> error::Result<Self> {
|
||||
let policy = if let Some(path) = &args.attestation_policy_file {
|
||||
let policy_content = fs::read_to_string(path).map_err(|e| {
|
||||
error::Error::internal(format!("Failed to read attestation policy file: {}", e))
|
||||
error::Error::internal(format!("Failed to read attestation policy file: {e}"))
|
||||
})?;
|
||||
|
||||
let policy_config: AttestationPolicyConfig = serde_yaml::from_str(&policy_content)
|
||||
.map_err(|e| {
|
||||
error::Error::internal(format!(
|
||||
"Failed to parse attestation policy file: {}",
|
||||
e
|
||||
))
|
||||
error::Error::internal(format!("Failed to parse attestation policy file: {e}"))
|
||||
})?;
|
||||
|
||||
tracing::info!("Loaded attestation policy from file: {:?}", path);
|
||||
|
@ -263,7 +260,7 @@ fn decode_tdx_mrs(
|
|||
Some(mrs_array) => {
|
||||
let result = mrs_array
|
||||
.into_iter()
|
||||
.map(|strings| decode_and_combine_mrs(strings, bytes_length))
|
||||
.map(|strings| decode_and_combine_mrs(&strings, bytes_length))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
@ -272,12 +269,12 @@ fn decode_tdx_mrs(
|
|||
|
||||
// Helper function to decode and combine MRs
|
||||
fn decode_and_combine_mrs(
|
||||
strings: [String; 5],
|
||||
strings: &[String; 5],
|
||||
bytes_length: usize,
|
||||
) -> Result<Bytes, hex::FromHexError> {
|
||||
let mut buffer = BytesMut::with_capacity(bytes_length * 5);
|
||||
|
||||
for s in &strings {
|
||||
for s in strings {
|
||||
if s.len() > (bytes_length * 2) {
|
||||
return Err(hex::FromHexError::InvalidStringLength);
|
||||
}
|
||||
|
@ -295,19 +292,16 @@ fn parse_batch_range(s: &str) -> error::Result<(L1BatchNumber, L1BatchNumber)> {
|
|||
.map(L1BatchNumber::from)
|
||||
.map_err(|e| error::Error::internal(format!("Can't convert batch {s} to number: {e}")))
|
||||
};
|
||||
match s.split_once('-') {
|
||||
Some((start, end)) => {
|
||||
let (start, end) = (parse(start)?, parse(end)?);
|
||||
if start > end {
|
||||
Err(error::Error::InvalidBatchRange(s.into()))
|
||||
} else {
|
||||
Ok((start, end))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let batch_number = parse(s)?;
|
||||
Ok((batch_number, batch_number))
|
||||
if let Some((start, end)) = s.split_once('-') {
|
||||
let (start, end) = (parse(start)?, parse(end)?);
|
||||
if start > end {
|
||||
Err(error::Error::InvalidBatchRange(s.into()))
|
||||
} else {
|
||||
Ok((start, end))
|
||||
}
|
||||
} else {
|
||||
let batch_number = parse(s)?;
|
||||
Ok((batch_number, batch_number))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,13 +31,13 @@ impl fmt::Display for VerifierMode {
|
|||
end_batch,
|
||||
} => {
|
||||
if start_batch == end_batch {
|
||||
write!(f, "one-shot mode (batch {})", start_batch)
|
||||
write!(f, "one-shot mode (batch {start_batch})")
|
||||
} else {
|
||||
write!(f, "one-shot mode (batches {}-{})", start_batch, end_batch)
|
||||
write!(f, "one-shot mode (batches {start_batch}-{end_batch})")
|
||||
}
|
||||
}
|
||||
VerifierMode::Continuous { start_batch } => {
|
||||
write!(f, "continuous mode (starting from batch {})", start_batch)
|
||||
write!(f, "continuous mode (starting from batch {start_batch})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,9 @@ impl VerificationResult {
|
|||
verified_count,
|
||||
unverified_count,
|
||||
} => verified_count > unverified_count,
|
||||
VerificationResult::Failure => false,
|
||||
VerificationResult::Interrupted => false,
|
||||
VerificationResult::NoProofsFound => false,
|
||||
VerificationResult::Failure
|
||||
| VerificationResult::Interrupted
|
||||
| VerificationResult::NoProofsFound => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +89,7 @@ impl fmt::Display for VerificationResult {
|
|||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Partial Success ({} verified, {} failed)",
|
||||
verified_count, unverified_count
|
||||
"Partial Success ({verified_count} verified, {unverified_count} failed)"
|
||||
)
|
||||
}
|
||||
VerificationResult::Failure => write!(f, "Failure"),
|
||||
|
|
|
@ -96,7 +96,7 @@ impl Error {
|
|||
impl From<reqwest::Error> for Error {
|
||||
fn from(value: reqwest::Error) -> Self {
|
||||
Self::Http {
|
||||
status_code: value.status().map(|v| v.as_u16()).unwrap_or(0),
|
||||
status_code: value.status().map_or(0, |v| v.as_u16()),
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ async fn main() -> Result<()> {
|
|||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Task panicked: {}", e);
|
||||
Err(Error::internal(format!("Task panicked: {}", e)))
|
||||
Err(Error::internal(format!("Task panicked: {e}")))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -53,7 +53,7 @@ impl BatchProcessor {
|
|||
|
||||
// Fetch proofs for the current batch across different TEE types
|
||||
let mut proofs = Vec::new();
|
||||
for tee_type in self.config.args.tee_types.iter() {
|
||||
for tee_type in self.config.args.tee_types.iter().copied() {
|
||||
match self
|
||||
.proof_fetcher
|
||||
.get_proofs(token, batch_number, tee_type)
|
||||
|
@ -68,7 +68,6 @@ impl BatchProcessor {
|
|||
batch_number.0,
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,9 @@ impl ContinuousProcessor {
|
|||
match self.batch_processor.process_batch(token, batch).await {
|
||||
Ok(result) => {
|
||||
match result {
|
||||
VerificationResult::Success => success_count += 1,
|
||||
VerificationResult::PartialSuccess { .. } => success_count += 1,
|
||||
VerificationResult::Success | VerificationResult::PartialSuccess { .. } => {
|
||||
success_count += 1;
|
||||
}
|
||||
VerificationResult::Failure => failure_count += 1,
|
||||
VerificationResult::Interrupted => {
|
||||
results.push((current_batch, result));
|
||||
|
|
|
@ -43,14 +43,14 @@ impl ProcessorFactory {
|
|||
/// Create a new processor based on the provided configuration
|
||||
pub fn create(config: VerifierConfig) -> Result<(ProcessorType, VerifierMode)> {
|
||||
let mode = if let Some((start, end)) = config.args.batch_range {
|
||||
let processor = OneShotProcessor::new(config.clone(), start, end)?;
|
||||
let processor = OneShotProcessor::new(config, start, end)?;
|
||||
let mode = VerifierMode::OneShot {
|
||||
start_batch: start,
|
||||
end_batch: end,
|
||||
};
|
||||
(ProcessorType::OneShot(processor), mode)
|
||||
} else if let Some(start) = config.args.continuous {
|
||||
let processor = ContinuousProcessor::new(config.clone(), start)?;
|
||||
let processor = ContinuousProcessor::new(config, start)?;
|
||||
let mode = VerifierMode::Continuous { start_batch: start };
|
||||
(ProcessorType::Continuous(processor), mode)
|
||||
} else {
|
||||
|
|
|
@ -55,8 +55,9 @@ impl OneShotProcessor {
|
|||
let result = self.batch_processor.process_batch(token, batch).await?;
|
||||
|
||||
match result {
|
||||
VerificationResult::Success => success_count += 1,
|
||||
VerificationResult::PartialSuccess { .. } => success_count += 1,
|
||||
VerificationResult::Success | VerificationResult::PartialSuccess { .. } => {
|
||||
success_count += 1;
|
||||
}
|
||||
VerificationResult::Failure => failure_count += 1,
|
||||
VerificationResult::Interrupted => {
|
||||
results.push((batch_number, result));
|
||||
|
|
|
@ -36,7 +36,7 @@ impl ProofFetcher {
|
|||
&self,
|
||||
token: &CancellationToken,
|
||||
batch_number: L1BatchNumber,
|
||||
tee_type: &TeeType,
|
||||
tee_type: TeeType,
|
||||
) -> Result<Vec<Proof>> {
|
||||
let mut proofs_request = GetProofsRequest::new(batch_number, tee_type);
|
||||
let mut backoff = Duration::from_secs(1);
|
||||
|
|
|
@ -21,7 +21,7 @@ impl ProofResponseParser {
|
|||
}
|
||||
}
|
||||
|
||||
return Err(error::Error::JsonRpc(format!("JSONRPC error: {:?}", error)));
|
||||
return Err(error::Error::JsonRpc(format!("JSONRPC error: {error:?}")));
|
||||
}
|
||||
|
||||
// Extract proofs from the result
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct GetProofsRequest {
|
|||
|
||||
impl GetProofsRequest {
|
||||
/// Create a new request for the given batch number
|
||||
pub fn new(batch_number: L1BatchNumber, tee_type: &TeeType) -> Self {
|
||||
pub fn new(batch_number: L1BatchNumber, tee_type: TeeType) -> Self {
|
||||
GetProofsRequest {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: 1,
|
||||
|
|
|
@ -17,7 +17,7 @@ impl AttestationVerifier {
|
|||
// Get current time for verification
|
||||
let unix_time: i64 = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map_err(|e| error::Error::internal(format!("Failed to get system time: {}", e)))?
|
||||
.map_err(|e| error::Error::internal(format!("Failed to get system time: {e}")))?
|
||||
.as_secs() as _;
|
||||
|
||||
// Verify the quote with the collateral
|
||||
|
|
|
@ -48,7 +48,7 @@ impl<C: JsonRpcClient> BatchVerifier<C> {
|
|||
let mut total_proofs_count: u32 = 0;
|
||||
let mut verified_proofs_count: u32 = 0;
|
||||
|
||||
for proof in proofs.into_iter() {
|
||||
for proof in proofs {
|
||||
if token.is_cancelled() {
|
||||
tracing::warn!("Stop signal received during batch verification");
|
||||
return Ok(BatchVerificationResult {
|
||||
|
@ -119,7 +119,7 @@ impl<C: JsonRpcClient> BatchVerifier<C> {
|
|||
);
|
||||
verified_proofs_count += 1;
|
||||
} else {
|
||||
tracing::warn!(batch_no, proof.proved_at, tee_type, "Verification failed!",);
|
||||
tracing::warn!(batch_no, proof.proved_at, tee_type, "Verification failed!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ impl PolicyEnforcer {
|
|||
match "e.report {
|
||||
Report::SgxEnclave(report_body) => {
|
||||
// Validate TCB level
|
||||
Self::validate_tcb_level(&attestation_policy.sgx_allowed_tcb_levels, tcblevel)?;
|
||||
Self::validate_tcb_level(attestation_policy.sgx_allowed_tcb_levels, tcblevel)?;
|
||||
|
||||
// Validate SGX Advisories
|
||||
for advisory in "e_verification_result.advisories {
|
||||
|
@ -50,7 +50,7 @@ impl PolicyEnforcer {
|
|||
}
|
||||
Report::TD10(report_body) => {
|
||||
// Validate TCB level
|
||||
Self::validate_tcb_level(&attestation_policy.tdx_allowed_tcb_levels, tcblevel)?;
|
||||
Self::validate_tcb_level(attestation_policy.tdx_allowed_tcb_levels, tcblevel)?;
|
||||
|
||||
// Validate TDX Advisories
|
||||
for advisory in "e_verification_result.advisories {
|
||||
|
@ -74,7 +74,7 @@ impl PolicyEnforcer {
|
|||
}
|
||||
Report::TD15(report_body) => {
|
||||
// Validate TCB level
|
||||
Self::validate_tcb_level(&attestation_policy.tdx_allowed_tcb_levels, tcblevel)?;
|
||||
Self::validate_tcb_level(attestation_policy.tdx_allowed_tcb_levels, tcblevel)?;
|
||||
|
||||
// Validate TDX Advisories
|
||||
for advisory in "e_verification_result.advisories {
|
||||
|
@ -101,14 +101,10 @@ impl PolicyEnforcer {
|
|||
}
|
||||
|
||||
/// Helper method to validate TCB levels
|
||||
fn validate_tcb_level(
|
||||
allowed_levels: &EnumSet<TcbLevel>,
|
||||
actual_level: TcbLevel,
|
||||
) -> Result<()> {
|
||||
fn validate_tcb_level(allowed_levels: EnumSet<TcbLevel>, actual_level: TcbLevel) -> Result<()> {
|
||||
if !allowed_levels.contains(actual_level) {
|
||||
let error_msg = format!(
|
||||
"Quote verification failed: TCB level mismatch (expected one of: {:?}, actual: {})",
|
||||
allowed_levels, actual_level
|
||||
"Quote verification failed: TCB level mismatch (expected one of: {allowed_levels:?}, actual: {actual_level})",
|
||||
);
|
||||
return Err(Error::policy_violation(error_msg));
|
||||
}
|
||||
|
@ -117,7 +113,7 @@ impl PolicyEnforcer {
|
|||
|
||||
/// Helper method to build combined TDX measurement register
|
||||
fn build_tdx_mr<const N: usize>(parts: [&[u8]; N]) -> Vec<u8> {
|
||||
parts.into_iter().flatten().cloned().collect()
|
||||
parts.into_iter().flatten().copied().collect()
|
||||
}
|
||||
|
||||
/// Check if a policy value matches the actual value
|
||||
|
@ -152,8 +148,7 @@ impl PolicyEnforcer {
|
|||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let error_msg = format!(
|
||||
"Quote verification failed: {} mismatch (expected one of: [ {} ], actual: {:x})",
|
||||
field_name, valid_values, actual_value
|
||||
"Quote verification failed: {field_name} mismatch (expected one of: [ {valid_values} ], actual: {actual_value:x})"
|
||||
);
|
||||
return Err(Error::policy_violation(error_msg));
|
||||
}
|
||||
|
|
|
@ -30,9 +30,8 @@ impl SignatureVerifier {
|
|||
let report_data_bytes = quote_verification_result.quote.get_report_data();
|
||||
tracing::trace!(?report_data_bytes);
|
||||
|
||||
let report_data = ReportData::try_from(report_data_bytes).map_err(|e| {
|
||||
error::Error::internal(format!("Could not convert to ReportData: {}", e))
|
||||
})?;
|
||||
let report_data = ReportData::try_from(report_data_bytes)
|
||||
.map_err(|e| error::Error::internal(format!("Could not convert to ReportData: {e}")))?;
|
||||
|
||||
Self::verify(&report_data, &root_hash, signature)
|
||||
}
|
||||
|
@ -60,7 +59,7 @@ impl SignatureVerifier {
|
|||
let signature = Signature::from_compact(signature)
|
||||
.map_err(|e| error::Error::signature_verification(e.to_string()))?;
|
||||
let root_hash_msg = Message::from_digest(root_hash.0);
|
||||
Ok(signature.verify(&root_hash_msg, &report.pubkey).is_ok())
|
||||
Ok(signature.verify(root_hash_msg, &report.pubkey).is_ok())
|
||||
}
|
||||
|
||||
/// Verify a V1 report
|
||||
|
@ -100,7 +99,7 @@ impl SignatureVerifier {
|
|||
})?;
|
||||
|
||||
recover_signer(&signature_bytes, &root_hash_msg).map_err(|e| {
|
||||
error::Error::signature_verification(format!("Failed to recover signer: {}", e))
|
||||
error::Error::signature_verification(format!("Failed to recover signer: {e}"))
|
||||
})?
|
||||
}
|
||||
// Any other length is invalid
|
||||
|
@ -140,7 +139,7 @@ impl SignatureVerifier {
|
|||
continue;
|
||||
};
|
||||
|
||||
let Ok(public) = SECP256K1.recover_ecdsa(message, &rec_sig) else {
|
||||
let Ok(public) = SECP256K1.recover_ecdsa(*message, &rec_sig) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
143
crates/intel-dcap-api/CLAUDE.md
Normal file
143
crates/intel-dcap-api/CLAUDE.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
This crate (`intel-dcap-api`) is a Rust client library for Intel's Data Center Attestation Primitives (DCAP) API. It
|
||||
provides access to Intel's Trusted Services API for SGX and TDX attestation, including TCB info, PCK certificates, CRLs,
|
||||
and enclave identity verification.
|
||||
|
||||
## Features
|
||||
|
||||
- Support for both API v3 and v4
|
||||
- Async/await API using tokio
|
||||
- Comprehensive error handling with Intel-specific error codes
|
||||
- Type-safe request/response structures
|
||||
- Support for SGX and TDX platforms
|
||||
- Real data integration tests
|
||||
- **Automatic rate limit handling with configurable retries**
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Build
|
||||
cargo build
|
||||
cargo build --no-default-features --features rustls # Use rustls instead of default TLS
|
||||
|
||||
# Test
|
||||
cargo test
|
||||
|
||||
# Lint
|
||||
cargo clippy
|
||||
|
||||
# Examples
|
||||
cargo run --example example # Basic usage example
|
||||
cargo run --example get_pck_crl # Fetch certificate revocation lists
|
||||
cargo run --example common_usage # Common attestation verification patterns
|
||||
cargo run --example integration_test # Comprehensive test of most API endpoints
|
||||
cargo run --example fetch_test_data # Fetch real data from Intel API for tests
|
||||
cargo run --example handle_rate_limit # Demonstrate automatic rate limiting handling
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Client Structure
|
||||
|
||||
- **ApiClient** (`src/client/mod.rs`): Main entry point supporting API v3/v4
|
||||
- Base URL: https://api.trustedservices.intel.com
|
||||
- Manages HTTP client and API version selection
|
||||
- Automatic retry logic for 429 (Too Many Requests) responses
|
||||
- Default: 3 retries, configurable via `set_max_retries()`
|
||||
|
||||
### Key Modules
|
||||
|
||||
- **client/**: API endpoint implementations
|
||||
- `tcb_info`: SGX/TDX TCB information retrieval
|
||||
- `get_sgx_tcb_info()`, `get_tdx_tcb_info()`
|
||||
- `pck_cert`: PCK certificate operations
|
||||
- `get_pck_certificate_by_ppid()`, `get_pck_certificate_by_manifest()`
|
||||
- `get_pck_certificates_by_ppid()`, `get_pck_certificates_by_manifest()`
|
||||
- `get_pck_certificates_config_by_ppid()`, `get_pck_certificates_config_by_manifest()`
|
||||
- `pck_crl`: Certificate revocation lists
|
||||
- `get_pck_crl()` - supports PEM and DER encoding
|
||||
- `enclave_identity`: SGX QE/QVE/QAE/TDQE identity
|
||||
- `get_sgx_qe_identity()`, `get_sgx_qve_identity()`, `get_sgx_qae_identity()`, `get_tdx_qe_identity()`
|
||||
- `fmspc`: FMSPC-related operations (V4 only)
|
||||
- `get_fmspcs()` - with optional platform filter
|
||||
- `get_sgx_tcb_evaluation_data_numbers()`, `get_tdx_tcb_evaluation_data_numbers()`
|
||||
- `registration`: Platform registration
|
||||
- `register_platform()`, `add_package()`
|
||||
|
||||
### Core Types
|
||||
|
||||
- **error.rs**: `IntelApiError` for comprehensive error handling
|
||||
- Extracts error details from Error-Code and Error-Message headers
|
||||
- **`TooManyRequests` variant for rate limiting (429) after retry exhaustion**
|
||||
- **types.rs**: Enums (CaType, ApiVersion, UpdateType, etc.)
|
||||
- **requests.rs**: Request structures
|
||||
- **responses.rs**: Response structures with JSON and certificate data
|
||||
|
||||
### API Pattern
|
||||
|
||||
All client methods follow this pattern:
|
||||
|
||||
1. Build request with query parameters
|
||||
2. Send HTTP request with proper headers (with automatic retry on 429)
|
||||
3. Parse response (JSON + certificate chains)
|
||||
4. Return typed response or error
|
||||
|
||||
### Rate Limiting & Retry Logic
|
||||
|
||||
- **Automatic Retries**: All HTTP requests automatically retry on 429 (Too Many Requests) responses
|
||||
- **Retry Configuration**: Default 3 retries, configurable via `ApiClient::set_max_retries()`
|
||||
- **Retry-After Handling**: Waits for duration specified in Retry-After header before retrying
|
||||
- **Error Handling**: `IntelApiError::TooManyRequests` returned only after all retries exhausted
|
||||
- **Implementation**: `execute_with_retry()` in `src/client/helpers.rs` handles retry logic
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
- **Mock Tests**: Two test suites using mockito for HTTP mocking
|
||||
- `tests/mock_api_tests.rs`: Basic API functionality tests with simple data (11 tests)
|
||||
- `tests/real_data_mock_tests.rs`: Tests using real Intel API responses (25 tests)
|
||||
- **Test Data**: Real responses stored in `tests/test_data/` (JSON format)
|
||||
- Fetched using `cargo run --example fetch_test_data`
|
||||
- Includes TCB info, CRLs, enclave identities for both SGX and TDX
|
||||
- Covers V3 and V4 API variations, different update types, and evaluation data numbers
|
||||
- **Key Testing Considerations**:
|
||||
- Headers with newlines must be URL-encoded for mockito (use `percent_encode` with `NON_ALPHANUMERIC`)
|
||||
- V3 vs V4 API use different header names:
|
||||
- V3: `SGX-TCB-Info-Issuer-Chain`
|
||||
- V4: `TCB-Info-Issuer-Chain`
|
||||
- Error responses include Error-Code and Error-Message headers
|
||||
- Examples use real Intel API endpoints
|
||||
- Test data (FMSPC, PPID) from Intel documentation
|
||||
- Async tests require tokio runtime
|
||||
|
||||
## API Version Differences
|
||||
|
||||
### V4-Only Features
|
||||
|
||||
- FMSPC listing (`get_fmspcs()`)
|
||||
- TCB Evaluation Data Numbers endpoints
|
||||
- PPID encryption key type parameter
|
||||
- TDX QE identity endpoint
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Mockito Header Encoding**: Always URL-encode headers containing newlines/special characters
|
||||
2. **API Version Selection**: Some endpoints are V4-only and will return errors on V3
|
||||
3. **Rate Limiting**: Client automatically retries 429 responses; disable with `set_max_retries(0)` if manual handling
|
||||
needed
|
||||
4. **Platform Filters**: Only certain values are valid (All, Client, E3, E5)
|
||||
5. **Test Data**: PCK certificate endpoints require valid platform data and often need subscription keys
|
||||
6. **Issuer Chain Validation**: Always check that `issuer_chain` is non-empty - it's critical for signature verification
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **Certificate Chain Verification**: The `issuer_chain` field contains the certificates needed to verify the signature
|
||||
of the response data
|
||||
- **Signature Validation**: All JSON responses (TCB info, enclave identities) should have their signatures verified
|
||||
using the issuer chain
|
||||
- **CRL Verification**: PCK CRLs must be signature-verified before being used for certificate revocation checking
|
||||
- **Empty Issuer Chains**: Always validate that issuer chains are present and non-empty before trusting response data
|
|
@ -11,7 +11,8 @@ keywords = ["sgx", "tdx", "intel", "attestation", "confidential"]
|
|||
categories = ["api-bindings", "cryptography", "authentication"]
|
||||
|
||||
[dependencies]
|
||||
percent-encoding = "2.3.1"
|
||||
base64.workspace = true
|
||||
percent-encoding.workspace = true
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
@ -20,9 +21,15 @@ tokio.workspace = true
|
|||
url.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
base64.workspace = true
|
||||
hex.workspace = true
|
||||
mockito.workspace = true
|
||||
x509-cert.workspace = true
|
||||
|
||||
[[example]]
|
||||
name = "integration_test"
|
||||
required-features = ["default"]
|
||||
|
||||
[features]
|
||||
default = ["reqwest/default-tls"]
|
||||
rustls = ["reqwest/rustls-tls"]
|
||||
|
|
182
crates/intel-dcap-api/examples/common_usage.rs
Normal file
182
crates/intel-dcap-api/examples/common_usage.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
use intel_dcap_api::{ApiClient, CaType, IntelApiError, UpdateType};
|
||||
|
||||
/// Common usage patterns for the Intel DCAP API client
|
||||
///
|
||||
/// This example demonstrates typical use cases for attestation verification.
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a client (defaults to V4 API)
|
||||
let client = ApiClient::new()?;
|
||||
|
||||
// Example 1: Get TCB info for quote verification
|
||||
println!("Example 1: Getting TCB info for SGX quote verification");
|
||||
println!("======================================================");
|
||||
|
||||
let fmspc = "00906ED50000"; // From SGX quote
|
||||
|
||||
match client.get_sgx_tcb_info(fmspc, None, None).await {
|
||||
Ok(response) => {
|
||||
// Critical: Check that issuer chain is present for signature verification
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Error: Empty issuer chain - cannot verify TCB info signature!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("✓ Retrieved TCB info for FMSPC: {}", fmspc);
|
||||
|
||||
// Parse the TCB info
|
||||
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
||||
|
||||
// Extract useful information
|
||||
if let Some(tcb_levels) = tcb_info["tcbInfo"]["tcbLevels"].as_array() {
|
||||
println!(" Found {} TCB levels", tcb_levels.len());
|
||||
|
||||
// Show the latest TCB level
|
||||
if let Some(latest) = tcb_levels.first() {
|
||||
println!(" Latest TCB level:");
|
||||
if let Some(status) = latest["tcbStatus"].as_str() {
|
||||
println!(" Status: {}", status);
|
||||
}
|
||||
if let Some(date) = latest["tcbDate"].as_str() {
|
||||
println!(" Date: {}", date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The issuer chain is needed to verify the signature
|
||||
println!(
|
||||
" Issuer chain length: {} bytes",
|
||||
response.issuer_chain.len()
|
||||
);
|
||||
// Verify we have certificate chain for signature verification
|
||||
let cert_count = response.issuer_chain.matches("BEGIN CERTIFICATE").count();
|
||||
println!(" Certificate chain contains {} certificates", cert_count);
|
||||
}
|
||||
Err(IntelApiError::ApiError {
|
||||
status,
|
||||
error_message,
|
||||
..
|
||||
}) => {
|
||||
println!(
|
||||
"✗ API Error {}: {}",
|
||||
status,
|
||||
error_message.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
// Example 2: Get QE identity for enclave verification
|
||||
println!("Example 2: Getting QE identity for enclave verification");
|
||||
println!("======================================================");
|
||||
|
||||
match client.get_sgx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
// Critical: Check that issuer chain is present for signature verification
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Error: Empty issuer chain - cannot verify QE identity signature!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("✓ Retrieved QE identity");
|
||||
println!(
|
||||
" Issuer chain length: {} bytes",
|
||||
response.issuer_chain.len()
|
||||
);
|
||||
|
||||
let identity: serde_json::Value =
|
||||
serde_json::from_str(&response.enclave_identity_json)?;
|
||||
|
||||
if let Some(enclave_id) = identity["enclaveIdentity"]["id"].as_str() {
|
||||
println!(" Enclave ID: {}", enclave_id);
|
||||
}
|
||||
|
||||
if let Some(version) = identity["enclaveIdentity"]["version"].as_u64() {
|
||||
println!(" Version: {}", version);
|
||||
}
|
||||
|
||||
if let Some(mrsigner) = identity["enclaveIdentity"]["mrsigner"].as_str() {
|
||||
println!(" MRSIGNER: {}...", &mrsigner[..16]);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed to get QE identity: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
// Example 3: Check certificate revocation
|
||||
println!("Example 3: Checking certificate revocation status");
|
||||
println!("================================================");
|
||||
|
||||
match client.get_pck_crl(CaType::Processor, None).await {
|
||||
Ok(response) => {
|
||||
// Critical: Check that issuer chain is present for CRL verification
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Error: Empty issuer chain - cannot verify CRL signature!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("✓ Retrieved PCK CRL");
|
||||
println!(
|
||||
" Issuer chain length: {} bytes",
|
||||
response.issuer_chain.len()
|
||||
);
|
||||
|
||||
let crl_pem = String::from_utf8_lossy(&response.crl_data);
|
||||
|
||||
// In real usage, you would parse this CRL and check if a certificate is revoked
|
||||
if crl_pem.contains("BEGIN X509 CRL") {
|
||||
println!(" CRL format: PEM");
|
||||
println!(" CRL size: {} bytes", crl_pem.len());
|
||||
|
||||
// Count the revoked certificates (naive approach)
|
||||
let revoked_count = crl_pem.matches("Serial Number:").count();
|
||||
println!(" Approximate revoked certificates: {}", revoked_count);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed to get CRL: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
// Example 4: Early update for testing
|
||||
println!("Example 4: Getting early TCB update (for testing)");
|
||||
println!("================================================");
|
||||
|
||||
match client
|
||||
.get_sgx_tcb_info(fmspc, Some(UpdateType::Early), None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
println!("✓ Retrieved early TCB update");
|
||||
|
||||
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
||||
|
||||
if let Some(next_update) = tcb_info["tcbInfo"]["nextUpdate"].as_str() {
|
||||
println!(" Next update: {}", next_update);
|
||||
}
|
||||
}
|
||||
Err(IntelApiError::ApiError { status, .. }) if status.as_u16() == 404 => {
|
||||
println!(" No early update available (this is normal)");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("Done! These examples show common patterns for attestation verification.");
|
||||
|
||||
Ok(())
|
||||
}
|
515
crates/intel-dcap-api/examples/fetch_test_data.rs
Normal file
515
crates/intel-dcap-api/examples/fetch_test_data.rs
Normal file
|
@ -0,0 +1,515 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use intel_dcap_api::{ApiClient, ApiVersion, CaType, CrlEncoding, PlatformFilter, UpdateType};
|
||||
use std::{fs, path::Path};
|
||||
|
||||
/// Fetch real data from Intel API and save it as JSON files
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create test data directory
|
||||
let test_data_dir = Path::new("tests/test_data");
|
||||
fs::create_dir_all(test_data_dir)?;
|
||||
|
||||
let client = ApiClient::new()?;
|
||||
|
||||
println!("Fetching real test data from Intel API...");
|
||||
|
||||
// Keep track of successful fetches
|
||||
let mut successes: Vec<String> = Vec::new();
|
||||
let mut failures: Vec<String> = Vec::new();
|
||||
|
||||
// 1. Fetch SGX TCB info
|
||||
println!("\n1. Fetching SGX TCB info...");
|
||||
match client
|
||||
.get_sgx_tcb_info("00606A6A0000", Some(UpdateType::Standard), None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_tcb_info.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX TCB info".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX TCB info: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fetch TDX TCB info
|
||||
println!("\n2. Fetching TDX TCB info...");
|
||||
match client
|
||||
.get_tdx_tcb_info("00806F050000", Some(UpdateType::Standard), None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("tdx_tcb_info.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("TDX TCB info".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("TDX TCB info: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fetch PCK CRL for processor
|
||||
println!("\n3. Fetching PCK CRL (processor)...");
|
||||
match client.get_pck_crl(CaType::Processor, None).await {
|
||||
Ok(response) => {
|
||||
let crl_string = String::from_utf8_lossy(&response.crl_data);
|
||||
let data = serde_json::json!({
|
||||
"crl_data": crl_string,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("pck_crl_processor.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("PCK CRL (processor)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("PCK CRL (processor): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fetch PCK CRL for platform
|
||||
println!("\n4. Fetching PCK CRL (platform)...");
|
||||
match client.get_pck_crl(CaType::Platform, None).await {
|
||||
Ok(response) => {
|
||||
let crl_string = String::from_utf8_lossy(&response.crl_data);
|
||||
let data = serde_json::json!({
|
||||
"crl_data": crl_string,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("pck_crl_platform.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("PCK CRL (platform)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("PCK CRL (platform): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Fetch SGX QE identity
|
||||
println!("\n5. Fetching SGX QE identity...");
|
||||
match client.get_sgx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"enclave_identity_json": response.enclave_identity_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_qe_identity.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX QE identity".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX QE identity: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Fetch SGX QVE identity
|
||||
println!("\n6. Fetching SGX QVE identity...");
|
||||
match client.get_sgx_qve_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"enclave_identity_json": response.enclave_identity_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_qve_identity.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX QVE identity".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX QVE identity: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Fetch TDX QE identity
|
||||
println!("\n7. Fetching TDX QE identity...");
|
||||
match client.get_tdx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"enclave_identity_json": response.enclave_identity_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("tdx_qe_identity.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("TDX QE identity".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("TDX QE identity: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Try an alternative FMSPC
|
||||
println!("\n8. Fetching alternative SGX TCB info...");
|
||||
match client.get_sgx_tcb_info("00906ED50000", None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_tcb_info_alt.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("Alternative SGX TCB info".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("Alternative SGX TCB info: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Fetch PCK certificate
|
||||
println!("\n9. Attempting to fetch PCK certificate...");
|
||||
let ppid = "3d6dd97e96f84536a2267e727dd860e4fdd3ffa3e319db41e8f69c9a43399e7b7ce97d7eb3bd05b0a58bdb5b90a0e218";
|
||||
let cpusvn = "0606060606060606060606060606060606060606060606060606060606060606";
|
||||
let pcesvn = "0a00";
|
||||
let pceid = "0000";
|
||||
|
||||
match client
|
||||
.get_pck_certificate_by_ppid(ppid, cpusvn, pcesvn, pceid, None, None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"pck_cert_pem": response.pck_cert_pem,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
"tcbm": response.tcbm,
|
||||
"fmspc": response.fmspc,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("pck_cert.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("PCK certificate".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("PCK certificate: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Fetch SGX QAE identity
|
||||
println!("\n10. Fetching SGX QAE identity...");
|
||||
match client.get_sgx_qae_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"enclave_identity_json": response.enclave_identity_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_qae_identity.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX QAE identity".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX QAE identity: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Fetch FMSPCs
|
||||
println!("\n11. Fetching FMSPCs...");
|
||||
match client.get_fmspcs(Some(PlatformFilter::All)).await {
|
||||
Ok(fmspcs_json) => {
|
||||
let data = serde_json::json!({
|
||||
"fmspcs_json": fmspcs_json,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("fmspcs.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("FMSPCs".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("FMSPCs: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Fetch SGX TCB evaluation data numbers
|
||||
println!("\n12. Fetching SGX TCB evaluation data numbers...");
|
||||
match client.get_sgx_tcb_evaluation_data_numbers().await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_evaluation_data_numbers_json": response.tcb_evaluation_data_numbers_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_tcb_eval_nums.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX TCB evaluation data numbers".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX TCB evaluation data numbers: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 13. Fetch TDX TCB evaluation data numbers
|
||||
println!("\n13. Fetching TDX TCB evaluation data numbers...");
|
||||
match client.get_tdx_tcb_evaluation_data_numbers().await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_evaluation_data_numbers_json": response.tcb_evaluation_data_numbers_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("tdx_tcb_eval_nums.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("TDX TCB evaluation data numbers".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("TDX TCB evaluation data numbers: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Fetch PCK CRL with DER encoding
|
||||
println!("\n14. Fetching PCK CRL (processor, DER encoding)...");
|
||||
match client
|
||||
.get_pck_crl(CaType::Processor, Some(CrlEncoding::Der))
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
// For DER, save as base64
|
||||
let crl_base64 = general_purpose::STANDARD.encode(&response.crl_data);
|
||||
let data = serde_json::json!({
|
||||
"crl_data_base64": crl_base64,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("pck_crl_processor_der.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("PCK CRL (processor, DER)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("PCK CRL (processor, DER): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Try different update types
|
||||
println!("\n15. Fetching SGX TCB info with Early update...");
|
||||
match client
|
||||
.get_sgx_tcb_info("00906ED50000", Some(UpdateType::Early), None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_tcb_info_early.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX TCB info (Early update)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX TCB info (Early update): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 16. Try with specific TCB evaluation data number
|
||||
println!("\n16. Fetching TDX TCB info with specific evaluation number...");
|
||||
match client
|
||||
.get_tdx_tcb_info("00806F050000", None, Some(17))
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("tdx_tcb_info_eval17.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("TDX TCB info (eval number 17)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("TDX TCB info (eval number 17): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Try different FMSPCs
|
||||
println!("\n17. Fetching more SGX TCB info variations...");
|
||||
let test_fmspcs = vec!["00906ED50000", "00906C0F0000", "00A06F050000"];
|
||||
for fmspc in test_fmspcs {
|
||||
match client.get_sgx_tcb_info(fmspc, None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join(format!("sgx_tcb_info_{}.json", fmspc)),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push(format!("SGX TCB info (FMSPC: {})", fmspc));
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX TCB info (FMSPC: {}): {}", fmspc, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 18. Try FMSPCs with different platform filters
|
||||
println!("\n18. Fetching FMSPCs with different platform filters...");
|
||||
match client.get_fmspcs(None).await {
|
||||
Ok(fmspcs_json) => {
|
||||
let data = serde_json::json!({
|
||||
"fmspcs_json": fmspcs_json,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("fmspcs_no_filter.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("FMSPCs (no filter)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("FMSPCs (no filter): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
match client.get_fmspcs(Some(PlatformFilter::All)).await {
|
||||
Ok(fmspcs_json) => {
|
||||
let data = serde_json::json!({
|
||||
"fmspcs_json": fmspcs_json,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("fmspcs_all_platforms.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("FMSPCs (all platforms)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("FMSPCs (all platforms): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 19. Try PCK certificates with different parameters (encrypted PPID)
|
||||
println!("\n19. Attempting to fetch PCK certificates with different params...");
|
||||
// Try with a different encrypted PPID format
|
||||
let encrypted_ppid = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
let pceid = "0000";
|
||||
|
||||
match client
|
||||
.get_pck_certificates_by_ppid(encrypted_ppid, pceid, None, None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"pck_certificates_json": response.pck_certs_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
"fmspc": response.fmspc,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("pck_certificates.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("PCK certificates (by PPID)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("PCK certificates (by PPID): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// 20. Try TDX TCB info with different FMSPCs
|
||||
println!("\n20. Fetching TDX TCB info variations...");
|
||||
let tdx_fmspcs = vec!["00806F050000", "00A06F050000", "00606A0000000"];
|
||||
for fmspc in tdx_fmspcs {
|
||||
match client.get_tdx_tcb_info(fmspc, None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join(format!("tdx_tcb_info_{}.json", fmspc)),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push(format!("TDX TCB info (FMSPC: {})", fmspc));
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("TDX TCB info (FMSPC: {}): {}", fmspc, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 21. Try with V3 API for some endpoints
|
||||
println!("\n21. Testing V3 API endpoints...");
|
||||
let v3_client =
|
||||
ApiClient::new_with_options("https://api.trustedservices.intel.com", ApiVersion::V3)?;
|
||||
|
||||
match v3_client.get_sgx_tcb_info("00906ED50000", None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"tcb_info_json": response.tcb_info_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_tcb_info_v3.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX TCB info (V3 API)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX TCB info (V3 API): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
match v3_client.get_sgx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
let data = serde_json::json!({
|
||||
"enclave_identity_json": response.enclave_identity_json,
|
||||
"issuer_chain": response.issuer_chain,
|
||||
});
|
||||
fs::write(
|
||||
test_data_dir.join("sgx_qe_identity_v3.json"),
|
||||
serde_json::to_string_pretty(&data)?,
|
||||
)?;
|
||||
successes.push("SGX QE identity (V3 API)".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
failures.push(format!("SGX QE identity (V3 API): {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n\nTest data fetching complete!");
|
||||
println!("\nSuccessful fetches:");
|
||||
for s in &successes {
|
||||
println!(" ✓ {}", s);
|
||||
}
|
||||
|
||||
if !failures.is_empty() {
|
||||
println!("\nFailed fetches:");
|
||||
for f in &failures {
|
||||
println!(" ✗ {}", f);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nData saved in: {}", test_data_dir.display());
|
||||
|
||||
Ok(())
|
||||
}
|
91
crates/intel-dcap-api/examples/handle_rate_limit.rs
Normal file
91
crates/intel-dcap-api/examples/handle_rate_limit.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
//! Example demonstrating automatic rate limit handling
|
||||
//!
|
||||
//! The Intel DCAP API client now automatically handles 429 Too Many Requests responses
|
||||
//! by retrying up to 3 times by default. This example shows how to configure the retry
|
||||
//! behavior and handle cases where all retries are exhausted.
|
||||
|
||||
use intel_dcap_api::{ApiClient, IntelApiError};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create API client with default settings (3 retries)
|
||||
let mut client = ApiClient::new()?;
|
||||
|
||||
println!("Example 1: Default behavior (automatic retries)");
|
||||
println!("================================================");
|
||||
|
||||
// Example FMSPC value
|
||||
let fmspc = "00606A000000";
|
||||
|
||||
// The client will automatically retry up to 3 times if rate limited
|
||||
match client.get_sgx_tcb_info(fmspc, None, None).await {
|
||||
Ok(tcb_info) => {
|
||||
println!("✓ Successfully retrieved TCB info");
|
||||
println!(
|
||||
" TCB Info JSON length: {} bytes",
|
||||
tcb_info.tcb_info_json.len()
|
||||
);
|
||||
println!(
|
||||
" Issuer Chain length: {} bytes",
|
||||
tcb_info.issuer_chain.len()
|
||||
);
|
||||
}
|
||||
Err(IntelApiError::TooManyRequests {
|
||||
request_id,
|
||||
retry_after,
|
||||
}) => {
|
||||
println!("✗ Rate limited even after 3 automatic retries");
|
||||
println!(" Request ID: {}", request_id);
|
||||
println!(" Last retry-after was: {} seconds", retry_after);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("✗ Other error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nExample 2: Custom retry configuration");
|
||||
println!("=====================================");
|
||||
|
||||
// Configure client to retry up to 5 times
|
||||
client.set_max_retries(5);
|
||||
println!("Set max retries to 5");
|
||||
|
||||
match client.get_sgx_tcb_info(fmspc, None, None).await {
|
||||
Ok(_) => println!("✓ Request succeeded"),
|
||||
Err(IntelApiError::TooManyRequests { .. }) => {
|
||||
println!("✗ Still rate limited after 5 retries")
|
||||
}
|
||||
Err(e) => eprintln!("✗ Error: {}", e),
|
||||
}
|
||||
|
||||
println!("\nExample 3: Disable automatic retries");
|
||||
println!("====================================");
|
||||
|
||||
// Disable automatic retries
|
||||
client.set_max_retries(0);
|
||||
println!("Disabled automatic retries");
|
||||
|
||||
match client.get_sgx_tcb_info(fmspc, None, None).await {
|
||||
Ok(_) => println!("✓ Request succeeded on first attempt"),
|
||||
Err(IntelApiError::TooManyRequests {
|
||||
request_id,
|
||||
retry_after,
|
||||
}) => {
|
||||
println!("✗ Rate limited (no automatic retry)");
|
||||
println!(" Request ID: {}", request_id);
|
||||
println!(" Retry after: {} seconds", retry_after);
|
||||
println!(" You would need to implement manual retry logic here");
|
||||
}
|
||||
Err(e) => eprintln!("✗ Error: {}", e),
|
||||
}
|
||||
|
||||
println!("\nNote: The client handles rate limiting automatically!");
|
||||
println!("You only need to handle TooManyRequests errors if:");
|
||||
println!("- You disable automatic retries (set_max_retries(0))");
|
||||
println!("- All automatic retries are exhausted");
|
||||
|
||||
Ok(())
|
||||
}
|
495
crates/intel-dcap-api/examples/integration_test.rs
Normal file
495
crates/intel-dcap-api/examples/integration_test.rs
Normal file
|
@ -0,0 +1,495 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
use intel_dcap_api::{
|
||||
ApiClient, ApiVersion, CaType, CrlEncoding, IntelApiError, PlatformFilter, UpdateType,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
/// Comprehensive integration test example demonstrating most Intel DCAP API client functions
|
||||
///
|
||||
/// This example shows how to use various endpoints of the Intel Trusted Services API.
|
||||
/// Note: Some operations may fail with 404 or 400 errors if the data doesn't exist on Intel's servers.
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== Intel DCAP API Integration Test Example ===\n");
|
||||
|
||||
// Create clients for both V3 and V4 APIs
|
||||
let v4_client = ApiClient::new()?;
|
||||
let v3_client =
|
||||
ApiClient::new_with_options("https://api.trustedservices.intel.com", ApiVersion::V3)?;
|
||||
|
||||
// Track successes and failures
|
||||
let mut results = Vec::new();
|
||||
|
||||
// Test FMSPC - commonly used for TCB lookups
|
||||
let test_fmspc = "00906ED50000";
|
||||
let test_fmspc_tdx = "00806F050000";
|
||||
|
||||
println!("1. Testing TCB Info Endpoints...");
|
||||
println!("================================");
|
||||
|
||||
// 1.1 SGX TCB Info (V4)
|
||||
print!(" - SGX TCB Info (V4): ");
|
||||
match v4_client.get_sgx_tcb_info(test_fmspc, None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX TCB Info (V4)", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" FMSPC: {}", test_fmspc);
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
||||
if let Some(version) = tcb_info["tcbInfo"]["version"].as_u64() {
|
||||
println!(" TCB Info Version: {}", version);
|
||||
}
|
||||
results.push(("SGX TCB Info (V4)", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX TCB Info (V4)", false));
|
||||
}
|
||||
}
|
||||
|
||||
// Add small delay between requests to be nice to the API
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 1.2 SGX TCB Info (V3)
|
||||
print!(" - SGX TCB Info (V3): ");
|
||||
match v3_client.get_sgx_tcb_info(test_fmspc, None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX TCB Info (V3)", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
results.push(("SGX TCB Info (V3)", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX TCB Info (V3)", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 1.3 TDX TCB Info
|
||||
print!(" - TDX TCB Info: ");
|
||||
match v4_client.get_tdx_tcb_info(test_fmspc_tdx, None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("TDX TCB Info", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
||||
if let Some(id) = tcb_info["tcbInfo"]["id"].as_str() {
|
||||
println!(" Platform: {}", id);
|
||||
}
|
||||
results.push(("TDX TCB Info", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("TDX TCB Info", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 1.4 SGX TCB Info with Early Update
|
||||
print!(" - SGX TCB Info (Early Update): ");
|
||||
match v4_client
|
||||
.get_sgx_tcb_info(test_fmspc, Some(UpdateType::Early), None)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX TCB Info (Early)", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
results.push(("SGX TCB Info (Early)", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX TCB Info (Early)", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
println!("\n2. Testing Enclave Identity Endpoints...");
|
||||
println!("========================================");
|
||||
|
||||
// 2.1 SGX QE Identity
|
||||
print!(" - SGX QE Identity: ");
|
||||
match v4_client.get_sgx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX QE Identity", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let identity: serde_json::Value =
|
||||
serde_json::from_str(&response.enclave_identity_json)?;
|
||||
if let Some(id) = identity["enclaveIdentity"]["id"].as_str() {
|
||||
println!(" Enclave ID: {}", id);
|
||||
}
|
||||
results.push(("SGX QE Identity", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX QE Identity", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 2.2 SGX QVE Identity
|
||||
print!(" - SGX QVE Identity: ");
|
||||
match v4_client.get_sgx_qve_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX QVE Identity", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
results.push(("SGX QVE Identity", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX QVE Identity", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 2.3 SGX QAE Identity
|
||||
print!(" - SGX QAE Identity: ");
|
||||
match v4_client.get_sgx_qae_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX QAE Identity", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
results.push(("SGX QAE Identity", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX QAE Identity", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 2.4 TDX QE Identity (V4 only)
|
||||
print!(" - TDX QE Identity: ");
|
||||
match v4_client.get_tdx_qe_identity(None, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("TDX QE Identity", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
results.push(("TDX QE Identity", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("TDX QE Identity", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
println!("\n3. Testing PCK CRL Endpoints...");
|
||||
println!("================================");
|
||||
|
||||
// 3.1 PCK CRL - Processor (PEM)
|
||||
print!(" - PCK CRL (Processor, PEM): ");
|
||||
match v4_client.get_pck_crl(CaType::Processor, None).await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("PCK CRL (Processor)", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let crl_str = String::from_utf8_lossy(&response.crl_data);
|
||||
if crl_str.contains("BEGIN X509 CRL") {
|
||||
println!(" Format: PEM");
|
||||
}
|
||||
results.push(("PCK CRL (Processor)", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("PCK CRL (Processor)", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 3.2 PCK CRL - Platform (DER)
|
||||
print!(" - PCK CRL (Platform, DER): ");
|
||||
match v4_client
|
||||
.get_pck_crl(CaType::Platform, Some(CrlEncoding::Der))
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("PCK CRL (Platform, DER)", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
println!(" CRL size: {} bytes", response.crl_data.len());
|
||||
results.push(("PCK CRL (Platform, DER)", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("PCK CRL (Platform, DER)", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
println!("\n4. Testing FMSPC Endpoints (V4 only)...");
|
||||
println!("=======================================");
|
||||
|
||||
// 4.1 Get FMSPCs (no filter)
|
||||
print!(" - Get FMSPCs (no filter): ");
|
||||
match v4_client.get_fmspcs(None).await {
|
||||
Ok(fmspcs_json) => {
|
||||
println!("✓ Success");
|
||||
let fmspcs: serde_json::Value = serde_json::from_str(&fmspcs_json)?;
|
||||
if let Some(arr) = fmspcs.as_array() {
|
||||
println!(" Total FMSPCs: {}", arr.len());
|
||||
// Show first few FMSPCs
|
||||
for (i, fmspc) in arr.iter().take(3).enumerate() {
|
||||
if let (Some(fmspc_val), Some(platform)) =
|
||||
(fmspc["fmspc"].as_str(), fmspc["platform"].as_str())
|
||||
{
|
||||
println!(" [{}] {} - {}", i + 1, fmspc_val, platform);
|
||||
}
|
||||
}
|
||||
if arr.len() > 3 {
|
||||
println!(" ... and {} more", arr.len() - 3);
|
||||
}
|
||||
}
|
||||
results.push(("Get FMSPCs", true));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("Get FMSPCs", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 4.2 Get FMSPCs with platform filter
|
||||
print!(" - Get FMSPCs (All platforms): ");
|
||||
match v4_client.get_fmspcs(Some(PlatformFilter::All)).await {
|
||||
Ok(_) => {
|
||||
println!("✓ Success");
|
||||
results.push(("Get FMSPCs (filtered)", true));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("Get FMSPCs (filtered)", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
println!("\n5. Testing TCB Evaluation Data Numbers (V4 only)...");
|
||||
println!("===================================================");
|
||||
|
||||
// 5.1 SGX TCB Evaluation Data Numbers
|
||||
print!(" - SGX TCB Evaluation Data Numbers: ");
|
||||
match v4_client.get_sgx_tcb_evaluation_data_numbers().await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("SGX TCB Eval Numbers", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let data: serde_json::Value =
|
||||
serde_json::from_str(&response.tcb_evaluation_data_numbers_json)?;
|
||||
if let Some(sgx_data) = data.get("sgx") {
|
||||
println!(
|
||||
" SGX entries: {}",
|
||||
sgx_data.as_array().map(|a| a.len()).unwrap_or(0)
|
||||
);
|
||||
}
|
||||
results.push(("SGX TCB Eval Numbers", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("SGX TCB Eval Numbers", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// 5.2 TDX TCB Evaluation Data Numbers
|
||||
print!(" - TDX TCB Evaluation Data Numbers: ");
|
||||
match v4_client.get_tdx_tcb_evaluation_data_numbers().await {
|
||||
Ok(response) => {
|
||||
if response.issuer_chain.is_empty() {
|
||||
println!("✗ Failed: Empty issuer chain");
|
||||
results.push(("TDX TCB Eval Numbers", false));
|
||||
} else {
|
||||
println!("✓ Success");
|
||||
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
||||
let data: serde_json::Value =
|
||||
serde_json::from_str(&response.tcb_evaluation_data_numbers_json)?;
|
||||
if let Some(tdx_data) = data.get("tdx") {
|
||||
println!(
|
||||
" TDX entries: {}",
|
||||
tdx_data.as_array().map(|a| a.len()).unwrap_or(0)
|
||||
);
|
||||
}
|
||||
results.push(("TDX TCB Eval Numbers", true));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Failed: {:?}", e);
|
||||
results.push(("TDX TCB Eval Numbers", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
println!("\n6. Testing PCK Certificate Endpoints...");
|
||||
println!("=======================================");
|
||||
|
||||
/* // 6.1 PCK Certificate by PPID (usually requires valid data)
|
||||
print!(" - PCK Certificate by PPID: ");
|
||||
let test_ppid = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
let test_cpusvn = "00000000000000000000000000000000";
|
||||
let test_pcesvn = "0000";
|
||||
let test_pceid = "0000";
|
||||
|
||||
match v4_client
|
||||
.get_pck_certificate_by_ppid(test_ppid, test_cpusvn, test_pcesvn, test_pceid, None, None)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
println!("✓ Success");
|
||||
results.push(("PCK Certificate", true));
|
||||
}
|
||||
Err(e) => {
|
||||
// Expected to fail with test data
|
||||
match &e {
|
||||
IntelApiError::ApiError { status, .. } => {
|
||||
println!("✗ Failed (Expected): HTTP {}", status);
|
||||
}
|
||||
_ => println!("✗ Failed: {:?}", e),
|
||||
}
|
||||
results.push(("PCK Certificate", false));
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
*/
|
||||
println!("\n7. Testing API Version Compatibility...");
|
||||
println!("=======================================");
|
||||
|
||||
// 7.1 Try V4-only endpoint on V3
|
||||
print!(" - V4-only endpoint on V3 (should fail): ");
|
||||
match v3_client.get_fmspcs(None).await {
|
||||
Ok(_) => {
|
||||
println!("✗ Unexpected success!");
|
||||
results.push(("V3/V4 compatibility check", false));
|
||||
}
|
||||
Err(IntelApiError::UnsupportedApiVersion(_)) => {
|
||||
println!("✓ Correctly rejected");
|
||||
results.push(("V3/V4 compatibility check", true));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Wrong error: {:?}", e);
|
||||
results.push(("V3/V4 compatibility check", false));
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n8. Testing Error Handling...");
|
||||
println!("============================");
|
||||
|
||||
// 8.1 Invalid FMSPC
|
||||
print!(" - Invalid FMSPC format: ");
|
||||
match v4_client.get_sgx_tcb_info("invalid", None, None).await {
|
||||
Ok(_) => {
|
||||
println!("✗ Unexpected success!");
|
||||
results.push(("Error handling", false));
|
||||
}
|
||||
Err(IntelApiError::ApiError {
|
||||
status,
|
||||
error_code,
|
||||
error_message,
|
||||
..
|
||||
}) => {
|
||||
println!("✓ Correctly handled");
|
||||
println!(" Status: {}", status);
|
||||
if let Some(code) = error_code {
|
||||
println!(" Error Code: {}", code);
|
||||
}
|
||||
if let Some(msg) = error_message {
|
||||
println!(" Error Message: {}", msg);
|
||||
}
|
||||
results.push(("Error handling", true));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("✗ Unexpected error: {:?}", e);
|
||||
results.push(("Error handling", false));
|
||||
}
|
||||
}
|
||||
|
||||
// Summary
|
||||
println!("\n\n=== Summary ===");
|
||||
println!("===============");
|
||||
|
||||
let total = results.len();
|
||||
let successful = results.iter().filter(|(_, success)| *success).count();
|
||||
let failed = total - successful;
|
||||
|
||||
println!("Total tests: {}", total);
|
||||
println!(
|
||||
"Successful: {} ({}%)",
|
||||
successful,
|
||||
(successful * 100) / total
|
||||
);
|
||||
println!("Failed: {} ({}%)", failed, (failed * 100) / total);
|
||||
|
||||
println!("\nDetailed Results:");
|
||||
for (test, success) in &results {
|
||||
println!(" {} {}", if *success { "✓" } else { "✗" }, test);
|
||||
}
|
||||
|
||||
println!("\nNote: Some failures are expected due to:");
|
||||
println!("- Test data not existing on Intel servers");
|
||||
println!("- PCK operations requiring valid platform data");
|
||||
println!("- Subscription key requirements for certain endpoints");
|
||||
|
||||
Ok(())
|
||||
}
|
694
crates/intel-dcap-api/specs/API spec V3.md
Normal file
694
crates/intel-dcap-api/specs/API spec V3.md
Normal file
|
@ -0,0 +1,694 @@
|
|||
# Intel® SGX and Intel® TDX services - V3 API Documentation
|
||||
|
||||
## Intel® SGX and Intel® TDX Registration Service for Scalable Platforms
|
||||
|
||||
The API exposed by the Intel SGX registration service allows registering an Intel® SGX platform with multiple processor
|
||||
packages as a single platform instance, which can be remotely attested as a single entity later on[cite: 1]. The minimum
|
||||
version of the TLS protocol supported by the service is 1.2; any connection attempts with previous versions of TLS/SSL
|
||||
will be dropped by the server[cite: 2].
|
||||
|
||||
### Register Platform
|
||||
|
||||
This API allows registering a multi-package SGX platform, covering initial registration and TCB Recovery[cite: 2].
|
||||
During registration, the platform manifest is authenticated by the Registration Service to verify it originates from a
|
||||
genuine, non-revoked SGX platform[cite: 2]. If the platform configuration is successfully verified, platform
|
||||
provisioning root keys are stored in the backend[cite: 2].
|
||||
|
||||
Stored platform provisioning root keys are later used to derive the public parts of Provisioning Certification Keys (
|
||||
PCKs)[cite: 2]. These PCKs are distributed as x.509 certificates by the Provisioning Certification Service for Intel SGX
|
||||
and are used during the remote attestation of the platform[cite: 3].
|
||||
|
||||
#### POST `https://api.trustedservices.intel.com/sgx/registration/v1/platform`
|
||||
|
||||
**Request**
|
||||
|
||||
**Headers**
|
||||
|
||||
Besides the headers explicitly mentioned below, the HTTP request may contain standard HTTP headers (e.g.,
|
||||
Content-Length)[cite: 3].
|
||||
|
||||
| Name | Required | Value | Description |
|
||||
|:-------------|:---------|:---------------------------|:----------------------------------------|
|
||||
| Content-Type | True | `application/octet-stream` | MIME type of the request body[cite: 4]. |
|
||||
|
||||
**Body**
|
||||
|
||||
The body is a binary representation of the Platform Manifest structure – an opaque blob representing a registration
|
||||
manifest for a multi-package platform[cite: 5]. It contains platform provisioning root keys established by the platform
|
||||
instance and data required to authenticate the platform as genuine and non-revoked[cite: 5].
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/octet-stream" --data-binary @platform_manifest POST "[https://api.trustedservices.intel.com/sgx/registration/v1/platform](https://api.trustedservices.intel.com/sgx/registration/v1/platform)"
|
||||
````
|
||||
|
||||
**Response**
|
||||
|
||||
**Model**
|
||||
|
||||
The response is a Hex-encoded representation of the PPID for the registered platform instance (only if the HTTP Status
|
||||
Code is 201; otherwise, the body is empty).
|
||||
|
||||
**Example Response**
|
||||
|
||||
```
|
||||
001122334455667788AABBCCDDEEFF
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Headers | Body | Description |
|
||||
|:-----|:--------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 201 | Request-ID: Randomly generated identifier for each request (for troubleshooting purposes). | Hex-encoded representation of PPID. | Operation successful (new platform instance registered). A new platform instance has been registered[cite: 5]. |
|
||||
| 400 | Request-ID: Randomly generated identifier[cite: 6]. \<br\> Error-Code and Error-Message: Additional details about the error[cite: 9]. | | Invalid Platform Manifest[cite: 8]. The request might be malformed[cite: 6], intended for a different server[cite: 7], contain an invalid/revoked package[cite: 7], an unrecognized package[cite: 7], an incompatible package[cite: 7], an invalid manifest[cite: 7], or violate a key caching policy[cite: 8]. The client should not repeat the request without modifications[cite: 9]. |
|
||||
| 415 | Request-ID: Randomly generated identifier[cite: 10]. | | MIME type specified in the request is not supported[cite: 10]. |
|
||||
| 500 | Request-ID: Randomly generated identifier[cite: 10]. | | Internal server error occurred[cite: 10]. |
|
||||
| 503 | Request-ID: Randomly generated identifier[cite: 10]. | | Server is currently unable to process the request. The client should try again later[cite: 11]. |
|
||||
|
||||
-----
|
||||
|
||||
### Add Package
|
||||
|
||||
This API adds new package(s) to an already registered platform instance[cite: 11]. A subscription is required[cite: 11].
|
||||
If successful, a Platform Membership Certificate is generated for each processor package in the Add Request[cite: 12].
|
||||
|
||||
#### POST `https://api.trustedservices.intel.com/sgx/registration/v1/package`
|
||||
|
||||
**Request**
|
||||
|
||||
**Headers**
|
||||
|
||||
| Name | Required | Value | Description |
|
||||
|:--------------------------|:---------|:---------------------------|:--------------------------------------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | True | | Subscription key providing access to this API, found in your Profile[cite: 14]. |
|
||||
| Content-Type | True | `application/octet-stream` | MIME type of the request body[cite: 14]. |
|
||||
|
||||
**Body**
|
||||
|
||||
Binary representation of the Add Request structure – an opaque blob for adding new processor packages to an existing
|
||||
platform instance.
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/octet-stream" --data-binary @add_package POST "[https://api.trustedservices.intel.com/sgx/registration/v1/package](https://api.trustedservices.intel.com/sgx/registration/v1/package)" -H "Ocp-Apim-Subscription-Key: {subscription_key}"
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
**Model**
|
||||
|
||||
For a 200 HTTP Status Code, the response is a fixed-size array (8 elements) containing binary representations of
|
||||
Platform Membership Certificate structures[cite: 15]. Certificates are populated sequentially, starting at index 0, with
|
||||
the rest of the elements zeroed[cite: 15].
|
||||
|
||||
**Example Response (hex-encoded)**
|
||||
|
||||
```
|
||||
E4B0E8B80F8B49184488F77273550840984816854488B7CFRP...
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Headers | Body | Description |
|
||||
|:-----|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 200 | Content-Type: `application/octet-stream`[cite: 17]. \<br\> Request-ID: Random identifier[cite: 17]. \<br\> CertificateCount: Number of certificates returned[cite: 17]. | Fixed-size array of Platform Membership Certificates[cite: 17]. | Operation successful. Packages added[cite: 17]. |
|
||||
| 400 | Request-ID: Random identifier[cite: 17]. \<br\> Error-Code and Error-Message: Details on the error[cite: 17]. | | Invalid Add Request Payload[cite: 17]. Can be due to malformed syntax, platform not found, invalid/revoked/unrecognized package, or invalid AddRequest[cite: 17]. |
|
||||
| 401 | Request-ID: Random identifier[cite: 17]. | | Failed to authenticate or authorize the request[cite: 17]. |
|
||||
| 415 | Request-ID: Random identifier[cite: 17]. | | MIME type specified is not supported[cite: 17]. |
|
||||
| 500 | Request-ID: Random identifier[cite: 17]. | | Internal server error occurred[cite: 17]. |
|
||||
| 503 | Request-ID: Random identifier[cite: 17]. | | Server is currently unable to process the request[cite: 17]. |
|
||||
|
||||
-----
|
||||
|
||||
## Intel® SGX Provisioning Certification Service for ECDSA Attestation
|
||||
|
||||
Download the Provisioning Certification Root CA Certificate (API v3) here:
|
||||
|
||||
* [DER](https://www.google.com/search?q=https://certificates.trustedservices.intel.com/Intel_SGX_Provisioning_Certification_RootCA.cer) [cite: 18]
|
||||
* [PEM](https://www.google.com/search?q=https://certificates.trustedservices.intel.com/intel_SGX_Provisioning_Certification_RootCA.perm) [cite: 18]
|
||||
|
||||
### Get PCK Certificate V3
|
||||
|
||||
This API allows requesting a single PCK certificate by specifying PPID and SVNs or Platform Manifest and SVNs[cite: 18].
|
||||
A subscription is required[cite: 18].
|
||||
|
||||
* **Using PPID and SVNs**:
|
||||
* Single-socket platforms: No prerequisites[cite: 18].
|
||||
* Multi-socket platforms: Requires previous registration via `Register Platform` API[cite: 18]. Platform root keys
|
||||
must be persistently stored[cite: 19], and the `Keys Caching Policy` must be set to `true`[cite: 21]. The service
|
||||
uses a PCK public key derived from stored keys[cite: 20].
|
||||
* **Using Platform Manifest and SVNs**:
|
||||
* Multi-socket platforms: Does not require previous registration[cite: 21]. It doesn't require keys to be
|
||||
persistently stored[cite: 22]. The service uses a PCK public key derived from the provided manifest[cite: 23].
|
||||
Depending on the `Keys Caching Policy`, keys might be stored[cite: 24].
|
||||
* **Direct Registration** (`Register Platform` first): Sets policy to always store keys[cite: 25]. Keys are
|
||||
stored when the manifest is sent[cite: 26]. `CachedKeys` flag in PCK Certificates is set to `true`[cite: 27].
|
||||
* **Indirect Registration** (`Get PCK Certificate(s)` first): Sets policy to never store keys[cite: 27]. Keys
|
||||
are discarded after use[cite: 28]. Standard metadata is stored, but `Register Platform` cannot be used
|
||||
anymore[cite: 29]. `CachedKeys` flag is set to `false`[cite: 30].
|
||||
|
||||
The PCS returns the PCK Certificate representing the TCB level with the highest security posture based on CPUSVN and PCE
|
||||
ISVSVN[cite: 30].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/pckcert`
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------|:---------|:--------------------|:-----------------------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 32]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Type of key for PPID encryption (Default: `RSA-3072`)[cite: 32]. |
|
||||
| encrypted\_ppid | String | Query | True | `[0-9a-fA-F]{768}$` | Base16-encoded PPID (encrypted with PPIDEK)[cite: 32]. |
|
||||
| cpusvn | String | Query | True | `[0-9a-fA-F]{32}$` | Base16-encoded CPUSVN (16 bytes)[cite: 32]. |
|
||||
| pcesvn | String | Query | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCESVN (2 bytes, little endian)[cite: 32]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID (2 bytes, little endian)[cite: 32]. |
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcert?encrypted_ppid=...&cpusvn=...&pcesvn=...&pceid=](https://api.trustedservices.intel.com/sgx/certification/v3/pckcert?encrypted_ppid=...&cpusvn=...&pcesvn=...&pceid=)..." -H "Ocp-Apim-Subscription-Key: {subscription_key}"
|
||||
```
|
||||
|
||||
**Response**: Response description can be
|
||||
found [here](https://www.google.com/search?q=%23response-get-and-post-1)[cite: 34].
|
||||
|
||||
#### POST `https://api.trustedservices.intel.com/sgx/certification/v3/pckcert`
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-----------------------------|:---------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 35]. |
|
||||
| Content-Type | String | Header | True | | Content Type (`application/json`)[cite: 35]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16882,112884}$` | Base16-encoded Platform Manifest[cite: 35]. |
|
||||
| cpusvn | String | Body Field | True | `[0-9a-fA-F]{32}$` | Base16-encoded CPUSVN[cite: 35]. |
|
||||
| pcesvn | String | Body Field | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCESVN[cite: 35]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID[cite: 35]. |
|
||||
|
||||
**Body**
|
||||
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...",
|
||||
"cpusvn": "...",
|
||||
"pcesvn": "...",
|
||||
"pceid": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X POST -d '{"platformManifest": "...", "cpusvn": "...", "pcesvn": "...", "pceid": "..."}' -H "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key: {subscription_key}" "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcert](https://api.trustedservices.intel.com/sgx/certification/v3/pckcert)"
|
||||
```
|
||||
|
||||
**Response (GET and POST)**
|
||||
|
||||
**Model**: PckCert (X-PEM-FILE) - PEM-encoded SGX PCK Certificate[cite: 36].
|
||||
|
||||
**Example Response**
|
||||
|
||||
```pem
|
||||
-----BEGIN CERTIFICATE-----
|
||||
...
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:--------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 200 | PckCert | Content-Type: `application/x-pem-file`[cite: 36]. \<br\> Request-ID[cite: 36]. \<br\> SGX-PCK-Certificate-Issuer-Chain: URL-encoded issuer chain[cite: 36]. \<br\> SGX-TCBm: Hex-encoded CPUSVN and PCESVN[cite: 37]. \<br\> SGX-FMSPC: Hex-encoded FMSPC[cite: 37]. \<br\> SGX-PCK-Certificate-CA-Type: 'processor' or 'platform'[cite: 39]. \<br\> Warning: Optional message[cite: 39]. | Operation successful[cite: 36]. |
|
||||
| 400 | | Request-ID[cite: 39]. \<br\> Warning[cite: 39]. | Invalid request parameters[cite: 39]. |
|
||||
| 401 | | Request-ID[cite: 40]. \<br\> Warning[cite: 40]. | Failed to authenticate or authorize the request[cite: 40]. |
|
||||
| 404 | | Request-ID[cite: 40]. \<br\> Warning[cite: 40]. | PCK Certificate not found[cite: 40]. Reasons: unsupported PPID/PCE-ID, TCB level too low, or Platform Manifest not registered/updated[cite: 41]. |
|
||||
| 500 | | Request-ID[cite: 41]. \<br\> Warning[cite: 41]. | Internal server error occurred[cite: 41]. |
|
||||
| 503 | | Request-ID[cite: 42]. \<br\> Warning[cite: 42]. | Server is currently unable to process the request[cite: 42]. |
|
||||
|
||||
-----
|
||||
|
||||
### Get PCK Certificates V3
|
||||
|
||||
This API retrieves PCK certificates for all configured TCB levels using PPID or Platform Manifest[cite: 42].
|
||||
Subscription required[cite: 42].
|
||||
|
||||
* **Using PPID**:
|
||||
* Single-socket platforms: No prerequisites[cite: 43].
|
||||
* Multi-socket platforms: Requires prior registration via `Register Platform` API[cite: 44]. Keys must be
|
||||
persistently stored[cite: 45], and `Keys Caching Policy` must be `true`[cite: 47]. PCS uses stored keys[cite: 46].
|
||||
* **Using Platform Manifest**:
|
||||
* Multi-socket platforms: Does not require prior registration[cite: 47]. Does not require persistent
|
||||
storage[cite: 48]. PCS uses manifest keys[cite: 49]. Caching policy determines storage[cite: 50].
|
||||
* **Direct Registration**: Always stores keys; `CachedKeys` is `true`[cite: 51, 52].
|
||||
* **Indirect Registration**: Never stores keys; `CachedKeys` is `false`[cite: 53].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts`
|
||||
|
||||
Retrieves certificates based on encrypted PPID and PCE-ID[cite: 53].
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------|:---------|:--------------------|:--------------------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 54]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Key type for PPID encryption (Default: `RSA-3072`)[cite: 54]. |
|
||||
| encrypted\_ppid | String | Query | True | `[0-9a-fA-F]{768}$` | Base16-encoded PPID[cite: 54]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID[cite: 54]. |
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts?encrypted_ppid=...&pceid=](https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts?encrypted_ppid=...&pceid=)..." -H "Ocp-Apim-Subscription-Key: {subscription_key}"
|
||||
```
|
||||
|
||||
**Response**: Response description can be
|
||||
found [here](https://www.google.com/search?q=%23response-get-and-post-2)[cite: 55].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config`
|
||||
|
||||
Retrieves certificates for a specific CPUSVN (multi-package only)[cite: 55].
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------|:---------|:--------------------|:----------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 56]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Key type for PPID encryption[cite: 56]. |
|
||||
| encrypted\_ppid | String | Query | True | `[0-9a-fA-F]{768}$` | Base16-encoded PPID[cite: 56]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID[cite: 56]. |
|
||||
| cpusvn | String | Query | True | `[0-9a-fA-F]{32}$` | Base16-encoded CPUSVN[cite: 56]. |
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config?encrypted_ppid=...&pceid=...&cpusvn=](https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config?encrypted_ppid=...&pceid=...&cpusvn=)..." -H "Ocp-Apim-Subscription-Key: {subscription_key}"
|
||||
```
|
||||
|
||||
**Response**: Response description can be
|
||||
found [here](https://www.google.com/search?q=%23response-get-and-post-2)[cite: 57].
|
||||
|
||||
#### POST `https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts`
|
||||
|
||||
Retrieves certificates based on Platform Manifest and PCE-ID (multi-package only)[cite: 57].
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-----------------------------|:--------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 58]. |
|
||||
| Content-Type | String | Header | True | `application/json` | Content Type[cite: 58]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16882,112884}$` | Base16-encoded Platform Manifest[cite: 58]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID[cite: 58]. |
|
||||
|
||||
**Body**
|
||||
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...",
|
||||
"pceid": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X POST -d '{"platformManifest": "...", "pceid": "..."}' -H "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key: {subscription_key}" "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts](https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts)"
|
||||
```
|
||||
|
||||
**Response**: Response description can be
|
||||
found [here](https://www.google.com/search?q=%23response-get-and-post-2)[cite: 59].
|
||||
|
||||
#### POST `https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config`
|
||||
|
||||
Retrieves certificates for a specific CPUSVN using Platform Manifest (multi-package only)[cite: 59].
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-----------------------------|:--------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | True | | Subscription key[cite: 61]. |
|
||||
| Content-Type | String | Header | True | `application/json` | Content Type[cite: 61]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16882,112884}$` | Base16-encoded Platform Manifest[cite: 61]. |
|
||||
| cpusvn | String | Body Field | True | `[0-9a-fA-F]{32}$` | Base16-encoded CPUSVN[cite: 61]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}$` | Base16-encoded PCE-ID[cite: 61]. |
|
||||
|
||||
**Body**
|
||||
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...",
|
||||
"cpusvn": "...",
|
||||
"pceid": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X POST -d '{"platformManifest": "...", "cpusvn": "...", "pceid": "..."}' -H "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key: {subscription_key}" "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config](https://api.trustedservices.intel.com/sgx/certification/v3/pckcerts/config)"
|
||||
```
|
||||
|
||||
**Response (GET and POST)**
|
||||
|
||||
**Model**: PckCerts (JSON) - Array of data structures with `tcb`, `tcm`, and `certificate`[cite: 62].
|
||||
|
||||
**PckCerts Structure**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"tcb": {
|
||||
"sgxtcbcomp01svn": 0,
|
||||
// Integer
|
||||
"sgxtcbcomp02svn": 0,
|
||||
// Integer
|
||||
// ... (03 to 16)
|
||||
"pcesvn": 0
|
||||
// Integer
|
||||
},
|
||||
"tcm": "...",
|
||||
// String, Hex-encoded TCBm [cite: 63, 64]
|
||||
"cert": "..."
|
||||
// String, PEM-encoded certificate or "Not available" [cite: 64]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example Response**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"tcb": {
|
||||
"sgxtcbcomp01svn": 0,
|
||||
"sgxtcbcomp02svn": 0,
|
||||
"sgxtcbcomp03svn": 0,
|
||||
"sgxtcbcomp04svn": 0,
|
||||
"sgxtcbcomp05svn": 0,
|
||||
"sgxtcbcomp06svn": 0,
|
||||
"sgxtcbcomp07svn": 0,
|
||||
"sgxtcbcomp08svn": 0,
|
||||
"sgxtcbcomp09svn": 0,
|
||||
"sgxtcbcomp10svn": 0,
|
||||
"sgxtcbcomp11svn": 0,
|
||||
"sgxtcbcomp12svn": 0,
|
||||
"sgxtcbcomp13svn": 0,
|
||||
"sgxtcbcomp14svn": 0,
|
||||
"sgxtcbcomp15svn": 0,
|
||||
"sgxtcbcomp16svn": 0,
|
||||
"pcesvn": 0
|
||||
},
|
||||
"tcm": "...",
|
||||
"cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------|
|
||||
| 200 | PckCerts | Content-Type: `application/json`[cite: 65]. \<br\> Request-ID[cite: 65]. \<br\> SGX-PCK-Certificate-Issuer-Chain: Issuer chain[cite: 66]. \<br\> SGX-FMSPC[cite: 66]. \<br\> SGX-PCK-Certificate-CA-Type[cite: 66]. \<br\> Warning[cite: 66]. | Operation successful[cite: 65]. |
|
||||
| 400 | | Request-ID[cite: 67]. \<br\> Warning[cite: 67]. | Invalid request parameters[cite: 67]. |
|
||||
| 401 | | Request-ID[cite: 68]. \<br\> Warning[cite: 68]. | Failed to authenticate or authorize the request[cite: 68]. |
|
||||
| 404 | | Request-ID[cite: 69]. \<br\> Warning[cite: 69]. | PCK Certificate not found[cite: 69]. Reasons: PPID/PCE-ID not supported or Platform Manifest not registered[cite: 70]. |
|
||||
| 500 | | Request-ID[cite: 70]. \<br\> Warning[cite: 70]. | Internal server error occurred[cite: 70]. |
|
||||
| 503 | | Request-ID[cite: 70]. \<br\> Warning[cite: 70]. | Server is currently unable to process the request[cite: 70]. |
|
||||
|
||||
-----
|
||||
|
||||
### Get Revocation List V3
|
||||
|
||||
Retrieves the X.509 Certificate Revocation List (CRL) for revoked SGX PCK Certificates[cite: 71]. CRLs are issued by
|
||||
Intel SGX Processor CA or Platform CA[cite: 71].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/pckcrl`
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:---------|:-------|:-------------|:---------|:------------|:------------|
|
||||
| ca | String | Query | True | `(processor | platform)` | CA that issued the CRL[cite: 71]. |
|
||||
| encoding | String | Query | False | `(pem | der)` | Encoding (Default: PEM)[cite: 71]. |
|
||||
|
||||
**Example Request**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/pckcrl?ca=platform&encoding=der](https://api.trustedservices.intel.com/sgx/certification/v3/pckcrl?ca=platform&encoding=der)"
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
**Model**: PckCrl (X-PEM-FILE or PKIX-CRL) - PEM or DER-encoded CRL[cite: 71].
|
||||
|
||||
**Example Response**
|
||||
|
||||
```
|
||||
-----BEGIN X509 CRL-----
|
||||
...
|
||||
-----END X509 CRL-----
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:-------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------|
|
||||
| 200 | PckCrl | Content-Type: `application/x-pem-file` (PEM) or `application/pkix-crl` (DER)[cite: 72]. \<br\> Request-ID[cite: 72]. \<br\> SGX-PCK-CRL-Issuer-Chain: Issuer chain[cite: 72]. \<br\> Warning[cite: 72]. | Operation successful[cite: 72]. |
|
||||
| 400 | | Request-ID[cite: 72]. \<br\> Warning[cite: 73]. | Invalid request parameters[cite: 72]. |
|
||||
| 401 | | Request-ID[cite: 73]. \<br\> Warning[cite: 73]. | Failed to authenticate or authorize[cite: 73]. |
|
||||
| 500 | | Request-ID[cite: 73]. \<br\> Warning[cite: 73]. | Internal server error occurred[cite: 73]. |
|
||||
| 503 | | Request-ID[cite: 73]. \<br\> Warning[cite: 73]. | Server is currently unable to process[cite: 73]. |
|
||||
|
||||
-----
|
||||
|
||||
### Get TCB Info V3
|
||||
|
||||
Retrieves SGX TCB information for a given FMSPC[cite: 74].
|
||||
|
||||
**Algorithm for TCB Status:**
|
||||
|
||||
1. Retrieve FMSPC from the SGX PCK Certificate[cite: 74].
|
||||
2. Retrieve TCB Info matching the FMSPC[cite: 75].
|
||||
3. Iterate through the sorted TCB Levels[cite: 75]:
|
||||
* Compare all SGX TCB Comp SVNs (01-16) from the certificate with TCB Level values[cite: 76]. If all are \>=,
|
||||
proceed[cite: 76]. Otherwise, move to the next item[cite: 76].
|
||||
* Compare PCESVN from the certificate with the TCB Level value[cite: 77]. If \>=, read the status[cite: 77].
|
||||
Otherwise, move to the next item[cite: 78].
|
||||
4. If no match is found, the TCB Level is not supported[cite: 78].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/tcb`
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:------------------------|:-------|:-------------|:---------|:-------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| fmspc | String | Query | True | `[0-9a-fA-F]{12}$` | Base16-encoded FMSPC (6 bytes)[cite: 81]. |
|
||||
| update | String | Query | False | `(early | standard)` | Update type (Default: standard). 'early' provides early access, 'standard' provides standard access[cite: 81]. Cannot be used with `tcbEvaluationDataNumber`[cite: 81]. |
|
||||
| tcbEvaluationDataNumber | Number | Query | False | `\d+$` | Specifies a TCB Evaluation Data Number. Allows fetching specific versions; returns 410 if \< M, 404 if \> N[cite: 81]. Cannot be used with `update`[cite: 81]. |
|
||||
|
||||
**Example Requests**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/tcb?fmspc=...&update=early](https://api.trustedservices.intel.com/sgx/certification/v3/tcb?fmspc=...&update=early)"
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/tcb?fmspc=...&tcbEvaluationDataNumber=](https://api.trustedservices.intel.com/sgx/certification/v3/tcb?fmspc=...&tcbEvaluationDataNumber=)..."
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
**Model**: TcbInfoV2 (JSON) - SGX TCB Info[cite: 82].
|
||||
|
||||
**TcbInfoV2 Structure**
|
||||
|
||||
* `version`: Integer[cite: 83].
|
||||
* `issueDate`: String (date-time, ISO 8601 UTC)[cite: 84].
|
||||
* `nextUpdate`: String (date-time, ISO 8601 UTC)[cite: 85].
|
||||
* `fmspc`: String (Base16-encoded FMSPC)[cite: 85].
|
||||
* `pceId`: String (Base16-encoded PCE-ID)[cite: 85].
|
||||
* `tcbType`: Integer[cite: 85].
|
||||
* `tcbEvaluationDataNumber`: Integer, monotonically increasing sequence number for TCB evaluation data set
|
||||
updates[cite: 86]. Synchronized across TCB Info and Identities[cite: 86]. Helps determine which data supersedes
|
||||
another[cite: 87].
|
||||
* `tcbLevels`: Array of TCB level objects[cite: 87].
|
||||
* `tcb`: Object with `sgxtcbcompXXsvn` (Integer) and `pcesvn` (Integer)[cite: 87].
|
||||
* `tcbDate`: String (date-time, ISO 8601 UTC)[cite: 89]. If advisories exist after this date with enforced
|
||||
mitigations, status won't be `UpToDate`[cite: 88].
|
||||
* `tcbStatus`: String (`UpToDate`, `HardeningNeeded`, `ConfigurationNeeded`, `ConfigurationAndHardeningNeeded`,
|
||||
`OutOfDate`, `OutOfDateConfigurationNeeded`, `Revoked`)[cite: 90, 91, 92].
|
||||
* `advisoryIDs`: Array of strings (e.g., `INTEL-SA-XXXXX`, `INTEL-DOC-XXXXX`)[cite: 93, 94].
|
||||
* `signature`: String (Base16 encoded)[cite: 94].
|
||||
|
||||
**Example Response**
|
||||
|
||||
```json
|
||||
{
|
||||
"tcbInfo": {
|
||||
"version": 2,
|
||||
"issueDate": "2018-07-30T12:00:00Z",
|
||||
"nextUpdate": "2018-08-30T12:00:00Z",
|
||||
"fmspc": "...",
|
||||
"pceId": "0000",
|
||||
"tcbType": 1,
|
||||
"tcbEvaluationDataNumber": 7,
|
||||
"tcbLevels": [
|
||||
{
|
||||
"tcb": {
|
||||
"sgxtcbcomp01svn": 0,
|
||||
/* ... */
|
||||
"pcesvn": 0
|
||||
},
|
||||
"tcbDate": "2018-07-11T12:00:00Z",
|
||||
"tcbStatus": "UpToDate",
|
||||
"advisoryIDs": [
|
||||
"INTEL-SA-00070",
|
||||
"INTEL-SA-00076"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:----------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------|
|
||||
| 200 | TcbInfoV2 | Content-Type: `application/json`[cite: 96]. \<br\> Request-ID[cite: 96]. \<br\> SGX-TCB-Info-Issuer-Chain: Issuer chain[cite: 96]. \<br\> Warning[cite: 96]. | Operation successful[cite: 96]. |
|
||||
| 400 | | Request-ID[cite: 96]. \<br\> Warning[cite: 96]. | Invalid request (bad FMSPC or conflicting `update`/`tcbEvaluationDataNumber`)[cite: 96]. |
|
||||
| 401 | | Request-ID[cite: 96]. \<br\> Warning[cite: 96]. | Failed to authenticate or authorize[cite: 96]. |
|
||||
| 404 | | Request-ID[cite: 96]. \<br\> Warning[cite: 96]. | TCB info not found for FMSPC or `tcbEvaluationDataNumber`[cite: 96]. |
|
||||
| 410 | | Request-ID[cite: 98]. \<br\> Warning[cite: 98]. | TCB Information for `tcbEvaluationDataNumber` no longer available[cite: 98]. |
|
||||
| 500 | | Request-ID[cite: 98]. \<br\> Warning[cite: 98]. | Internal server error[cite: 98]. |
|
||||
| 503 | | Request-ID[cite: 98]. \<br\> Warning[cite: 98]. | Server unable to process[cite: 98]. |
|
||||
|
||||
-----
|
||||
|
||||
### Get Quoting Enclave Identity V3
|
||||
|
||||
Verifies if an SGX Enclave Report matches a valid Quoting Enclave (QE) identity[cite: 99].
|
||||
|
||||
**Algorithm:**
|
||||
|
||||
1. Retrieve and validate QE Identity[cite: 99].
|
||||
2. Compare SGX Enclave Report against QE Identity:
|
||||
* Verify `MRSIGNER` equals `mrsigner`[cite: 100].
|
||||
* Verify `ISVPRODID` equals `isvprodid`[cite: 101].
|
||||
* Verify `(miscselectMask & MISCSELECT)` equals `miscselect`[cite: 102].
|
||||
* Verify `(attributesMask & ATTRIBUTES)` equals `attributes`[cite: 103, 104].
|
||||
3. If any check fails, identity doesn't match[cite: 105].
|
||||
4. Determine TCB status:
|
||||
* Retrieve TCB Levels[cite: 106].
|
||||
* Find TCB Level with ISVSVN \<= Enclave Report ISVSVN (descending)[cite: 107].
|
||||
* Read `tcbStatus`; if not found, it's unsupported[cite: 108].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/qe/identity`
|
||||
|
||||
**Request**
|
||||
|
||||
| Name | Type | Type | Required | Pattern | Description |
|
||||
|:------------------------|:-------|:------|:---------|:--------|:------------------------------------------------------------------------------------------|
|
||||
| update | String | Query | False | `(early | standard)` | Update type (Default: standard)[cite: 110]. Cannot be used with `tcbEvaluationDataNumber`[cite: 110]. |
|
||||
| tcbEvaluationDataNumber | Number | Query | False | `\d+` | Specifies TCB Evaluation Data Number[cite: 110]. Cannot be used with `update`[cite: 110]. |
|
||||
|
||||
**Example Requests**
|
||||
|
||||
```bash
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/qe/identity?update=early](https://api.trustedservices.intel.com/sgx/certification/v3/qe/identity?update=early)"
|
||||
curl -X GET "[https://api.trustedservices.intel.com/sgx/certification/v3/qe/identity?tcbEvaluationDataNumber=](https://api.trustedservices.intel.com/sgx/certification/v3/qe/identity?tcbEvaluationDataNumber=)..."
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
**Model**: QEIdentityV2 (JSON) - QE Identity data[cite: 111].
|
||||
|
||||
**QEIdentityV2 Structure**
|
||||
|
||||
* `enclaveIdentity`:
|
||||
* `id`: String (`QE`, `QVE`, or `QAE`)[cite: 113].
|
||||
* `version`: Integer[cite: 113].
|
||||
* `issueDate`, `nextUpdate`: String (date-time, ISO 8601 UTC)[cite: 114].
|
||||
* `tcbEvaluationDataNumber`: Integer[cite: 115].
|
||||
* `miscselect`, `miscselectMask`: String (Base16-encoded)[cite: 115, 116].
|
||||
* `attributes`, `attributesMask`: String (Base16-encoded)[cite: 116].
|
||||
* `mrsigner`: String (Base16-encoded)[cite: 116].
|
||||
* `isvprodid`: Integer[cite: 116].
|
||||
* `tcbLevels`: Array of TCB level objects[cite: 116].
|
||||
* `tcb`: Object with `isvsvn` (Integer)[cite: 117].
|
||||
* `tcbDate`: String (date-time, ISO 8601 UTC)[cite: 117].
|
||||
* `tcbStatus`: String (`UpToDate`, `OutOfDate`, `Revoked`)[cite: 119].
|
||||
* `advisoryIDs`: Array of strings[cite: 119].
|
||||
* `signature`: String (Hex-encoded)[cite: 119].
|
||||
|
||||
**Status Codes**
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:-------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------|
|
||||
| 200 | QEIdentityV2 | Content-Type: `application/json`[cite: 122]. \<br\> Request-ID[cite: 122]. \<br\> SGX-Enclave-Identity-Issuer-Chain: Issuer chain[cite: 122]. \<br\> Warning[cite: 122]. | Operation successful[cite: 122]. |
|
||||
| 400 | | Request-ID[cite: 122]. \<br\> Warning[cite: 123]. | Invalid request (bad params or conflicting `update`/`tcbEvaluationDataNumber`)[cite: 122, 124]. |
|
||||
| 401 | | Request-ID[cite: 123]. \<br\> Warning[cite: 123]. | Failed to authenticate or authorize[cite: 123]. |
|
||||
| 404 | | Request-ID[cite: 123]. \<br\> Warning[cite: 123]. | QE identity not found for `tcbEvaluationDataNumber`[cite: 124]. |
|
||||
| 410 | | Request-ID[cite: 124]. \<br\> Warning[cite: 124]. | QEIdentity for `tcbEvaluationDataNumber` no longer available[cite: 124]. |
|
||||
| 500 | | Request-ID[cite: 125]. \<br\> Warning[cite: 125]. | Internal server error[cite: 125]. |
|
||||
| 503 | | Request-ID[cite: 125]. \<br\> Warning[cite: 125]. | Server unable to process[cite: 125]. |
|
||||
|
||||
-----
|
||||
|
||||
### Get Quote Verification Enclave Identity V3
|
||||
|
||||
Verifies if an SGX Enclave Report matches a valid QVE identity[cite: 126].
|
||||
|
||||
**Algorithm:**
|
||||
|
||||
1. Retrieve and validate QVE Identity[cite: 126].
|
||||
2. Compare Enclave Report: `MRSIGNER`[cite: 127], `ISVPRODID`[cite: 128], `MISCSELECT` (with mask)[cite: 128],
|
||||
`ATTRIBUTES` (with mask)[cite: 128].
|
||||
3. If any fails, no match[cite: 129].
|
||||
4. Determine TCB status via ISVSVN comparison[cite: 129, 130].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/qve/identity`
|
||||
|
||||
**Request**: Same parameters as `Get Quoting Enclave Identity V3` (`update` and `tcbEvaluationDataNumber`)[cite: 132].
|
||||
|
||||
**Response**: QVEIdentityV2 (JSON) - QVE Identity data[cite: 133]. Structure similar to QE
|
||||
Identity[cite: 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144].
|
||||
|
||||
**Status Codes**: Similar to `Get Quoting Enclave Identity V3`[cite: 145].
|
||||
|
||||
-----
|
||||
|
||||
### Get Quote Appraisal Enclave Identity V3
|
||||
|
||||
Verifies if an SGX Enclave Report matches a valid QAE identity[cite: 149].
|
||||
|
||||
**Algorithm:**
|
||||
|
||||
1. Retrieve and validate QAE Identity[cite: 149].
|
||||
2. Compare Enclave Report: `MRSIGNER`[cite: 151], `ISVPRODID`[cite: 151], `MISCSELECT` (with mask)[cite: 152, 153],
|
||||
`ATTRIBUTES` (with mask)[cite: 154, 155].
|
||||
3. If any fails, no match[cite: 155].
|
||||
4. Determine TCB status via ISVSVN comparison[cite: 157, 158].
|
||||
|
||||
#### GET `https://api.trustedservices.intel.com/sgx/certification/v3/qae/identity`
|
||||
|
||||
**Request**: Same parameters as `Get Quoting Enclave Identity V3` (`update` and `tcbEvaluationDataNumber`)[cite: 160].
|
||||
|
||||
**Response**: QAEIdentityV2 (JSON) - QAE Identity data[cite: 161]. Structure similar to QE
|
||||
Identity[cite: 162, 163, 164, 165, 166, 167, 168, 169, 170].
|
||||
|
||||
**Status Codes**: Similar to `Get Quoting Enclave Identity V3`[cite: 171, 174].
|
||||
|
||||
-----
|
||||
|
||||
### PCK Certificate and CRL Specification
|
||||
|
||||
This document specifies the hierarchy and format of X.509 v3 certificates and v2 CRLs for Provisioning Certification
|
||||
Keys[cite: 175].
|
||||
|
||||
Enforcement of a mitigation means the attestation process can detect its presence and the result will differ[cite: 175].
|
||||
Intel offers `standard` (default) and `early` update parameters, affecting when enforcement occurs[cite: 176]. The
|
||||
attestation result is an objective assessment[cite: 177]. Relying parties can use additional factors [cite: 178] and may
|
||||
choose to trust an 'OutOfDate' platform, accepting risks[cite: 180]. Intel will strive to communicate schedule
|
||||
deviations[cite: 181].
|
||||
|
664
crates/intel-dcap-api/specs/API spec V4.md
Normal file
664
crates/intel-dcap-api/specs/API spec V4.md
Normal file
|
@ -0,0 +1,664 @@
|
|||
This document outlines the API for Intel® SGX and Intel® TDX services, focusing on platform registration and
|
||||
provisioning certification using ECDSA attestation.
|
||||
|
||||
## Intel® SGX and Intel® TDX Registration Service for Scalable Platforms [cite: 1]
|
||||
|
||||
The Intel® SGX and Intel® TDX Registration Service API enables the registration of Intel® SGX platforms with multiple
|
||||
processor packages as a unified platform instance[cite: 2]. This allows these platforms to be remotely attested as a
|
||||
single entity[cite: 2]. It is important to note that the service enforces a minimum TLS protocol version of 1.2; any
|
||||
attempts to connect with older TLS/SSL versions will be rejected[cite: 3].
|
||||
|
||||
### Register Platform
|
||||
|
||||
This API facilitates the registration of multi-package SGX platforms, encompassing both initial registration and TCB (
|
||||
Trusted Computing Base) recovery[cite: 4]. During this process, the Registration Service authenticates the platform
|
||||
manifest to confirm it originates from a genuine, non-revoked SGX platform[cite: 4]. If the platform configuration
|
||||
passes verification, its provisioning root keys are securely stored[cite: 4]. These stored keys are subsequently used to
|
||||
derive the public components of Provisioning Certification Keys (PCKs), which are then distributed as X.509 certificates
|
||||
by the Provisioning Certification Service[cite: 5]. These PCK certificates are integral to the remote attestation
|
||||
process for the platform[cite: 5].
|
||||
|
||||
**POST** `https://api.trustedservices.intel.com/sgx/registration/v1/platform`
|
||||
|
||||
**Request**
|
||||
|
||||
* **Headers**: In addition to standard HTTP headers (like `Content-Length`), the following is required[cite: 1]:
|
||||
|
||||
| Name | Required | Value | Description |
|
||||
|:-------------|:---------|:-------------------------|:----------------------------------------|
|
||||
| Content-Type | True | application/octet-stream | MIME type of the request body[cite: 1]. |
|
||||
|
||||
* **Body**: The request body must be a binary representation of the Platform Manifest structure[cite: 6]. This is an
|
||||
opaque blob containing the registration manifest for a multi-package platform[cite: 6]. It includes the platform
|
||||
provisioning root keys established by the platform instance and the necessary data to authenticate it as a genuine,
|
||||
non-revoked SGX platform[cite: 6].
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X POST "Content-Type: application/octet-stream" --data-binary @platform_manifest.bin "https://api.trustedservices.intel.com/sgx/registration/v1/platform" [cite: 1]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: The response body will contain the hex-encoded representation of the PPID (Platform Provisioning ID) for
|
||||
the registered platform instance, but only if the HTTP Status Code is 201[cite: 1]. Otherwise, the body will be
|
||||
empty[cite: 1].
|
||||
|
||||
* **Example Response**:
|
||||
```
|
||||
00112233445566778899AABBCCDDEEFF [cite: 1]
|
||||
```
|
||||
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Headers | Body | Description |
|
||||
|:-----|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------|:--------------------------------------------------------------------------------------------------|
|
||||
| 201 | `Request-ID`: Randomly generated identifier for troubleshooting[cite: 7]. | Hex-encoded PPID | Operation successful; a new platform instance has been registered[cite: 7]. |
|
||||
| 400 | `Request-ID`: Randomly generated identifier[cite: 8]. `Error-Code` & `Error-Message`: Details on the error (e.g., `InvalidRequestSyntax`, `InvalidRegistrationServer`, `InvalidOrRevokedPackage`, `PackageNotFound`, `IncompatiblePackage`, `InvalidPlatformManifest`, `CachedKeysPolicyViolation`)[cite: 8]. | | Invalid Platform Manifest[cite: 10]. The client should not retry without modifications[cite: 10]. |
|
||||
| 415 | `Request-ID`: Randomly generated identifier[cite: 8]. | | The MIME type specified in the request is not supported[cite: 8]. |
|
||||
| 500 | `Request-ID`: Randomly generated identifier[cite: 8]. | | An internal server error occurred[cite: 8]. |
|
||||
| 503 | `Request-ID`: Randomly generated identifier[cite: 8]. | | The server is currently unable to process the request; try again later[cite: 8]. |
|
||||
|
||||
### Add Package
|
||||
|
||||
This API allows for adding new processor packages to an already registered platform instance[cite: 11]. Upon successful
|
||||
execution, a Platform Membership Certificate is generated for each processor package included in the Add
|
||||
Request[cite: 11]. This requires a subscription for registration[cite: 11].
|
||||
|
||||
**POST** `https://api.trustedservices.intel.com/sgx/registration/v1/package`
|
||||
|
||||
**Request**
|
||||
|
||||
* **Headers**: Besides standard headers like `Content-Length`[cite: 12], the following are needed:
|
||||
|
||||
| Name | Required | Value | Description |
|
||||
|:--------------------------|:---------|:-------------------------|:------------------------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | True | *Your Subscription Key* | Subscription key for API access, found in your profile[cite: 12]. |
|
||||
| Content-Type | True | application/octet-stream | MIME type of the request body[cite: 12]. |
|
||||
|
||||
* **Body**: A binary representation of the Add Request structure, an opaque blob for adding new packages to an existing
|
||||
platform[cite: 13].
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X POST "Content-Type: application/octet-stream" --data-binary @add_package_request.bin "https://api.trustedservices.intel.com/sgx/registration/v1/package" -H "Ocp-Apim-Subscription-Key: {subscription_key}" [cite: 14]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: For a 200 HTTP Status Code, the response is a fixed-size array (8 elements) containing binary Platform
|
||||
Membership Certificate structures appended together[cite: 14]. Certificates fill the array sequentially, starting from
|
||||
index 0, with remaining elements zeroed out[cite: 14].
|
||||
|
||||
* **Example Response (hex-encoded)**:
|
||||
```
|
||||
E8BDBECFEF9040184488777267355084...00000000 [cite: 15]
|
||||
```
|
||||
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Headers | Body | Description |
|
||||
|:-----|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------|
|
||||
| 200 | `Content-Type`: application/octet-stream[cite: 18]. `Request-ID`: Randomly generated identifier[cite: 18]. `Certificate-Count`: Number of certificates returned[cite: 18]. | Fixed-size array (8 elements) with binary Platform Membership Certificates[cite: 18, 19]. | Operation successful; packages added to the platform[cite: 18]. |
|
||||
| 400 | `Request-ID`: Randomly generated identifier[cite: 18]. `Error-Code` & `Error-Message`: Details on the error (e.g., `InvalidRequestSyntax`, `PlatformNotFound`, `InvalidOrRevokedPackage`, `PackageNotFound`[cite: 17], `InvalidAddRequest`)[cite: 18]. | | Invalid Add Request Payload[cite: 20]. Do not retry without modifications[cite: 20]. |
|
||||
| 401 | `Request-ID`: Randomly generated identifier[cite: 18]. | | Failed to authenticate or authorize the request[cite: 18]. |
|
||||
| 415 | `Request-ID`: Randomly generated identifier[cite: 18]. | | The MIME type specified is not supported[cite: 18]. |
|
||||
| 500 | `Request-ID`: Randomly generated identifier[cite: 18]. | | Internal server error occurred[cite: 18]. |
|
||||
| 503 | `Request-ID`: Randomly generated identifier[cite: 18]. | | Server is currently unable to process the request[cite: 18]. |
|
||||
|
||||
## Intel® SGX and Intel® TDX Provisioning Certification Service for ECDSA Attestation [cite: 21]
|
||||
|
||||
This service provides PCK certificates. You can download the Provisioning Certification Root CA Certificate (v4) in both
|
||||
DER and PEM formats[cite: 21].
|
||||
|
||||
### Get/Post PCK Certificate V4
|
||||
|
||||
This API allows requesting a single PCK certificate. It offers two primary methods:
|
||||
|
||||
1. **Using PPID and SVNs**:
|
||||
* **Single-socket platforms**: No prerequisites[cite: 22].
|
||||
* **Multi-socket platforms**: Requires prior platform registration via the Register Platform API[cite: 22]. This
|
||||
flow necessitates that platform root keys are persistently stored in the backend[cite: 23], and the Keys Caching
|
||||
Policy must be `true`[cite: 23].
|
||||
2. **Using Platform Manifest and SVNs**:
|
||||
* **Multi-socket platforms**: Does *not* require prior registration[cite: 24]. Platform root keys are *not* required
|
||||
to be persistently stored[cite: 24]. The Keys Caching Policy determines whether keys are stored or not[cite: 25].
|
||||
* **Direct Registration (via Register Platform API)**: Keys are always stored; `CachedKeys` flag in PCK
|
||||
certificates is `true`[cite: 26, 27].
|
||||
* **Indirect Registration (via Get PCK Certificate(s) API)**: Keys are never stored; `CachedKeys` flag is
|
||||
`false`[cite: 28, 30]. Register Platform API cannot be used afterward[cite: 29].
|
||||
|
||||
**Note**: The PCS returns the PCK Certificate representing the highest TCB security level based on the CPUSVN and PCE
|
||||
ISVSVN inputs[cite: 31].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcert`
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-------------------|:--------------------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 32]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Key type for PPID encryption (default: "RSA-3072")[cite: 32]. |
|
||||
| encrypted_ppid | String | Query | True | `[0-9a-fA-F]{768}` | Base16-encoded encrypted PPID[cite: 32]. |
|
||||
| cpusvn | String | Query | True | `[0-9a-fA-F]{32}` | Base16-encoded CPUSVN[cite: 32]. |
|
||||
| pcesvn | String | Query | True | `[0-9a-fA-F]{4}` | Base16-encoded PCESVN (little endian)[cite: 32]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}` | Base16-encoded PCE-ID (little endian)[cite: 32]. |
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/pckcert?encrypted_ppid={encrypted_ppid}&cpusvn={cpusvn}&pcesvn={pcesvn}&pceid={pceid}" -H "Ocp-Apim-Subscription-Key: {subscription_key}" [cite: 33]
|
||||
```
|
||||
|
||||
**POST** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcert`
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:----------------------------|:-------------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 33]. |
|
||||
| Content-Type | String | Header | True | `application/json` | Content type[cite: 35]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16862,112884}` | Base16-encoded Platform Manifest[cite: 35]. |
|
||||
| cpusvn | String | Body Field | True | `[0-9a-fA-F]{32}` | Base16-encoded CPUSVN[cite: 35]. |
|
||||
| pcesvn | String | Body Field | True | `[0-9a-fA-F]{4}` | Base16-encoded PCESVN (little endian)[cite: 35]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}` | Base16-encoded PCE-ID (little endian)[cite: 35]. |
|
||||
|
||||
* **Body**:
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...", [cite: 36]
|
||||
"cpusvn": "...", [cite: 36]
|
||||
"pcesvn": "...", [cite: 36]
|
||||
"pceid": "..." [cite: 36]
|
||||
}
|
||||
```
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X POST --data '{"platformManifest":"...","cpusvn":"...","pcesvn":"...","pceid":"..."}' "https://api.trustedservices.intel.com/sgx/certification/v4/pckcert" -H "Ocp-Apim-Subscription-Key: {subscription_key}" -H "Content-Type: application/json" [cite: 36]
|
||||
```
|
||||
|
||||
**Response (Both GET & POST)**
|
||||
|
||||
* **Model**: `PckCert (X-PEM-FILE)` - PEM-encoded SGX PCK Certificate[cite: 36].
|
||||
* **Example Response**:
|
||||
```pem
|
||||
-----BEGIN CERTIFICATE-----
|
||||
...
|
||||
-----END CERTIFICATE----- [cite: 36]
|
||||
```
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 200 | PckCert | `Content-Type`: application/x-pem-file[cite: 37]. `Request-ID`: Identifier[cite: 37]. `SGX-PCK-Certificate-Issuer-Chain`: PEM-encoded Issuer Chain[cite: 37]. `SGX-TCBm`: Hex-encoded CPUSVN & PCESVN[cite: 37]. `SGX-FMSPC`: Hex-encoded FMSPC[cite: 37]. `SGX-PCK-Certificate-CA-Type`: "processor" or "platform"[cite: 37]. `Warning` (Optional)[cite: 37]. | Operation successful[cite: 37]. |
|
||||
| 400 | | `Request-ID`: Identifier[cite: 37]. `Warning` (Optional)[cite: 37]. `Error-Code` & `Error-Message` (e.g., `InvalidRequestSyntax`, `InvalidRegistrationServer`, `InvalidOrRevokedPackage`, `PackageNotFound`, `IncompatiblePackage`, `InvalidPlatformManifest`)[cite: 37]. | Invalid request parameters[cite: 37]. |
|
||||
| 401 | | `Request-ID`: Identifier[cite: 37]. `Warning` (Optional)[cite: 37]. | Failed to authenticate or authorize[cite: 37]. |
|
||||
| 404 | | `Request-ID`: Identifier[cite: 37]. `Warning` (Optional)[cite: 37]. | PCK Certificate not found (e.g., unsupported PPID/PCE-ID, TCB below minimum, Platform Manifest not registered/updated)[cite: 37]. |
|
||||
| 429 | | `Retry-After`: Wait time in seconds[cite: 37]. `Warning` (Optional)[cite: 37]. | Too many requests[cite: 37]. |
|
||||
| 500 | | `Request-ID`: Identifier[cite: 37]. `Warning` (Optional)[cite: 37]. | Internal server error[cite: 37]. |
|
||||
| 503 | | `Request-ID`: Identifier[cite: 39]. `Warning` (Optional)[cite: 39]. | Server is currently unable to process[cite: 39]. |
|
||||
|
||||
### Get PCK Certificates V4
|
||||
|
||||
This API retrieves PCK certificates for *all* configured TCB levels for a platform. The usage conditions (single-socket
|
||||
vs. multi-socket, PPID vs. Platform Manifest, key caching) are similar to the single PCK certificate
|
||||
API[cite: 40, 41, 42, 43, 44, 45, 46, 47, 48].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts` (Using PPID & PCE-ID)
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-------------------|:------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 49]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Key type (default: "RSA-3072")[cite: 49]. |
|
||||
| encrypted_ppid | String | Query | True | `[0-9a-fA-F]{768}` | Encrypted PPID[cite: 49]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}` | PCE-ID[cite: 49]. |
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts?encrypted_ppid={...}&pceid={...}" -H "Ocp-Apim-Subscription-Key: {subscription_key}" [cite: 50]
|
||||
```
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts/config` (Using PPID, PCE-ID &
|
||||
CPUSVN) [cite: 51]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:-------------------|:------------------------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 52]. |
|
||||
| PPID-Encryption-Key | String | Header | False | | Key type (default: "RSA-3072")[cite: 52]. |
|
||||
| encrypted_ppid | String | Query | True | `[0-9a-fA-F]{768}` | Encrypted PPID[cite: 52]. |
|
||||
| pceid | String | Query | True | `[0-9a-fA-F]{4}` | PCE-ID[cite: 52]. |
|
||||
| cpusvn | String | Query | True | `[0-9a-fA-F]{32}` | CPUSVN[cite: 52]. |
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts/config?encrypted_ppid={...}&pceid={...}&cpusvn={...}" -H "Ocp-Apim-Subscription-Key: {subscription_key}" [cite: 53]
|
||||
```
|
||||
|
||||
**POST** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts` (Using Platform Manifest & PCE-ID)
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:----------------------------|:-----------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 54]. |
|
||||
| Content-Type | String | Header | True | `application/json` | Content type[cite: 54]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16862,112884}` | Platform Manifest[cite: 54]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}` | PCE-ID[cite: 54]. |
|
||||
|
||||
* **Body**:
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...", [cite: 55]
|
||||
"pceid": "..." [cite: 55]
|
||||
}
|
||||
```
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X POST --data '{"platformManifest":"...","pceid":"..."}' "https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts" -H "Ocp-Apim-Subscription-Key: {subscription_key}" -H "Content-Type: application/json" [cite: 55]
|
||||
```
|
||||
|
||||
**POST** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts/config` (Using Platform Manifest, PCE-ID &
|
||||
CPUSVN)
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:--------------------------|:-------|:-------------|:---------|:----------------------------|:-----------------------------|
|
||||
| Ocp-Apim-Subscription-Key | String | Header | False | | Subscription key[cite: 56]. |
|
||||
| Content-Type | String | Header | True | `application/json` | Content type[cite: 57]. |
|
||||
| platformManifest | String | Body Field | True | `[0-9a-fA-F]{16862,112884}` | Platform Manifest[cite: 56]. |
|
||||
| cpusvn | String | Body Field | True | `[0-9a-fA-F]{32}` | CPUSVN[cite: 56]. |
|
||||
| pceid | String | Body Field | True | `[0-9a-fA-F]{4}` | PCE-ID[cite: 56]. |
|
||||
|
||||
* **Body**:
|
||||
```json
|
||||
{
|
||||
"platformManifest": "...", [cite: 57]
|
||||
"cpusvn": "...", [cite: 57]
|
||||
"pceid": "..." [cite: 57]
|
||||
}
|
||||
```
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X POST --data '{"platformManifest":"...","cpusvn":"...","pceid":"..."}' "https://api.trustedservices.intel.com/sgx/certification/v4/pckcerts/config" -H "Ocp-Apim-Subscription-Key: {subscription_key}" -H "Content-Type: application/json" [cite: 57]
|
||||
```
|
||||
|
||||
**Response (All GET & POST for multiple certs)**
|
||||
|
||||
* **Model**: `PckCerts` (JSONArray of objects, each containing `tcb`, `tcbm`, and `cert`)[cite: 56].
|
||||
* `tcb`: Object with 16 `sgxtcbcompXXsvn` fields (integer 0-255) and `pcesvn` (integer 0-65535)[cite: 59].
|
||||
* `tcbm`: Hex-encoded string of CPUSVN (16 bytes) and PCESVN (2 bytes)[cite: 7].
|
||||
* `cert`: URL-encoded PEM PCK Certificate, or "Not available" string[cite: 60].
|
||||
* **Example Response**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"tcb": {
|
||||
"sgxtcbcomp01svn": 3,
|
||||
"sgxtcbcomp02svn": 1,
|
||||
...
|
||||
"pcesvn": 11
|
||||
},
|
||||
"tcbm": "...",
|
||||
"cert": "-----BEGIN%20CERTIFICATE-----%0A...%0A-----END%20CERTIFICATE-----" [cite: 61]
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:---------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------|
|
||||
| 200 | PckCerts | `Content-Type`: application/json[cite: 8]. `Request-ID`: Identifier[cite: 8]. `SGX-PCK-Certificate-Issuer-Chain`: Issuer Chain[cite: 62]. `SGX-FMSPC`: FMSPC[cite: 8]. `SGX-PCK-Certificate-CA-Type`: "processor" or "platform"[cite: 63]. `Warning` (Optional)[cite: 8]. | Operation successful[cite: 8]. |
|
||||
| 400 | | `Request-ID`: Identifier[cite: 8]. `Warning` (Optional)[cite: 8]. `Error-Code` & `Error-Message` (e.g., `InvalidRequestSyntax`[cite: 65], `InvalidRegistrationServer`[cite: 65], `InvalidOrRevokedPackage`[cite: 65], `PackageNotFound`[cite: 65], `IncompatiblePackage`[cite: 65], `InvalidPlatformManifest` [cite: 66]) | Invalid request parameters[cite: 8]. |
|
||||
| 401 | | `Request-ID`: Identifier[cite: 68]. `Warning` (Optional)[cite: 68]. | Failed to authenticate or authorize[cite: 68]. |
|
||||
| 404 | | `Request-ID`: Identifier[cite: 68]. `Warning` (Optional)[cite: 68]. | PCK Certificate not found (e.g., unsupported PPID/PCE-ID, Platform Manifest not registered)[cite: 68]. |
|
||||
| 429 | | `Retry-After`: Wait time[cite: 68]. `Warning` (Optional)[cite: 68]. | Too many requests[cite: 68]. |
|
||||
| 500 | | `Request-ID`: Identifier[cite: 68]. `Warning` (Optional)[cite: 68]. | Internal server error[cite: 68]. |
|
||||
| 503 | | `Request-ID`: Identifier[cite: 68]. `Warning` (Optional)[cite: 68]. | Server is currently unable to process[cite: 68]. |
|
||||
|
||||
### Get Revocation List V4
|
||||
|
||||
This API retrieves the X.509 Certificate Revocation List (CRL) for revoked SGX PCK Certificates, issued by either the
|
||||
Intel SGX Processor CA or Platform CA[cite: 69, 70].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl` [cite: 71]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:---------|:-------|:-------------|:---------|:------------|:------------|
|
||||
| ca | String | Query | True | `(processor | platform)` | CA identifier ("processor" or "platform")[cite: 71, 72]. |
|
||||
| encoding | String | Query | False | `(pem | der)` | CRL encoding (default: PEM)[cite: 71]. |
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=platform&encoding=pem" [cite: 71]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: `PckCrl` (X-PEM-FILE or PKIX-CRL) - PEM or DER encoded CRL[cite: 71].
|
||||
* **Example Response**:
|
||||
```pem
|
||||
-----BEGIN X509 CRL-----
|
||||
...
|
||||
-----END X509 CRL----- [cite: 71]
|
||||
```
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:-------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------|
|
||||
| 200 | PckCrl | `Content-Type`: "application/x-pem-file" or "application/pkix-crl"[cite: 71]. `Request-ID`: Identifier[cite: 71]. `SGX-PCK-CRL-Issuer-Chain`: Issuer Chain[cite: 73]. `Warning` (Optional)[cite: 71]. | Operation successful[cite: 71]. |
|
||||
| 400 | | `Request-ID`: Identifier[cite: 71]. `Warning` (Optional)[cite: 71]. | Invalid request parameters[cite: 71]. |
|
||||
| 401 | | `Request-ID`: Identifier[cite: 74]. `Warning` (Optional)[cite: 74]. | Failed to authenticate or authorize[cite: 74]. |
|
||||
| 500 | | `Request-ID`: Identifier[cite: 74]. `Warning` (Optional)[cite: 74]. | Internal server error[cite: 74]. |
|
||||
| 503 | | `Request-ID`: Identifier[cite: 74]. `Warning` (Optional)[cite: 74]. | Server is currently unable to process[cite: 74]. |
|
||||
|
||||
### Get SGX TCB Info V4
|
||||
|
||||
This API retrieves SGX TCB information for a specific FMSPC, which is crucial for determining the TCB status of a
|
||||
platform[cite: 75]. The process involves:
|
||||
|
||||
1. Retrieving the FMSPC from the SGX PCK Certificate[cite: 75].
|
||||
2. Fetching the corresponding SGX TCB info[cite: 76].
|
||||
3. Iterating through the TCB Levels:
|
||||
* Comparing all 16 SGX TCB Comp SVNs from the certificate against the TCB Level; they must be >=[cite: 77, 78].
|
||||
* Comparing the PCESVN from the certificate against the TCB Level; it must be >=[cite: 79, 80]. If both match, the
|
||||
TCB level's status is found[cite: 80].
|
||||
4. If no match is found, the TCB level is unsupported[cite: 82].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/tcb` [cite: 82]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:------------------------|:-------|:-------------|:---------|:------------------|:-------------------------------------------------------------------------------------------------------|
|
||||
| fmspc | String | Query | True | `[0-9a-fA-F]{12}` | Base16-encoded FMSPC[cite: 83]. |
|
||||
| update | String | Query | False | `(early | standard)` | TCB Info update type (default: standard). `early` provides access sooner than `standard`[cite: 83]. Cannot be used with `tcbEvaluationDataNumber`[cite: 83]. |
|
||||
| tcbEvaluationDataNumber | Number | Query | False | `\d+` | Retrieves TCB info for a specific evaluation number[cite: 83]. Cannot be used with `update`[cite: 83]. |
|
||||
|
||||
* **Example Requests**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc={fmspc_value}&update=early" [cite: 84]
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc={fmspc_value}&tcbEvaluationDataNumber={number}" [cite: 84]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: `Appendix A: TCB info V3`[cite: 86]. (See Appendix A below).
|
||||
* **Example Response**: (JSON structure as shown in the document)[cite: 85].
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------|
|
||||
| 200 | TcbInfoV3 | `Content-Type`: application/json[cite: 86]. `Request-ID`: Identifier[cite: 86]. `TCB-Info-Issuer-Chain`: Issuer Chain[cite: 86]. `Warning` (Optional)[cite: 86]. | Operation successful[cite: 86]. |
|
||||
| 400 | | `Request-ID`: Identifier[cite: 86]. `Warning` (Optional)[cite: 86]. | Invalid request (bad `fmspc`, invalid params, or `update` & `tcbEvaluationDataNumber` used together)[cite: 86]. |
|
||||
| 401 | | `Request-ID`: Identifier[cite: 86]. `Warning` (Optional)[cite: 87]. | Failed to authenticate or authorize[cite: 86]. |
|
||||
| 404 | | `Request-ID`: Identifier[cite: 86]. `Warning` (Optional)[cite: 87]. | TCB info not found for the given `fmspc` or `tcbEvaluationDataNumber`[cite: 86]. |
|
||||
| 410 | | `Request-ID`: Identifier[cite: 88]. `Warning` (Optional)[cite: 88]. | TCB info for the provided `tcbEvaluationDataNumber` is no longer available[cite: 88]. |
|
||||
| 500 | | `Request-ID`: Identifier[cite: 88]. `Warning` (Optional)[cite: 88]. | Internal server error[cite: 88]. |
|
||||
| 503 | | `Request-ID`: Identifier[cite: 88]. `Warning` (Optional)[cite: 88]. | Server currently unable to process[cite: 88]. |
|
||||
|
||||
### Get TDX TCB Info V4
|
||||
|
||||
This API retrieves TDX TCB information[cite: 89]. The TCB status determination follows a similar process to SGX but
|
||||
includes additional steps for TDX TEE TCB SVNs and TDX Module
|
||||
Identity[cite: 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/tdx/certification/v4/tcb` [cite: 102]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:------------------------|:-------|:-------------|:---------|:------------------|:---------------------------------------------------------------------------------------------------------|
|
||||
| fmspc | String | Query | True | `[0-9a-fA-F]{12}` | Base16-encoded FMSPC[cite: 103]. |
|
||||
| update | String | Query | False | `(early | standard)` | TCB Info update type (default: standard)[cite: 103]. Cannot be used with `tcbEvaluationDataNumber`[cite: 103]. |
|
||||
| tcbEvaluationDataNumber | Number | Query | False | `\d+` | Retrieves TCB info for a specific evaluation number[cite: 103]. Cannot be used with `update`[cite: 103]. |
|
||||
|
||||
* **Example Requests**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/tdx/certification/v4/tcb?fmspc={fmspc_value}&update=early" [cite: 104]
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/tdx/certification/v4/tcb?fmspc={fmspc_value}&tcbEvaluationDataNumber={number}" [cite: 104]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: `Appendix A: TCB info V3`[cite: 107]. (See Appendix A below).
|
||||
* **Example Response**: (JSON structure including `tdxModule` and `tdxtcbcomponents` as shown in the
|
||||
document)[cite: 105, 106].
|
||||
* **Status Codes**: Similar to Get SGX TCB Info V4[cite: 108].
|
||||
|
||||
### Enclave Identity V4
|
||||
|
||||
This set of APIs allows for determining if an SGX Enclave's identity matches Intel's published identity[cite: 109]. The
|
||||
process involves:
|
||||
|
||||
1. Retrieving the Enclave Identity (SGX QE, TDX QE, QVE, or QAE)[cite: 109].
|
||||
2. Comparing `MRSIGNER` and `ISVPRODID` fields[cite: 109].
|
||||
3. Applying `miscselectMask` and `attributesMask` and comparing the results[cite: 111, 112, 113, 114].
|
||||
4. If checks pass, determining the TCB status by finding the highest TCB Level (sorted by ISVSVN) whose ISVSVN is <= the
|
||||
Enclave Report's ISVSVN[cite: 116, 117].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/qe/identity` [cite: 118]
|
||||
**GET** `https://api.trustedservices.intel.com/tdx/certification/v4/qe/identity` [cite: 128]
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/qve/identity` [cite: 133]
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/qae/identity` [cite: 138]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Pattern | Description |
|
||||
|:------------------------|:-------|:-------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| update | String | Query | False | `(early | standard)` | Identity update type (default: standard)[cite: 118, 127, 132, 137]. Cannot be used with `tcbEvaluationDataNumber`[cite: 118, 121, 127, 132, 137]. |
|
||||
| tcbEvaluationDataNumber | Number | Query | False | `\d+` | Retrieves Identity for a specific evaluation number[cite: 119, 120, 127, 132, 137]. Cannot be used with `update`[cite: 121, 127, 132, 137]. |
|
||||
|
||||
* **Example Requests** (SGX QE shown):
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/qe/identity?update=early" [cite: 122]
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/qe/identity?tcbEvaluationDataNumber={number}" [cite: 122]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: `Appendix B: Enclave Identity V2`[cite: 122, 128, 134, 139]. (See Appendix B below).
|
||||
* **Example Response**: (JSON structure as shown in the document for QE[cite: 125], TDX QE[cite: 131], QVE[cite: 136],
|
||||
and QAE [cite: 141]).
|
||||
* **Status Codes** (SGX QE shown, others are similar):
|
||||
|
||||
| Code | Model | Headers | Description |
|
||||
|:-----|:------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------|
|
||||
| 200 | EIdentityV2 | `Content-Type`: application/json[cite: 122]. `Request-ID`: Identifier[cite: 122]. `SGX-Enclave-Identity-Issuer-Chain`: Issuer Chain[cite: 122]. `Warning` (Optional)[cite: 122]. | Operation successful[cite: 122]. |
|
||||
| 400 | | `Request-ID`: Identifier[cite: 122]. `Warning` (Optional)[cite: 122]. | Invalid request (params or `update` & `tcbEvaluationDataNumber` conflict)[cite: 122]. |
|
||||
| 401 | | `Request-ID`: Identifier[cite: 123]. `Warning` (Optional)[cite: 123]. | Failed to authenticate or authorize[cite: 122]. |
|
||||
| 404 | | `Request-ID`: Identifier[cite: 123]. `Warning` (Optional)[cite: 123]. | Identity info not found[cite: 122]. |
|
||||
| 410 | | `Request-ID`: Identifier[cite: 124]. `Warning` (Optional)[cite: 124]. | Identity info no longer available[cite: 124]. |
|
||||
| 500 | | `Request-ID`: Identifier[cite: 124]. `Warning` (Optional)[cite: 124]. | Internal server error[cite: 124]. |
|
||||
| 503 | | `Request-ID`: Identifier[cite: 124]. `Warning` (Optional)[cite: 124]. | Server currently unable to process[cite: 124]. |
|
||||
|
||||
### Retrieve FMSPCs V4
|
||||
|
||||
Retrieves a list of FMSPC values for SGX and TDX platforms that support DCAP attestation[cite: 141].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/sgx/certification/v4/fmspcs` [cite: 141]
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Request Type | Required | Description |
|
||||
|:---------|:-------|:-------------|:---------|:----------------------------------------------------------------------------|
|
||||
| platform | String | Query | False | Optional platform filter: `all` (default), `client`, `E3`, `E5`[cite: 141]. |
|
||||
|
||||
* **Example Request**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/fmspcs?platform=E5" [cite: 141]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Example Response**:
|
||||
```json
|
||||
[
|
||||
{"platform": "E3", "fmspc": "123456789000"}, [cite: 142]
|
||||
{"platform": "E5", "fmspc": "987654321000"}, [cite: 142]
|
||||
{"platform": "client", "fmspc": "ABCDEF123456"} [cite: 142]
|
||||
]
|
||||
```
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Headers | Description |
|
||||
|:-----|:-------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------|
|
||||
| 200 | `Content-Type`: application/json[cite: 142]. `Request-ID`: Identifier[cite: 142]. `Warning` (Optional)[cite: 142]. | Operation successful[cite: 142]. |
|
||||
| 400 | `Request-ID`: Identifier[cite: 142]. `Warning` (Optional)[cite: 143]. | Invalid request parameters[cite: 142]. |
|
||||
| 500 | `Request-ID`: Identifier[cite: 142]. `Warning` (Optional)[cite: 142]. | Internal server error[cite: 142]. |
|
||||
| 503 | `Request-ID`: Identifier[cite: 142]. `Warning` (Optional)[cite: 142]. | Server currently unable to process[cite: 142]. |
|
||||
|
||||
### Retrieve TCB Evaluation Data Numbers V4
|
||||
|
||||
Retrieves the list of currently supported TCB Evaluation Data Numbers and their associated TCB-R event
|
||||
states[cite: 142].
|
||||
|
||||
**GET** `https://api.trustedservices.intel.com/{sgx|tdx}/certification/v4/tcbevaluationdatanumbers` [cite: 142]
|
||||
|
||||
* **Example Requests**:
|
||||
```bash
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/sgx/certification/v4/tcbevaluationdatanumbers" [cite: 142]
|
||||
curl -v -X GET "https://api.trustedservices.intel.com/tdx/certification/v4/tcbevaluationdatanumbers" [cite: 142]
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
* **Model**: `Appendix C: TCB Evaluation Data Numbers V1`[cite: 144]. (See Appendix C below).
|
||||
* **Example Response**:
|
||||
```json
|
||||
{
|
||||
"tcbEvaluationDataNumbers": {
|
||||
"version": 1,
|
||||
"issueDate": "2023-04-13T09:38:17Z",
|
||||
"nextUpdate": "2023-05-13T09:38:17Z",
|
||||
"tcbNumbers": [
|
||||
{"tcbEvaluationDataNumber": 12, "tcbRecoveryEventDate": "2023-04-13T00:00:00Z", "tcbDate": "2023-04-13T00:00:00Z"},
|
||||
{"tcbEvaluationDataNumber": 11, "tcbRecoveryEventDate": "2023-01-14T00:00:00Z", "tcbDate": "2023-01-14T00:00:00Z"}
|
||||
],
|
||||
"signature": "..." [cite: 142]
|
||||
}
|
||||
}
|
||||
```
|
||||
* **Status Codes**:
|
||||
|
||||
| Code | Headers | Description |
|
||||
|:-----|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------|
|
||||
| 200 | `Content-Type`: application/json[cite: 144]. `Request-ID`: Identifier[cite: 144]. `TCB-Evaluation-Data-Numbers-Issuer-Chain`: Issuer Chain[cite: 145]. `Warning` (Optional)[cite: 144]. | Operation successful[cite: 144]. |
|
||||
| 500 | `Request-ID`: Identifier[cite: 144]. `Warning` (Optional)[cite: 144]. | Internal server error[cite: 144]. |
|
||||
| 503 | `Request-ID`: Identifier[cite: 144]. `Warning` (Optional)[cite: 144]. | Server currently unable to process[cite: 146]. |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: TCB Info V3 [cite: 147]
|
||||
|
||||
This defines the structure of the TCB Info V3 JSON response[cite: 147].
|
||||
|
||||
* `tcbInfo`: (Object)
|
||||
* `id`: (String) Identifier (e.g., "SGX", "TDX")[cite: 148].
|
||||
* `version`: (Integer) Structure version[cite: 148].
|
||||
* `issueDate`: (String - datetime) Creation timestamp (ISO 8601 UTC)[cite: 148].
|
||||
* `nextUpdate`: (String - datetime) Next update timestamp (ISO 8601 UTC)[cite: 149].
|
||||
* `fmspc`: (String) Base16-encoded FMSPC[cite: 149].
|
||||
* `pceId`: (String) Base16-encoded PCE ID[cite: 149].
|
||||
* `tcbType`: (Integer) TCB level composition type[cite: 149].
|
||||
* `tcbEvaluationDataNumber`: (Integer) Monotonically increasing sequence number, synchronized across TCB Info and
|
||||
Enclave Identities, indicating updates[cite: 150, 151, 152].
|
||||
* `tdxModule`: (Object - Optional, only for TDX TCB Info)[cite: 153].
|
||||
* `mrsigner`: (String) Base16-encoded TDX SEAM module's signer measurement[cite: 154].
|
||||
* `attributes`: (String) Base16-encoded "golden" attributes[cite: 154].
|
||||
* `attributesMask`: (String) Base16-encoded attributes mask[cite: 154].
|
||||
* `tdxModuleIdentities`: (Array - Optional, for multiple TDX SEAM Modules)[cite: 154].
|
||||
* `id`: (String) Module identifier[cite: 154].
|
||||
* `mrsigner`: (String) Base16-encoded signer measurement[cite: 155].
|
||||
* `attributes`: (String) Base16-encoded "golden" attributes[cite: 155].
|
||||
* `attributesMask`: (String) Base16-encoded attributes mask[cite: 156].
|
||||
* `tcbLevels`: (Array) Sorted list of TCB levels for this module[cite: 157].
|
||||
* `tcb`: (Object)
|
||||
* `isvsvn`: (Integer) ISV SVN[cite: 157].
|
||||
* `tcbDate`: (String - datetime) TCB date (ISO 8601 UTC)[cite: 158].
|
||||
* `tcbStatus`: (String) "UpToDate", "OutOfDate", or "Revoked"[cite: 158].
|
||||
* `advisoryIDs`: (Array - Optional) List of relevant `INTEL-SA-XXXXX` or `INTEL-DOC-XXXXX`
|
||||
identifiers[cite: 159, 160].
|
||||
* `tcbLevels`: (Array) Sorted list of TCB levels for the FMSPC[cite: 160].
|
||||
* `tcb`: (Object)
|
||||
* `sgxtcbcomponents`: (Array - Optional) 16 SGX TCB Components (SVN, Category, Type)[cite: 161].
|
||||
* `tdxtcbcomponents`: (Array - Optional, only for TDX TCB Info) 16 TDX TCB Components (SVN, Category,
|
||||
Type)[cite: 161, 162, 164].
|
||||
* `pcesvn`: (Integer) PCE SVN[cite: 161].
|
||||
* `tcbDate`: (String - datetime) TCB date (ISO 8601 UTC)[cite: 165].
|
||||
* `tcbStatus`: (String) "UpToDate", "HardeningNeeded", "ConfigurationNeeded", "
|
||||
ConfigurationAndHardeningNeeded", "OutOfDate", "OutOfDateConfigurationNeeded", "Revoked"[cite: 165, 166].
|
||||
* `advisoryIDs`: (Array - Optional) List of relevant `INTEL-SA-XXXXX` or `INTEL-DOC-XXXXX`
|
||||
identifiers[cite: 167, 168].
|
||||
* `signature`: (String) Base16-encoded signature over the `tcbInfo` body[cite: 163].
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Enclave Identity V2 [cite: 168]
|
||||
|
||||
This defines the structure of the Enclave Identity V2 JSON response[cite: 168].
|
||||
|
||||
* `enclaveIdentity`: (Object)
|
||||
* `id`: (String) Identifier ("QE", "QVE", "QAE", "TD_QE")[cite: 169].
|
||||
* `version`: (Integer) Structure version[cite: 169].
|
||||
* `issueDate`: (String - datetime) Creation timestamp (ISO 8601 UTC)[cite: 170].
|
||||
* `nextUpdate`: (String - datetime) Next update timestamp (ISO 8601 UTC)[cite: 170].
|
||||
* `tcbEvaluationDataNumber`: (Integer) Monotonically increasing sequence number, synchronized across TCB Info and
|
||||
Enclave Identities[cite: 171, 172].
|
||||
* `miscselect`: (String) Base16-encoded "golden" miscselect value[cite: 172].
|
||||
* `miscselectMask`: (String) Base16-encoded miscselect mask[cite: 172].
|
||||
* `attributes`: (String) Base16-encoded "golden" attributes value[cite: 172].
|
||||
* `attributesMask`: (String) Base16-encoded attributes mask[cite: 173].
|
||||
* `mrsigner`: (String) Base16-encoded mrsigner hash[cite: 173].
|
||||
* `isvprodid`: (Integer) Enclave Product ID[cite: 173].
|
||||
* `tcbLevels`: (Array) Sorted list of Enclave TCB levels[cite: 173].
|
||||
* `tcb`: (Object)
|
||||
* `isvsvn`: (Integer) Enclave's ISV SVN[cite: 173].
|
||||
* `tcbDate`: (String - datetime) TCB date (ISO 8601 UTC)[cite: 174].
|
||||
* `tcbStatus`: (String) "UpToDate", "OutOfDate", or "Revoked"[cite: 174, 176].
|
||||
* `advisoryIDs`: (Array - Optional) List of relevant `INTEL-SA-XXXXX` or `INTEL-DOC-XXXXX`
|
||||
identifiers[cite: 177].
|
||||
* `signature`: (String) Base16-encoded signature over the `enclaveIdentity` body[cite: 175].
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: TCB Evaluation Data Numbers V1 [cite: 177]
|
||||
|
||||
This defines the structure of the TCB Evaluation Data Numbers V1 JSON response[cite: 177].
|
||||
|
||||
* `tcbEvaluationDataNumbers`: (Object)
|
||||
* `id`: (String) Identifier ("SGX" or "TDX")[cite: 178].
|
||||
* `version`: (Integer) Structure version[cite: 178].
|
||||
* `issueDate`: (String - datetime) Creation timestamp (ISO 8601 UTC)[cite: 178].
|
||||
* `nextUpdate`: (String - datetime) Suggested next call timestamp (ISO 8601 UTC)[cite: 179].
|
||||
* `tcbNumbers`: (Array) List of TCB Evaluation Data Number objects[cite: 179].
|
||||
* `tcbEvaluationDataNumber`: (Integer) The number itself[cite: 179].
|
||||
* `tcbRecoveryEventDate`: (String - datetime) The date Intel first publishes related collateral (ISO 8601
|
||||
UTC)[cite: 179].
|
||||
* `tcbDate`: (String - datetime) TCB date (ISO 8601 UTC)[cite: 180, 181].
|
||||
* `signature`: (String) Base16-encoded signature over the structure's body[cite: 181].
|
||||
|
||||
---
|
||||
|
||||
## Appendix D: PCK Certificate and CRL Specification
|
||||
|
||||
This section refers to an external document that specifies the hierarchy and format of X.509 v3 certificates and X.509
|
||||
v2 CRLs issued by Intel for Provisioning Certification Keys[cite: 181].
|
||||
|
||||
---
|
||||
|
||||
**Notes on TCB Status and Enforcement:**
|
||||
|
||||
* **Enforcement Grace Periods**: Intel provides "early" and "standard" update parameters, offering different enforcement
|
||||
grace periods[cite: 182]. The attestation result depends on which parameter is used[cite: 182].
|
||||
* **Relying Party Trust Decisions**: Relying parties can use additional factors beyond the attestation result to make
|
||||
trust decisions[cite: 183]. They might accept risks even if a platform is technically "OutOfDate" due to low-severity
|
||||
issues[cite: 184].
|
||||
* **Communication**: Intel aims to communicate planned deviations via email to registered API subscribers[cite: 185].
|
|
@ -46,7 +46,7 @@ impl ApiClient {
|
|||
}
|
||||
|
||||
let request_builder = self.client.get(url);
|
||||
let response = request_builder.send().await?;
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
let fmspcs_json = response.text().await?;
|
||||
|
|
|
@ -11,7 +11,8 @@ use crate::{
|
|||
};
|
||||
use percent_encoding::percent_decode_str;
|
||||
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||
use std::io;
|
||||
use std::{io, time::Duration};
|
||||
use tokio::time::sleep;
|
||||
|
||||
impl ApiClient {
|
||||
/// Helper to construct API paths dynamically based on version and technology (SGX/TDX).
|
||||
|
@ -25,20 +26,18 @@ impl ApiClient {
|
|||
|
||||
if technology == "tdx" && self.api_version == ApiVersion::V3 {
|
||||
return Err(IntelApiError::UnsupportedApiVersion(format!(
|
||||
"TDX endpoint /{}/{}/{} requires API v4",
|
||||
service, endpoint, technology
|
||||
"TDX endpoint /{service}/{endpoint}/{technology} requires API v4",
|
||||
)));
|
||||
}
|
||||
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("//", "/"));
|
||||
return Ok(format!("/sgx/registration/v1/{endpoint}").replace("//", "/"));
|
||||
}
|
||||
|
||||
Ok(format!(
|
||||
"/{}/certification/{}/{}/{}",
|
||||
technology, api_segment, service, endpoint
|
||||
Ok(
|
||||
format!("/{technology}/certification/{api_segment}/{service}/{endpoint}")
|
||||
.replace("//", "/"),
|
||||
)
|
||||
.replace("//", "/"))
|
||||
}
|
||||
|
||||
/// Helper to add an optional header if the string is non-empty.
|
||||
|
@ -86,7 +85,7 @@ impl ApiClient {
|
|||
&self,
|
||||
request_builder: RequestBuilder,
|
||||
) -> Result<PckCertificateResponse, IntelApiError> {
|
||||
let response = request_builder.send().await?;
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
let issuer_chain = self.get_required_header(
|
||||
|
@ -111,7 +110,7 @@ impl ApiClient {
|
|||
&self,
|
||||
request_builder: RequestBuilder,
|
||||
) -> Result<PckCertificatesResponse, IntelApiError> {
|
||||
let response = request_builder.send().await?;
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
let issuer_chain = self.get_required_header(
|
||||
|
@ -136,7 +135,7 @@ impl ApiClient {
|
|||
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 = self.execute_with_retry(request_builder).await?;
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
let issuer_chain =
|
||||
|
@ -154,13 +153,12 @@ impl ApiClient {
|
|||
resource_description: &str,
|
||||
) -> Result<(), IntelApiError> {
|
||||
let builder_clone = request_builder.try_clone().ok_or_else(|| {
|
||||
IntelApiError::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
IntelApiError::Io(io::Error::other(
|
||||
"Failed to clone request builder for status check",
|
||||
))
|
||||
})?;
|
||||
|
||||
let response = builder_clone.send().await?;
|
||||
let response = self.execute_with_retry(builder_clone).await?;
|
||||
let status = response.status();
|
||||
|
||||
if status == StatusCode::NOT_FOUND || status == StatusCode::GONE {
|
||||
|
@ -187,25 +185,22 @@ impl ApiClient {
|
|||
/// 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(())
|
||||
return Err(IntelApiError::UnsupportedApiVersion(format!(
|
||||
"{function_name} requires API v4",
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if a V4-only parameter is provided with a V3 API version.
|
||||
pub(super) fn check_v4_only_param<T>(
|
||||
pub(super) fn check_v4_only_param<T: Copy>(
|
||||
&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
|
||||
"'{param_name}' parameter requires API v4",
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -229,4 +224,68 @@ impl ApiClient {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a request with automatic retry logic for rate limiting (429 responses).
|
||||
///
|
||||
/// This method will automatically retry the request up to `max_retries` times
|
||||
/// when receiving a 429 Too Many Requests response, waiting for the duration
|
||||
/// specified in the Retry-After header.
|
||||
pub(super) async fn execute_with_retry(
|
||||
&self,
|
||||
request_builder: RequestBuilder,
|
||||
) -> Result<Response, IntelApiError> {
|
||||
let mut retries = 0;
|
||||
|
||||
loop {
|
||||
// Clone the request builder for retry attempts
|
||||
let builder = request_builder.try_clone().ok_or_else(|| {
|
||||
IntelApiError::Io(io::Error::other(
|
||||
"Failed to clone request builder for retry",
|
||||
))
|
||||
})?;
|
||||
|
||||
let response = builder.send().await?;
|
||||
let status = response.status();
|
||||
|
||||
if status != StatusCode::TOO_MANY_REQUESTS {
|
||||
// Not a rate limit error, return the response
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
// Handle 429 Too Many Requests
|
||||
if retries >= self.max_retries {
|
||||
// No more retries, return the error
|
||||
let request_id = response
|
||||
.headers()
|
||||
.get("Request-ID")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("Unknown")
|
||||
.to_string();
|
||||
|
||||
let retry_after = response
|
||||
.headers()
|
||||
.get("Retry-After")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| v.parse::<u64>().ok())
|
||||
.unwrap_or(60);
|
||||
|
||||
return Err(IntelApiError::TooManyRequests {
|
||||
request_id,
|
||||
retry_after,
|
||||
});
|
||||
}
|
||||
|
||||
// Parse Retry-After header
|
||||
let retry_after_secs = response
|
||||
.headers()
|
||||
.get("Retry-After")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| v.parse::<u64>().ok())
|
||||
.unwrap_or(60); // Default to 60 seconds
|
||||
|
||||
// Wait before retrying
|
||||
sleep(Duration::from_secs(retry_after_secs)).await;
|
||||
retries += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ pub struct ApiClient {
|
|||
client: Client,
|
||||
base_url: Url,
|
||||
api_version: ApiVersion,
|
||||
/// Maximum number of automatic retries for rate-limited requests (429 responses)
|
||||
max_retries: u32,
|
||||
}
|
||||
|
||||
impl ApiClient {
|
||||
|
@ -114,6 +116,20 @@ impl ApiClient {
|
|||
.build()?,
|
||||
base_url: base_url.into_url()?,
|
||||
api_version,
|
||||
max_retries: 3, // Default to 3 retries
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the maximum number of automatic retries for rate-limited requests.
|
||||
///
|
||||
/// When the API returns a 429 (Too Many Requests) response, the client will
|
||||
/// automatically wait for the duration specified in the Retry-After header
|
||||
/// and retry the request up to this many times.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_retries` - Maximum number of retries (0 disables automatic retries)
|
||||
pub fn set_max_retries(&mut self, max_retries: u32) {
|
||||
self.max_retries = max_retries;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ impl ApiClient {
|
|||
}
|
||||
|
||||
let request_builder = self.client.get(url);
|
||||
let response = request_builder.send().await?;
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
let issuer_chain = self.get_required_header(
|
||||
|
|
|
@ -36,13 +36,13 @@ impl ApiClient {
|
|||
let path = self.build_api_path("sgx", "registration", "platform")?;
|
||||
let url = self.base_url.join(&path)?;
|
||||
|
||||
let response = self
|
||||
let request_builder = self
|
||||
.client
|
||||
.post(url)
|
||||
.header(header::CONTENT_TYPE, "application/octet-stream")
|
||||
.body(platform_manifest)
|
||||
.send()
|
||||
.await?;
|
||||
.body(platform_manifest);
|
||||
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
|
||||
let response = check_status(response, &[StatusCode::CREATED]).await?;
|
||||
|
||||
|
@ -81,14 +81,14 @@ impl ApiClient {
|
|||
let path = self.build_api_path("sgx", "registration", "package")?;
|
||||
let url = self.base_url.join(&path)?;
|
||||
|
||||
let response = self
|
||||
let request_builder = self
|
||||
.client
|
||||
.post(url)
|
||||
.header("Ocp-Apim-Subscription-Key", subscription_key)
|
||||
.header(header::CONTENT_TYPE, "application/octet-stream")
|
||||
.body(add_package_request)
|
||||
.send()
|
||||
.await?;
|
||||
.body(add_package_request);
|
||||
|
||||
let response = self.execute_with_retry(request_builder).await?;
|
||||
|
||||
let response = check_status(response, &[StatusCode::OK]).await?;
|
||||
|
||||
|
|
|
@ -59,6 +59,40 @@ pub enum IntelApiError {
|
|||
/// Indicates an invalid parameter was provided.
|
||||
#[error("Invalid parameter value: {0}")]
|
||||
InvalidParameter(&'static str),
|
||||
|
||||
/// Indicates that the API rate limit has been exceeded (HTTP 429).
|
||||
///
|
||||
/// This error is returned after the client has exhausted all automatic retry attempts
|
||||
/// for a rate-limited request. The `retry_after` field contains the number of seconds
|
||||
/// that was specified in the last Retry-After header. By default, the client automatically
|
||||
/// retries rate-limited requests up to 3 times.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use intel_dcap_api::{ApiClient, IntelApiError};
|
||||
///
|
||||
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut client = ApiClient::new()?;
|
||||
/// client.set_max_retries(0); // Disable automatic retries
|
||||
///
|
||||
/// match client.get_sgx_tcb_info("00606A000000", None, None).await {
|
||||
/// Ok(tcb_info) => println!("Success"),
|
||||
/// Err(IntelApiError::TooManyRequests { request_id, retry_after }) => {
|
||||
/// println!("Rate limited after all retries. Last retry-after was {} seconds.", retry_after);
|
||||
/// }
|
||||
/// Err(e) => eprintln!("Other error: {}", e),
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[error("Too many requests. Retry after {retry_after} seconds")]
|
||||
TooManyRequests {
|
||||
/// The unique request identifier for tracing.
|
||||
request_id: String,
|
||||
/// Number of seconds to wait before retrying, from Retry-After header.
|
||||
retry_after: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Extracts common API error details from response headers.
|
||||
|
@ -92,6 +126,27 @@ pub(crate) async fn check_status(
|
|||
let status = response.status();
|
||||
if expected_statuses.contains(&status) {
|
||||
Ok(response)
|
||||
} else if status == StatusCode::TOO_MANY_REQUESTS {
|
||||
// Handle 429 Too Many Requests with Retry-After header
|
||||
let request_id = response
|
||||
.headers()
|
||||
.get("Request-ID")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("Unknown")
|
||||
.to_string();
|
||||
|
||||
// Parse Retry-After header (can be in seconds or HTTP date format)
|
||||
let retry_after = response
|
||||
.headers()
|
||||
.get("Retry-After")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| v.parse::<u64>().ok())
|
||||
.unwrap_or(60); // Default to 60 seconds if header is missing or invalid
|
||||
|
||||
Err(IntelApiError::TooManyRequests {
|
||||
request_id,
|
||||
retry_after,
|
||||
})
|
||||
} else {
|
||||
let (request_id, error_code, error_message) = extract_api_error_details(&response);
|
||||
Err(IntelApiError::ApiError {
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
//!
|
||||
//! Create an [`ApiClient`] to interface with the Intel API.
|
||||
//!
|
||||
//! # Rate Limiting
|
||||
//!
|
||||
//! The Intel API implements rate limiting and may return HTTP 429 (Too Many Requests) responses.
|
||||
//! This client automatically handles rate limiting by retrying requests up to 3 times by default,
|
||||
//! waiting for the duration specified in the `Retry-After` header. You can configure the retry
|
||||
//! behavior using [`ApiClient::set_max_retries`]. If all retries are exhausted, the client
|
||||
//! returns an [`IntelApiError::TooManyRequests`] error.
|
||||
//!
|
||||
//! Example
|
||||
//! ```rust,no_run
|
||||
//! use intel_dcap_api::{ApiClient, IntelApiError, TcbInfoResponse};
|
||||
|
|
312
crates/intel-dcap-api/tests/mock_api_tests.rs
Normal file
312
crates/intel-dcap-api/tests/mock_api_tests.rs
Normal file
|
@ -0,0 +1,312 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
use intel_dcap_api::{ApiClient, ApiVersion, CaType, IntelApiError, UpdateType};
|
||||
use mockito::Server;
|
||||
use reqwest::Client;
|
||||
|
||||
// Create a test client without TLS requirements
|
||||
async fn create_test_client(base_url: &str) -> ApiClient {
|
||||
// Create a custom client without TLS requirements for testing
|
||||
ApiClient::new_with_base_url(base_url).expect("Failed to create client")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_simple_request() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// First, test with plain reqwest to ensure mock works
|
||||
let _m = server
|
||||
.mock("GET", "/test")
|
||||
.with_status(200)
|
||||
.with_body("test")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = Client::new();
|
||||
let resp = client
|
||||
.get(format!("{}/test", server.url()))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
assert_eq!(resp.text().await.unwrap(), "test");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_minimal() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// Use minimal response
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"test123".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("TCB-Info-Issuer-Chain", "test-cert")
|
||||
.with_body("{}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_tdx_tcb_info("test123", None, None).await;
|
||||
|
||||
match &result {
|
||||
Ok(resp) => {
|
||||
assert_eq!(resp.tcb_info_json, "{}");
|
||||
assert_eq!(resp.issuer_chain, "test-cert");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error: {:?}", e);
|
||||
panic!("Request failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qe_identity_minimal() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/qe/identity")
|
||||
.with_status(200)
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", "test-cert")
|
||||
.with_body("{}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_sgx_qe_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(resp.enclave_identity_json, "{}");
|
||||
assert_eq!(resp.issuer_chain, "test-cert");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pck_crl_minimal() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/pckcrl")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"ca".into(),
|
||||
"processor".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", "test-cert")
|
||||
.with_body("test-crl")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_pck_crl(CaType::Processor, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(String::from_utf8_lossy(&resp.crl_data), "test-crl");
|
||||
assert_eq!(resp.issuer_chain, "test-cert");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_handling() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded("fmspc".into(), "bad".into()))
|
||||
.with_status(404)
|
||||
.with_header("Request-ID", "test-123")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_sgx_tcb_info("bad", None, None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
IntelApiError::ApiError {
|
||||
status, request_id, ..
|
||||
} => {
|
||||
assert_eq!(status.as_u16(), 404);
|
||||
assert_eq!(request_id, "test-123");
|
||||
}
|
||||
_ => panic!("Wrong error type"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_types() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// Test Early update type
|
||||
let _m1 = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("fmspc".into(), "test".into()),
|
||||
mockito::Matcher::UrlEncoded("update".into(), "early".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("TCB-Info-Issuer-Chain", "cert")
|
||||
.with_body("{\"early\":true}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client
|
||||
.get_tdx_tcb_info("test", Some(UpdateType::Early), None)
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap().tcb_info_json, "{\"early\":true}");
|
||||
|
||||
// Test Standard update type
|
||||
let _m2 = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("fmspc".into(), "test".into()),
|
||||
mockito::Matcher::UrlEncoded("update".into(), "standard".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("TCB-Info-Issuer-Chain", "cert")
|
||||
.with_body("{\"standard\":true}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let result2 = client
|
||||
.get_tdx_tcb_info("test", Some(UpdateType::Standard), None)
|
||||
.await;
|
||||
assert!(result2.is_ok());
|
||||
assert_eq!(result2.unwrap().tcb_info_json, "{\"standard\":true}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_v3_api_headers() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// V3 uses different header names for CRL
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v3/pckcrl")
|
||||
.match_query(mockito::Matcher::UrlEncoded("ca".into(), "platform".into()))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", "v3-cert")
|
||||
.with_body("v3-crl-data")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
let result = client.get_pck_crl(CaType::Platform, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(String::from_utf8_lossy(&resp.crl_data), "v3-crl-data");
|
||||
assert_eq!(resp.issuer_chain, "v3-cert");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qve_identity() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/qve/identity")
|
||||
.with_status(200)
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", "qve-cert")
|
||||
.with_body("{\"id\":\"QVE\"}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_sgx_qve_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(resp.enclave_identity_json, "{\"id\":\"QVE\"}");
|
||||
assert_eq!(resp.issuer_chain, "qve-cert");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_qe_identity() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/qe/identity")
|
||||
.with_status(200)
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", "tdx-qe-cert")
|
||||
.with_body("{\"id\":\"TDX-QE\"}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_tdx_qe_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(resp.enclave_identity_json, "{\"id\":\"TDX-QE\"}");
|
||||
assert_eq!(resp.issuer_chain, "tdx-qe-cert");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_with_details() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/pckcert")
|
||||
.match_query(mockito::Matcher::Any)
|
||||
.with_status(400)
|
||||
.with_header("Request-ID", "error-req-123")
|
||||
.with_header("Error-Code", "InvalidParameter")
|
||||
.with_header("Error-Message", "PPID format is invalid")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client
|
||||
.get_pck_certificate_by_ppid("bad", "bad", "bad", "bad", None, None)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
IntelApiError::ApiError {
|
||||
status,
|
||||
request_id,
|
||||
error_code,
|
||||
error_message,
|
||||
} => {
|
||||
assert_eq!(status.as_u16(), 400);
|
||||
assert_eq!(request_id, "error-req-123");
|
||||
assert_eq!(error_code.as_deref(), Some("InvalidParameter"));
|
||||
assert_eq!(error_message.as_deref(), Some("PPID format is invalid"));
|
||||
}
|
||||
_ => panic!("Wrong error type"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_tcb_info() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"00606A6A0000".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("TCB-Info-Issuer-Chain", "sgx-tcb-cert")
|
||||
.with_body("{\"tcbInfo\":{\"fmspc\":\"00606A6A0000\"}}")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = create_test_client(&server.url()).await;
|
||||
let result = client.get_sgx_tcb_info("00606A6A0000", None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let resp = result.unwrap();
|
||||
assert_eq!(
|
||||
resp.tcb_info_json,
|
||||
"{\"tcbInfo\":{\"fmspc\":\"00606A6A0000\"}}"
|
||||
);
|
||||
assert_eq!(resp.issuer_chain, "sgx-tcb-cert");
|
||||
}
|
901
crates/intel-dcap-api/tests/real_data_mock_tests.rs
Normal file
901
crates/intel-dcap-api/tests/real_data_mock_tests.rs
Normal file
|
@ -0,0 +1,901 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2025 Matter Labs
|
||||
|
||||
use intel_dcap_api::{
|
||||
ApiClient, ApiVersion, CaType, CrlEncoding, IntelApiError, PlatformFilter, UpdateType,
|
||||
};
|
||||
use mockito::Server;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
use serde_json::Value;
|
||||
|
||||
// Include real test data
|
||||
const TDX_TCB_INFO_DATA: &[u8] = include_bytes!("test_data/tdx_tcb_info.json");
|
||||
const PCK_CRL_PROCESSOR_DATA: &[u8] = include_bytes!("test_data/pck_crl_processor.json");
|
||||
const PCK_CRL_PLATFORM_DATA: &[u8] = include_bytes!("test_data/pck_crl_platform.json");
|
||||
const SGX_QE_IDENTITY_DATA: &[u8] = include_bytes!("test_data/sgx_qe_identity.json");
|
||||
const SGX_QVE_IDENTITY_DATA: &[u8] = include_bytes!("test_data/sgx_qve_identity.json");
|
||||
const TDX_QE_IDENTITY_DATA: &[u8] = include_bytes!("test_data/tdx_qe_identity.json");
|
||||
const SGX_TCB_INFO_ALT_DATA: &[u8] = include_bytes!("test_data/sgx_tcb_info_alt.json");
|
||||
const SGX_QAE_IDENTITY_DATA: &[u8] = include_bytes!("test_data/sgx_qae_identity.json");
|
||||
const FMSPCS_DATA: &[u8] = include_bytes!("test_data/fmspcs.json");
|
||||
const SGX_TCB_EVAL_NUMS_DATA: &[u8] = include_bytes!("test_data/sgx_tcb_eval_nums.json");
|
||||
const TDX_TCB_EVAL_NUMS_DATA: &[u8] = include_bytes!("test_data/tdx_tcb_eval_nums.json");
|
||||
const PCK_CRL_PROCESSOR_DER_DATA: &[u8] = include_bytes!("test_data/pck_crl_processor_der.json");
|
||||
const SGX_TCB_INFO_EARLY_DATA: &[u8] = include_bytes!("test_data/sgx_tcb_info_early.json");
|
||||
const TDX_TCB_INFO_EVAL17_DATA: &[u8] = include_bytes!("test_data/tdx_tcb_info_eval17.json");
|
||||
const FMSPCS_NO_FILTER_DATA: &[u8] = include_bytes!("test_data/fmspcs_no_filter.json");
|
||||
// const FMSPCS_ALL_PLATFORMS_DATA: &[u8] = include_bytes!("test_data/fmspcs_all_platforms.json"); // Reserved for future use
|
||||
const SGX_QE_IDENTITY_V3_DATA: &[u8] = include_bytes!("test_data/sgx_qe_identity_v3.json");
|
||||
const SGX_TCB_INFO_V3_DATA: &[u8] = include_bytes!("test_data/sgx_tcb_info_v3.json");
|
||||
const TDX_TCB_INFO_ALT_DATA: &[u8] = include_bytes!("test_data/tdx_tcb_info_00806F050000.json");
|
||||
|
||||
fn parse_test_data(data: &[u8]) -> Value {
|
||||
serde_json::from_slice(data).expect("Failed to parse test data")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_info_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_TCB_INFO_DATA);
|
||||
|
||||
// URL encode the issuer chain header value
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"00806F050000".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_tdx_tcb_info("00806F050000", None, None).await;
|
||||
|
||||
if let Err(e) = &result {
|
||||
eprintln!("Error: {:?}", e);
|
||||
eprintln!("Server URL: {}", server.url());
|
||||
}
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.tcb_info_json,
|
||||
test_data["tcb_info_json"].as_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON can be parsed
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00806F050000");
|
||||
assert_eq!(tcb_info["tcbInfo"]["id"], "TDX");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qe_identity_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_QE_IDENTITY_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/qe/identity")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["enclave_identity_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_qe_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.enclave_identity_json,
|
||||
test_data["enclave_identity_json"].as_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON structure
|
||||
let identity: Value = serde_json::from_str(&response.enclave_identity_json).unwrap();
|
||||
assert_eq!(identity["enclaveIdentity"]["id"], "QE");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qve_identity_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_QVE_IDENTITY_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/qve/identity")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["enclave_identity_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_qve_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify the JSON structure
|
||||
let identity: Value = serde_json::from_str(&response.enclave_identity_json).unwrap();
|
||||
assert_eq!(identity["enclaveIdentity"]["id"], "QVE");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_qe_identity_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_QE_IDENTITY_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/qe/identity")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["enclave_identity_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_tdx_qe_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify the JSON structure
|
||||
let identity: Value = serde_json::from_str(&response.enclave_identity_json).unwrap();
|
||||
assert_eq!(identity["enclaveIdentity"]["id"], "TD_QE");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pck_crl_processor_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(PCK_CRL_PROCESSOR_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/pckcrl")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"ca".into(),
|
||||
"processor".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["crl_data"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_pck_crl(CaType::Processor, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&response.crl_data),
|
||||
test_data["crl_data"].as_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify it's a valid CRL format
|
||||
let crl_str = String::from_utf8_lossy(&response.crl_data);
|
||||
assert!(crl_str.contains("BEGIN X509 CRL"));
|
||||
assert!(crl_str.contains("END X509 CRL"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pck_crl_platform_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(PCK_CRL_PLATFORM_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/pckcrl")
|
||||
.match_query(mockito::Matcher::UrlEncoded("ca".into(), "platform".into()))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["crl_data"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_pck_crl(CaType::Platform, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify issuer chain contains multiple certificates
|
||||
assert!(response.issuer_chain.contains("BEGIN CERTIFICATE"));
|
||||
assert!(response.issuer_chain.contains("END CERTIFICATE"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_tcb_info_alt_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_TCB_INFO_ALT_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"00906ED50000".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_tcb_info("00906ED50000", None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify the JSON structure
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00906ED50000");
|
||||
assert_eq!(tcb_info["tcbInfo"]["id"], "SGX");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_with_update_type() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_TCB_INFO_DATA);
|
||||
|
||||
// Test with Early update type
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m1 = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("fmspc".into(), "00806F050000".into()),
|
||||
mockito::Matcher::UrlEncoded("update".into(), "early".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client
|
||||
.get_tdx_tcb_info("00806F050000", Some(UpdateType::Early), None)
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_handling_with_intel_headers() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// Real error response from Intel API
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"invalid".into(),
|
||||
))
|
||||
.with_status(404)
|
||||
.with_header("Request-ID", "abc123def456")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_tcb_info("invalid", None, None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
IntelApiError::ApiError {
|
||||
status, request_id, ..
|
||||
} => {
|
||||
assert_eq!(status.as_u16(), 404);
|
||||
assert_eq!(request_id, "abc123def456");
|
||||
}
|
||||
_ => panic!("Expected ApiError"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_v3_api_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(PCK_CRL_PROCESSOR_DATA);
|
||||
|
||||
// V3 uses different header names
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v3/pckcrl")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"ca".into(),
|
||||
"processor".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["crl_data"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
let result = client.get_pck_crl(CaType::Processor, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&response.crl_data),
|
||||
test_data["crl_data"].as_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qae_identity_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_QAE_IDENTITY_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/qae/identity")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["enclave_identity_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_qae_identity(None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.enclave_identity_json,
|
||||
test_data["enclave_identity_json"].as_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON structure
|
||||
let identity: Value = serde_json::from_str(&response.enclave_identity_json).unwrap();
|
||||
assert_eq!(identity["enclaveIdentity"]["id"], "QAE");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_fmspcs_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(FMSPCS_DATA);
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/fmspcs")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"platform".into(),
|
||||
"all".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_body(test_data["fmspcs_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_fmspcs(Some(PlatformFilter::All)).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let fmspcs_json = result.unwrap();
|
||||
assert_eq!(fmspcs_json, test_data["fmspcs_json"].as_str().unwrap());
|
||||
|
||||
// Verify the JSON structure
|
||||
let fmspcs: Value = serde_json::from_str(&fmspcs_json).unwrap();
|
||||
assert!(fmspcs.is_array());
|
||||
assert!(!fmspcs.as_array().unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_tcb_evaluation_data_numbers_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_TCB_EVAL_NUMS_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcbevaluationdatanumbers")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header(
|
||||
"TCB-Evaluation-Data-Numbers-Issuer-Chain",
|
||||
&encoded_issuer_chain,
|
||||
)
|
||||
.with_body(
|
||||
test_data["tcb_evaluation_data_numbers_json"]
|
||||
.as_str()
|
||||
.unwrap(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_tcb_evaluation_data_numbers().await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.tcb_evaluation_data_numbers_json,
|
||||
test_data["tcb_evaluation_data_numbers_json"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON structure
|
||||
let eval_nums: Value =
|
||||
serde_json::from_str(&response.tcb_evaluation_data_numbers_json).unwrap();
|
||||
assert!(eval_nums.is_object());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_evaluation_data_numbers_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_TCB_EVAL_NUMS_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/tcbevaluationdatanumbers")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header(
|
||||
"TCB-Evaluation-Data-Numbers-Issuer-Chain",
|
||||
&encoded_issuer_chain,
|
||||
)
|
||||
.with_body(
|
||||
test_data["tcb_evaluation_data_numbers_json"]
|
||||
.as_str()
|
||||
.unwrap(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_tdx_tcb_evaluation_data_numbers().await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.tcb_evaluation_data_numbers_json,
|
||||
test_data["tcb_evaluation_data_numbers_json"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pck_crl_der_encoding_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(PCK_CRL_PROCESSOR_DER_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
// The DER data is stored as base64 in our test data
|
||||
let crl_base64 = test_data["crl_data_base64"].as_str().unwrap();
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
let crl_der = general_purpose::STANDARD.decode(crl_base64).unwrap();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/pckcrl")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("ca".into(), "processor".into()),
|
||||
mockito::Matcher::UrlEncoded("encoding".into(), "der".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("SGX-PCK-CRL-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(crl_der)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client
|
||||
.get_pck_crl(CaType::Processor, Some(CrlEncoding::Der))
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify the response data matches
|
||||
let response_base64 = general_purpose::STANDARD.encode(&response.crl_data);
|
||||
assert_eq!(response_base64, crl_base64);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_tcb_info_early_update_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_TCB_INFO_EARLY_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("fmspc".into(), "00906ED50000".into()),
|
||||
mockito::Matcher::UrlEncoded("update".into(), "early".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client
|
||||
.get_sgx_tcb_info("00906ED50000", Some(UpdateType::Early), None)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.tcb_info_json,
|
||||
test_data["tcb_info_json"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON structure
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00906ED50000");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_info_with_eval_number_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_TCB_INFO_EVAL17_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::AllOf(vec![
|
||||
mockito::Matcher::UrlEncoded("fmspc".into(), "00806F050000".into()),
|
||||
mockito::Matcher::UrlEncoded("tcbEvaluationDataNumber".into(), "17".into()),
|
||||
]))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client
|
||||
.get_tdx_tcb_info("00806F050000", None, Some(17))
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify the response
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00806F050000");
|
||||
assert_eq!(tcb_info["tcbInfo"]["id"], "TDX");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_fmspcs_v3_should_fail() {
|
||||
let server = Server::new_async().await;
|
||||
|
||||
// FMSPCs is V4 only
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
let result = client.get_fmspcs(None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
IntelApiError::UnsupportedApiVersion(msg) => {
|
||||
assert!(msg.contains("API v4 only"));
|
||||
}
|
||||
_ => panic!("Expected UnsupportedApiVersion error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tcb_evaluation_data_numbers_v3_should_fail() {
|
||||
let server = Server::new_async().await;
|
||||
|
||||
// TCB evaluation data numbers is V4 only
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
|
||||
let sgx_result = client.get_sgx_tcb_evaluation_data_numbers().await;
|
||||
assert!(sgx_result.is_err());
|
||||
match sgx_result.unwrap_err() {
|
||||
IntelApiError::UnsupportedApiVersion(msg) => {
|
||||
assert!(msg.contains("requires API v4"));
|
||||
}
|
||||
_ => panic!("Expected UnsupportedApiVersion error"),
|
||||
}
|
||||
|
||||
let tdx_result = client.get_tdx_tcb_evaluation_data_numbers().await;
|
||||
assert!(tdx_result.is_err());
|
||||
match tdx_result.unwrap_err() {
|
||||
IntelApiError::UnsupportedApiVersion(msg) => {
|
||||
assert!(msg.contains("requires API v4"));
|
||||
}
|
||||
_ => panic!("Expected UnsupportedApiVersion error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_fmspcs_no_filter_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(FMSPCS_NO_FILTER_DATA);
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/fmspcs")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_body(test_data["fmspcs_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_fmspcs(None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let fmspcs_json = result.unwrap();
|
||||
assert_eq!(fmspcs_json, test_data["fmspcs_json"].as_str().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_qe_identity_v3_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_QE_IDENTITY_V3_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
// V3 uses different header names
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v3/qe/identity")
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-Enclave-Identity-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["enclave_identity_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
let result = client.get_sgx_qe_identity(None, None).await;
|
||||
|
||||
if let Err(e) = &result {
|
||||
eprintln!("Error in V3 test: {:?}", e);
|
||||
}
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.enclave_identity_json,
|
||||
test_data["enclave_identity_json"].as_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
response.issuer_chain,
|
||||
test_data["issuer_chain"].as_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sgx_tcb_info_v3_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(SGX_TCB_INFO_V3_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
// V3 uses different header names
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v3/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"00906ED50000".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("SGX-TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_options(server.url(), ApiVersion::V3).unwrap();
|
||||
let result = client.get_sgx_tcb_info("00906ED50000", None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert_eq!(
|
||||
response.tcb_info_json,
|
||||
test_data["tcb_info_json"].as_str().unwrap()
|
||||
);
|
||||
|
||||
// Verify the JSON structure
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00906ED50000");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tdx_tcb_info_alternate_fmspc_with_real_data() {
|
||||
let mut server = Server::new_async().await;
|
||||
let test_data = parse_test_data(TDX_TCB_INFO_ALT_DATA);
|
||||
|
||||
let issuer_chain = test_data["issuer_chain"].as_str().unwrap();
|
||||
let encoded_issuer_chain =
|
||||
percent_encode(issuer_chain.as_bytes(), NON_ALPHANUMERIC).to_string();
|
||||
|
||||
let _m = server
|
||||
.mock("GET", "/tdx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"00806F050000".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_header("TCB-Info-Issuer-Chain", &encoded_issuer_chain)
|
||||
.with_body(test_data["tcb_info_json"].as_str().unwrap())
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_tdx_tcb_info("00806F050000", None, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
|
||||
// Verify we got the same data as the first TDX TCB info test
|
||||
let tcb_info: Value = serde_json::from_str(&response.tcb_info_json).unwrap();
|
||||
assert_eq!(tcb_info["tcbInfo"]["fmspc"], "00806F050000");
|
||||
assert_eq!(tcb_info["tcbInfo"]["id"], "TDX");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_platform_filter_combinations() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// Test with different platform filters
|
||||
let filters = vec![
|
||||
(Some(PlatformFilter::All), "all"),
|
||||
(Some(PlatformFilter::Client), "client"),
|
||||
(Some(PlatformFilter::E3), "E3"),
|
||||
(Some(PlatformFilter::E5), "E5"),
|
||||
(None, ""),
|
||||
];
|
||||
|
||||
for (filter, query_value) in filters {
|
||||
let mock_response = r#"[{"fmspc": "00906ED50000", "platform": "SGX"}]"#;
|
||||
|
||||
let mut mock = server.mock("GET", "/sgx/certification/v4/fmspcs");
|
||||
|
||||
if !query_value.is_empty() {
|
||||
mock = mock.match_query(mockito::Matcher::UrlEncoded(
|
||||
"platform".into(),
|
||||
query_value.into(),
|
||||
));
|
||||
}
|
||||
|
||||
let _m = mock
|
||||
.with_status(200)
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_body(mock_response)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_fmspcs(filter).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let response = result.unwrap();
|
||||
assert!(response.contains("00906ED50000"));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_scenarios() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
// Test 404 with Error headers
|
||||
let _m = server
|
||||
.mock("GET", "/sgx/certification/v4/tcb")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fmspc".into(),
|
||||
"invalid".into(),
|
||||
))
|
||||
.with_status(404)
|
||||
.with_header("Request-ID", "test123")
|
||||
.with_header("Error-Code", "InvalidParameter")
|
||||
.with_header("Error-Message", "Invalid FMSPC format")
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ApiClient::new_with_base_url(server.url()).unwrap();
|
||||
let result = client.get_sgx_tcb_info("invalid", None, None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
IntelApiError::ApiError {
|
||||
status,
|
||||
request_id,
|
||||
error_code,
|
||||
error_message,
|
||||
} => {
|
||||
assert_eq!(status.as_u16(), 404);
|
||||
assert_eq!(request_id, "test123");
|
||||
assert_eq!(error_code.as_deref(), Some("InvalidParameter"));
|
||||
assert_eq!(error_message.as_deref(), Some("Invalid FMSPC format"));
|
||||
}
|
||||
_ => panic!("Expected ApiError"),
|
||||
}
|
||||
}
|
3
crates/intel-dcap-api/tests/test_data/fmspcs.json
Normal file
3
crates/intel-dcap-api/tests/test_data/fmspcs.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"fmspcs_json": "[{\"fmspc\":\"00A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"70A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"00A06E050000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EA50000\",\"platform\":\"client\"},{\"fmspc\":\"20606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"50806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A067110000\",\"platform\":\"E3\"},{\"fmspc\":\"00606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"00706E470000\",\"platform\":\"client\"},{\"fmspc\":\"00806EA60000\",\"platform\":\"client\"},{\"fmspc\":\"00706A800000\",\"platform\":\"client\"},{\"fmspc\":\"00706A100000\",\"platform\":\"client\"},{\"fmspc\":\"F0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EC50000\",\"platform\":\"client\"},{\"fmspc\":\"90806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06F010000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"B0C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906ED50000\",\"platform\":\"E3\"},{\"fmspc\":\"40A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"D0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A065510000\",\"platform\":\"client\"},{\"fmspc\":\"10A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"30606A000000\",\"platform\":\"E5\"},{\"fmspc\":\"20806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EA10000\",\"platform\":\"E3\"},{\"fmspc\":\"30806F040000\",\"platform\":\"E5\"},{\"fmspc\":\"C0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"30A06D050000\",\"platform\":\"E5\"},{\"fmspc\":\"60C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F050000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"20906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"90C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"80C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EB10000\",\"platform\":\"client\"},{\"fmspc\":\"00606A000000\",\"platform\":\"E5\"}]"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"fmspcs_json": "[{\"fmspc\":\"00A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"70A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"00A06E050000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EA50000\",\"platform\":\"client\"},{\"fmspc\":\"20606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"50806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A067110000\",\"platform\":\"E3\"},{\"fmspc\":\"00606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"00706E470000\",\"platform\":\"client\"},{\"fmspc\":\"00806EA60000\",\"platform\":\"client\"},{\"fmspc\":\"00706A800000\",\"platform\":\"client\"},{\"fmspc\":\"00706A100000\",\"platform\":\"client\"},{\"fmspc\":\"F0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EC50000\",\"platform\":\"client\"},{\"fmspc\":\"90806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06F010000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"B0C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906ED50000\",\"platform\":\"E3\"},{\"fmspc\":\"40A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"D0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A065510000\",\"platform\":\"client\"},{\"fmspc\":\"10A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"30606A000000\",\"platform\":\"E5\"},{\"fmspc\":\"20806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EA10000\",\"platform\":\"E3\"},{\"fmspc\":\"30806F040000\",\"platform\":\"E5\"},{\"fmspc\":\"C0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"30A06D050000\",\"platform\":\"E5\"},{\"fmspc\":\"60C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F050000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"20906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"90C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"80C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EB10000\",\"platform\":\"client\"},{\"fmspc\":\"00606A000000\",\"platform\":\"E5\"}]"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"fmspcs_json": "[{\"fmspc\":\"00A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"70A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"00A06E050000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EA50000\",\"platform\":\"client\"},{\"fmspc\":\"20606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"50806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A067110000\",\"platform\":\"E3\"},{\"fmspc\":\"00606C040000\",\"platform\":\"E5\"},{\"fmspc\":\"00706E470000\",\"platform\":\"client\"},{\"fmspc\":\"00806EA60000\",\"platform\":\"client\"},{\"fmspc\":\"00706A800000\",\"platform\":\"client\"},{\"fmspc\":\"00706A100000\",\"platform\":\"client\"},{\"fmspc\":\"F0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EC50000\",\"platform\":\"client\"},{\"fmspc\":\"90806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06F010000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"B0C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906ED50000\",\"platform\":\"E3\"},{\"fmspc\":\"40A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"D0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00A065510000\",\"platform\":\"client\"},{\"fmspc\":\"10A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"30606A000000\",\"platform\":\"E5\"},{\"fmspc\":\"20806EB70000\",\"platform\":\"client\"},{\"fmspc\":\"00906EA10000\",\"platform\":\"E3\"},{\"fmspc\":\"30806F040000\",\"platform\":\"E5\"},{\"fmspc\":\"C0806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"30A06D050000\",\"platform\":\"E5\"},{\"fmspc\":\"60C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"20A06D080000\",\"platform\":\"E5\"},{\"fmspc\":\"10A06D000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F050000\",\"platform\":\"E5\"},{\"fmspc\":\"60A06D070000\",\"platform\":\"E5\"},{\"fmspc\":\"20906EC10000\",\"platform\":\"client\"},{\"fmspc\":\"90C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"80C06F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00806F000000\",\"platform\":\"E5\"},{\"fmspc\":\"00906EB10000\",\"platform\":\"client\"},{\"fmspc\":\"00606A000000\",\"platform\":\"E5\"}]"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"crl_data": "-----BEGIN X509 CRL-----\nMIIKYTCCCggCAQEwCgYIKoZIzj0EAwIwcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBD\nSyBQbGF0Zm9ybSBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNV\nBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTI1MDUy\nNzE5MjUwNVoXDTI1MDYyNjE5MjUwNVowggk0MDMCFG/DTlAj5yiSNDXWGqS4PGGB\nZq01Fw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAO+ubpcV/KE7h+Mz\n6CYe1tmQqSatFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAP1ghkhi\nnLpzB4tNSS9LPqdBrQjNFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIV\nAIr5JBhOHVr93XPD1joS9ei1c35WFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMK\nAQEwNAIVALEleXjPqczdB1mr+MXKcvrjp4qbFw0yNTA1MjcxOTI1MDVaMAwwCgYD\nVR0VBAMKAQEwMwIUdP6mFKlyvg4oQ/IFmDWBHthy+bMXDTI1MDUyNzE5MjUwNVow\nDDAKBgNVHRUEAwoBATA0AhUA+cTvVrOrSNV34Qi67fS/iAFCFLkXDTI1MDUyNzE5\nMjUwNVowDDAKBgNVHRUEAwoBATAzAhQHHeB3j55fxPKHjzDWsHyaMOazCxcNMjUw\nNTI3MTkyNTA1WjAMMAoGA1UdFQQDCgEBMDQCFQDN4kJPlyzqlP8jmTf02AwlAp3W\nCxcNMjUwNTI3MTkyNTA1WjAMMAoGA1UdFQQDCgEBMDMCFGwzGeUQm2RQfTzxEyzg\nA0nvUnMZFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAN8I11a2anSX\n9DtbtYraBNP096k3Fw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwMwIUKK9I\nW2z2fkCaOdXLWu5FmPeo+nsXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATA0\nAhUA+4strsCSytqKqbxP8vHCDQNGZowXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUE\nAwoBATA0AhUAzUhQrFK9zGmmpvBYyLxXu9C1+GQXDTI1MDUyNzE5MjUwNVowDDAK\nBgNVHRUEAwoBATA0AhUAmU3TZm9SdfuAX5XdAr1QyyZ52K0XDTI1MDUyNzE5MjUw\nNVowDDAKBgNVHRUEAwoBATAzAhQHAhNpACUidNkDXu31RXRi+tDvTBcNMjUwNTI3\nMTkyNTA1WjAMMAoGA1UdFQQDCgEBMDMCFGHyv3Pjm04EqifYAb1z0kMZtb+AFw0y\nNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwMwIUOZK+hRuWkC7/OJWebC7/GwZR\npLUXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATAzAhQP2kOgC2jqebfC3q6s\nC0mL37KvkBcNMjUwNTI3MTkyNTA1WjAMMAoGA1UdFQQDCgEBMDMCFGOfE5pQQP3P\n8ZHopPsb8IbtYDlxFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAJWd\nUz+SSdweUTVEzcgwvxm38fMBFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEw\nMwIUeuN3SKn5EvTGO6erB8WTzh0dEYEXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUE\nAwoBATAzAhQTiEszJpk4wZWqFw/KddoXdTjfCxcNMjUwNTI3MTkyNTA1WjAMMAoG\nA1UdFQQDCgEBMDQCFQCF08k4G3en4E0RnJ5a1nSf8/+rhxcNMjUwNTI3MTkyNTA1\nWjAMMAoGA1UdFQQDCgEBMDQCFQCTiHykQR56kjvR/tKBmylJ8gG1tBcNMjUwNTI3\nMTkyNTA1WjAMMAoGA1UdFQQDCgEBMDMCFCSY3GKDkwmW/YvyOjesviajvtRXFw0y\nNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAIpm8adJSIZnaJzDkDrFTGYr\ncS5zFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAK/BNhC902y3mF0Q\nZIGogNOgH9oHFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIVAO/gSywz\n0DaqyWymc78emke2TVy7Fw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwNAIV\nAIPZrI2LtQnRxsgJrXEuhDBVntfzFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMK\nAQEwMwIUeTH9ULUHHBu/xbe23ti0W52LhSkXDTI1MDUyNzE5MjUwNVowDDAKBgNV\nHRUEAwoBATAzAhQfog4pcL3l1X97jd+DOUhOHx0IIxcNMjUwNTI3MTkyNTA1WjAM\nMAoGA1UdFQQDCgEBMDMCFB6HssOzLY0j5BHO80GXuVrwyK31Fw0yNTA1MjcxOTI1\nMDVaMAwwCgYDVR0VBAMKAQEwNAIVAJr9LukKRzVQoWfZlpEUN8dQLR8JFw0yNTA1\nMjcxOTI1MDVaMAwwCgYDVR0VBAMKAQEwMwIURIGw8RcooTtpbT6px3CgsV7FjdoX\nDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATA0AhUAp4WfV5gu8OZ9N7yO8u9a\nyDX/GqkXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATA0AhUAnWd1O4HkcJCu\np2P77ExFSbzbmTMXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATAzAhQ0v7t6\nHZxWgUfhGLYU97du0+9o3xcNMjUwNTI3MTkyNTA1WjAMMAoGA1UdFQQDCgEBMDMC\nFCw8xv6SedsVFtXOOfKomM2loXXhFw0yNTA1MjcxOTI1MDVaMAwwCgYDVR0VBAMK\nAQEwMwIUcXlIaHUJI0vpeeS33ObzG+9ktowXDTI1MDUyNzE5MjUwNVowDDAKBgNV\nHRUEAwoBATA0AhUAnXbvLDnBNuhli25zlrHXRFonYx8XDTI1MDUyNzE5MjUwNVow\nDDAKBgNVHRUEAwoBATA0AhUAw+Al/KmV829ZtIRnk54+NOY2Gm8XDTI1MDUyNzE5\nMjUwNVowDDAKBgNVHRUEAwoBATA0AhUAjF9rMlfaBbF0KeLmG6ll1nMwYGoXDTI1\nMDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBATA0AhUAoXxRci7B4MMnj+i98FIFnL7E\n5kgXDTI1MDUyNzE5MjUwNVowDDAKBgNVHRUEAwoBAaAvMC0wCgYDVR0UBAMCAQEw\nHwYDVR0jBBgwFoAUlW9dzb0b4elAScnU9DPOAVcL3lQwCgYIKoZIzj0EAwIDRwAw\nRAIgUpcU4PTB0Bc3qvMCWYHx5EEDXqxSLgCoYKp4C/GgxpkCIE/xDOudQg2ldK1m\nABQqvvzE8ibtGcDyaq1WI56Wv1bl\n-----END X509 CRL-----\n",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAg\nBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVs\nIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0Ex\nCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO\n2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJl\neTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBS\nBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2Vy\ndmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9d\nzb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1Ue\nnA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"crl_data": "-----BEGIN X509 CRL-----\nMIIBKjCB0QIBATAKBggqhkjOPQQDAjBxMSMwIQYDVQQDDBpJbnRlbCBTR1ggUENL\nIFByb2Nlc3NvciBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNV\nBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTI1MDUy\nNzE4NDYyNVoXDTI1MDYyNjE4NDYyNVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQY\nMBaAFNDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMCA0gAMEUCIQDtYSVu\nju3asUsAGZ2Hbe9uvZmk5zvLtwDk38KrWfb5zAIgSfk6Dmqhc4+moiRuRz0wQqLj\nckwO2BEUviI+nZfN75I=\n-----END X509 CRL-----\n",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICmDCCAj6gAwIBAgIVANDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHExIzAh\nBgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQKDBFJbnRl\nbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNB\nMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL9q+NMp2IOg\ntdl1bk/uWZ5+TGQm8aCi8z78fs+fKCQ3d+uDzXnVTAT2ZhDCifyIuJwvN3wNBp9i\nHBSSMJMJrBOjgbswgbgwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqww\nUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNl\ncnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFNDo\nqtp11/kuSReYPHsUZdDV8llNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEAMAoGCCqGSM49BAMCA0gAMEUCIQCJgTbtVqOyZ1m3jqiAXM6QYa6r5sWS\n4y/G7y8uIJGxdwIgRqPvBSKzzQagBLQq5s5A70pdoiaRJ8z/0uDz4NgV91k=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"crl_data_base64": "MIIBKjCB0QIBATAKBggqhkjOPQQDAjBxMSMwIQYDVQQDDBpJbnRlbCBTR1ggUENLIFByb2Nlc3NvciBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTI1MDUyNzE5MjMwOVoXDTI1MDYyNjE5MjMwOVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFNDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMCA0gAMEUCIQC2Q0kz4IioOr5HsdYUY8b0m3XSS6FwuKVUAIvroURNHgIgIo5mAP1gCBeW719AqdBaxnoNuUypHQ/X+1zfDiY69ec=",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICmDCCAj6gAwIBAgIVANDoqtp11/kuSReYPHsUZdDV8llNMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHExIzAh\nBgNVBAMMGkludGVsIFNHWCBQQ0sgUHJvY2Vzc29yIENBMRowGAYDVQQKDBFJbnRl\nbCBDb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNB\nMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL9q+NMp2IOg\ntdl1bk/uWZ5+TGQm8aCi8z78fs+fKCQ3d+uDzXnVTAT2ZhDCifyIuJwvN3wNBp9i\nHBSSMJMJrBOjgbswgbgwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqww\nUgYDVR0fBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNl\ncnZpY2VzLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFNDo\nqtp11/kuSReYPHsUZdDV8llNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEAMAoGCCqGSM49BAMCA0gAMEUCIQCJgTbtVqOyZ1m3jqiAXM6QYa6r5sWS\n4y/G7y8uIJGxdwIgRqPvBSKzzQagBLQq5s5A70pdoiaRJ8z/0uDz4NgV91k=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enclave_identity_json": "{\"enclaveIdentity\":{\"id\":\"QAE\",\"version\":2,\"issueDate\":\"2025-05-27T19:31:54Z\",\"nextUpdate\":\"2025-06-26T19:31:54Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"01000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":3,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},\"signature\":\"a5dfb799f78ea3d32f7760f2b529fc80fe7efa3236c9888e8ece69379e206880f0b67b9407a9b139feb5007b785601f09050d4963116c1bd2cd5def4e3a11da8\"}",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enclave_identity_json": "{\"enclaveIdentity\":{\"id\":\"QE\",\"version\":2,\"issueDate\":\"2025-05-27T19:05:27Z\",\"nextUpdate\":\"2025-06-26T19:05:27Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":1,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":8},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":6},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00615\"]},{\"tcb\":{\"isvsvn\":5},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00615\"]},{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00334\",\"INTEL-SA-00477\",\"INTEL-SA-00615\"]},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00293\",\"INTEL-SA-00334\",\"INTEL-SA-00477\",\"INTEL-SA-00615\"]},{\"tcb\":{\"isvsvn\":1},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00202\",\"INTEL-SA-00219\",\"INTEL-SA-00293\",\"INTEL-SA-00334\",\"INTEL-SA-00477\",\"INTEL-SA-00615\"]}]},\"signature\":\"5ecc03899589b58e8216c69c3439d1a9310d8af9ebfb37e61518a2a3cb801e0019a5fc955e38e6becc1c75a8a05bb337c93c1a61009a34cc8291fdd82f67ae19\"}",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enclave_identity_json": "{\"enclaveIdentity\":{\"id\":\"QE\",\"version\":2,\"issueDate\":\"2025-05-27T18:38:43Z\",\"nextUpdate\":\"2025-06-26T18:38:43Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":1,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":8},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":6},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"isvsvn\":5},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"isvsvn\":1},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]},\"signature\":\"b4cc7bd5ee712a62cf6fbad0a052bd44194a25a5313b4bfff241a3c08ff00bcf0d15f1feb3a369bd9b362a6e5104c82f06d827ef676e70fdccf947566b77f6e8\"}",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enclave_identity_json": "{\"enclaveIdentity\":{\"id\":\"QVE\",\"version\":2,\"issueDate\":\"2025-05-27T19:31:54Z\",\"nextUpdate\":\"2025-06-26T19:31:54Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"01000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":2,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},\"signature\":\"3bb26b16155b207f884ef10fad705129bf566ccc9e6bd4e9907c99bc0ccd6deb6b6451b103b495926c582ece9d22c491f05a627806e09ca07e1063de898460e7\"}",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n",
|
||||
"tcb_evaluation_data_numbers_json": "{\"tcbEvaluationDataNumbers\":{\"id\":\"SGX\",\"version\":1,\"issueDate\":\"2025-05-27T19:04:23Z\",\"nextUpdate\":\"2025-06-26T19:04:23Z\",\"tcbEvalNumbers\":[{\"tcbEvaluationDataNumber\":19,\"tcbRecoveryEventDate\":\"2025-05-13T00:00:00Z\",\"tcbDate\":\"2025-05-14T00:00:00Z\"},{\"tcbEvaluationDataNumber\":18,\"tcbRecoveryEventDate\":\"2024-11-12T00:00:00Z\",\"tcbDate\":\"2024-11-13T00:00:00Z\"},{\"tcbEvaluationDataNumber\":17,\"tcbRecoveryEventDate\":\"2024-03-12T00:00:00Z\",\"tcbDate\":\"2024-03-13T00:00:00Z\"}]},\"signature\":\"19799ae10942dc046340aa279123fe743e2ab51c862ab6a04abdaab86083013ca81ac1963aa08f1a3b44f0c12e9c6d094cb98aa5ca51bc40439833ada6f0e9e1\"}"
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enclave_identity_json": "{\"enclaveIdentity\":{\"id\":\"TD_QE\",\"version\":2,\"issueDate\":\"2025-05-27T19:22:46Z\",\"nextUpdate\":\"2025-06-26T19:22:46Z\",\"tcbEvaluationDataNumber\":17,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5\",\"isvprodid\":2,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},\"signature\":\"cba4e80e12e114ac591bcf43c155cabb2f48bc6e629dce6d5aab26127c7c23b1a3eafc52f60bab7ac39aff2866431494315fd553fa73d9688a802383eea4edc9\"}",
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"issuer_chain": "-----BEGIN CERTIFICATE-----\nMIICjTCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTI1MDUwNjA5MjUwMFoXDTMyMDUwNjA5MjUwMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0kAMEYCIQDdmmRuAo3qCO8TC1IoJMITAoOEw4dlgEBHzSz1TuMSTAIh\nAKVTqOkt59+co0O3m3hC+v5Fb00FjYWcgeu3EijOULo5\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n",
|
||||
"tcb_evaluation_data_numbers_json": "{\"tcbEvaluationDataNumbers\":{\"id\":\"TDX\",\"version\":1,\"issueDate\":\"2025-05-27T18:50:21Z\",\"nextUpdate\":\"2025-06-26T18:50:21Z\",\"tcbEvalNumbers\":[{\"tcbEvaluationDataNumber\":19,\"tcbRecoveryEventDate\":\"2025-05-13T00:00:00Z\",\"tcbDate\":\"2025-05-14T00:00:00Z\"},{\"tcbEvaluationDataNumber\":18,\"tcbRecoveryEventDate\":\"2024-11-12T00:00:00Z\",\"tcbDate\":\"2024-11-13T00:00:00Z\"},{\"tcbEvaluationDataNumber\":17,\"tcbRecoveryEventDate\":\"2024-03-12T00:00:00Z\",\"tcbDate\":\"2024-03-13T00:00:00Z\"}]},\"signature\":\"507f5f4406a15e9fa311142b41c47da082d10ce35e863491061c49adac188a2e974561b134a7eff9e10f98be2c5bd5e28c18e3dc6327067fd1a5459d48cd1e58\"}"
|
||||
}
|
4
crates/intel-dcap-api/tests/test_data/tdx_tcb_info.json
Normal file
4
crates/intel-dcap-api/tests/test_data/tdx_tcb_info.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
293
crates/teepot-tdx-attest-rs/Cargo.lock
generated
Normal file
293
crates/teepot-tdx-attest-rs/Cargo.lock
generated
Normal file
|
@ -0,0 +1,293 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tdx-attest-rs"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"teepot-tdx-attest-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tdx-attest-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
15
crates/teepot-tdx-attest-rs/Cargo.toml
Normal file
15
crates/teepot-tdx-attest-rs/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Fork of the original crate: https://github.com/intel/SGXDataCenterAttestationPrimitives
|
||||
|
||||
[package]
|
||||
name = "teepot-tdx-attest-rs"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/matter-labs/teepot"
|
||||
homepage = "https://github.com/matter-labs/teepot"
|
||||
description = "Fork of tdx-attest-rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tdx-attest-sys = { version = "0.1.0", path = "../teepot-tdx-attest-sys", package = "teepot-tdx-attest-sys" }
|
38
crates/teepot-tdx-attest-rs/License.txt
Normal file
38
crates/teepot-tdx-attest-rs/License.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
BSD License
|
||||
|
||||
Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
==============================================================
|
||||
|
||||
pce.signed.dll, qve.signed.dll,id_enclave.signed.dll and qe3.signed.dll,
|
||||
libsgx_pce.signed.so, libsgx_qve.signed.so, libsgx_id_enclave.signed.so,
|
||||
libsgx_qe3.signed.so and libsgx_tdqe.signed.so are licensed under
|
||||
3-Clause BSD License.
|
||||
|
149
crates/teepot-tdx-attest-rs/README.md
Normal file
149
crates/teepot-tdx-attest-rs/README.md
Normal file
|
@ -0,0 +1,149 @@
|
|||
# teepot-tdx-attest-rs
|
||||
|
||||
[](https://crates.io/crates/teepot-tdx-attest-rs)
|
||||
[](https://docs.rs/teepot-tdx-attest-rs)
|
||||
[](LICENSE)
|
||||
|
||||
Rust bindings for Intel TDX (Trust Domain Extensions) attestation functionality. This crate provides a safe Rust interface to the Intel TDX attestation library, enabling trusted execution environments to generate attestation quotes and reports.
|
||||
|
||||
This is a fork of the original [tdx-attest-rs](https://github.com/intel/SGXDataCenterAttestationPrimitives) crate, maintained as part of the [Teepot](https://github.com/matter-labs/teepot) project.
|
||||
|
||||
## Features
|
||||
|
||||
- Request TDX attestation quotes
|
||||
- Generate TDX reports
|
||||
- Extend runtime measurement registers (RTMRs)
|
||||
- Query supported attestation key IDs
|
||||
- Safe Rust wrappers around the Intel TDX attestation C library
|
||||
|
||||
## Installation
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
teepot-tdx-attest-rs = "0.1.2"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Generate a TDX Quote
|
||||
|
||||
```rust
|
||||
use teepot_tdx_attest_rs::*;
|
||||
|
||||
// Prepare report data (typically a hash you want to bind to the quote)
|
||||
let tdx_report_data = tdx_report_data_t {
|
||||
d: [0; 64], // Your data here
|
||||
};
|
||||
|
||||
// List of supported attestation key IDs
|
||||
let att_key_id_list = [tdx_uuid_t {
|
||||
d: [0; 16], // Your key ID
|
||||
}];
|
||||
|
||||
let mut att_key_id = tdx_uuid_t {
|
||||
d: [0; 16],
|
||||
};
|
||||
|
||||
// Request the quote
|
||||
let (result, quote) = tdx_att_get_quote(
|
||||
Some(&tdx_report_data),
|
||||
Some(&att_key_id_list),
|
||||
Some(&mut att_key_id),
|
||||
0
|
||||
);
|
||||
|
||||
match result {
|
||||
tdx_attest_error_t::TDX_ATTEST_SUCCESS => {
|
||||
// Process the quote
|
||||
if let Some(quote_data) = quote {
|
||||
println!("Quote generated successfully, size: {}", quote_data.len());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Failed to generate quote: {:?}", result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Generate a TDX Report
|
||||
|
||||
```rust
|
||||
use teepot_tdx_attest_rs::*;
|
||||
|
||||
let tdx_report_data = tdx_report_data_t {
|
||||
d: [0; 64], // Your report data
|
||||
};
|
||||
|
||||
let mut tdx_report = tdx_report_t {
|
||||
d: [0; 1024],
|
||||
};
|
||||
|
||||
let result = tdx_att_get_report(Some(&tdx_report_data), &mut tdx_report);
|
||||
|
||||
if result == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
||||
println!("Report generated successfully");
|
||||
}
|
||||
```
|
||||
|
||||
### Extend RTMR
|
||||
|
||||
```rust
|
||||
use teepot_tdx_attest_rs::*;
|
||||
|
||||
// Prepare RTMR event data
|
||||
let rtmr_event = vec![0u8; 68]; // Your event data
|
||||
|
||||
let result = tdx_att_extend(&rtmr_event);
|
||||
|
||||
if result == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
||||
println!("RTMR extended successfully");
|
||||
}
|
||||
```
|
||||
|
||||
### Get Supported Attestation Key IDs
|
||||
|
||||
```rust
|
||||
use teepot_tdx_attest_rs::*;
|
||||
|
||||
let (result, att_key_ids) = tdx_att_get_supported_att_key_ids();
|
||||
|
||||
if result == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
||||
if let Some(ids) = att_key_ids {
|
||||
println!("Found {} supported attestation key IDs", ids.len());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The crate uses the `tdx_attest_error_t` enum for error reporting. Common error values include:
|
||||
|
||||
- `TDX_ATTEST_SUCCESS` - Operation completed successfully
|
||||
- `TDX_ATTEST_ERROR_INVALID_PARAMETER` - Invalid parameter provided
|
||||
- `TDX_ATTEST_ERROR_DEVICE_FAILURE` - Failed to access TDX attestation device
|
||||
- `TDX_ATTEST_ERROR_OUT_OF_MEMORY` - Memory allocation failure
|
||||
- `TDX_ATTEST_ERROR_UNSUPPORTED_ATT_KEY_ID` - Unsupported attestation key ID
|
||||
|
||||
## Requirements
|
||||
|
||||
- Intel TDX-enabled hardware
|
||||
- TDX attestation runtime environment
|
||||
- The `teepot-tdx-attest-sys` crate (automatically included as a dependency)
|
||||
|
||||
## Safety
|
||||
|
||||
This crate provides safe Rust wrappers around unsafe FFI calls to the Intel TDX attestation library. All pointer operations are handled internally, and the API uses Rust's type system to ensure safety.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD-3-Clause License - see the [License.txt](License.txt) file for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
This is a fork maintained as part of the Teepot project. For contributions, please visit the [Teepot repository](https://github.com/matter-labs/teepot).
|
||||
|
||||
## Original Work
|
||||
|
||||
This crate is based on Intel's SGX Data Center Attestation Primitives. The original source can be found at [Intel's repository](https://github.com/intel/SGXDataCenterAttestationPrimitives).
|
302
crates/teepot-tdx-attest-rs/src/lib.rs
Normal file
302
crates/teepot-tdx-attest-rs/src/lib.rs
Normal file
|
@ -0,0 +1,302 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2022 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
//! This is the Intel TDX attestation library for Rust.
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::{mem, option::Option};
|
||||
pub use tdx_attest_sys::{
|
||||
tdx_attest_error_t, tdx_report_data_t, tdx_report_t, tdx_rtmr_event_t, tdx_uuid_t,
|
||||
};
|
||||
|
||||
/// Request a Quote of the calling TD.
|
||||
///
|
||||
/// # Param
|
||||
/// - **tdx_report_data**\
|
||||
/// A set of data that the caller/TD wants to cryptographically bind to the Quote, typically a hash. May be all zeros for the Report data.
|
||||
/// - **att_key_id_list**\
|
||||
/// List (array) of the attestation key IDs supported by the Quote verifier.
|
||||
/// - **att_key_id**\
|
||||
/// The selected attestation key ID when the function returns.
|
||||
/// - **flags**\
|
||||
/// Reserved, must be zero.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***TDX_ATTEST_SUCCESS***\
|
||||
/// Successfully generated the Quote.\
|
||||
/// - ***TDX_ATTEST_ERROR_UNSUPPORTED_ATT_KEY_ID***\
|
||||
/// The platform Quoting infrastructure does not support any of the keys.\
|
||||
/// - ***TDX_ATT_ERROR_INVALID_PARAMETER***\
|
||||
/// The parameter is incorrect.\
|
||||
/// - ***TDX_ATTEST_ERROR_DEVICE_FAILURE***\
|
||||
/// Failed to acess tdx attest device.\
|
||||
/// - ***TDX_ATTEST_ERROR_VSOCK_FAILURE***\
|
||||
/// vsock related failure.\
|
||||
/// - ***TDX_ATTEST_ERROR_OUT_OF_MEMORY***\
|
||||
/// Heap memory allocation error in library or enclave.\
|
||||
/// - ***TDX_ATT_ERROR_UNEXPECTED***\
|
||||
/// An unexpected internal error occurred.\
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tdx_attest_rs::*;
|
||||
///
|
||||
/// let tdx_report_data = tdx_report_data_t{
|
||||
/// d: [0; 64usize],
|
||||
/// };
|
||||
/// let att_key_id_list = [tdx_uuid_t{
|
||||
/// d: [0; 16usize],
|
||||
/// }; 2usize];
|
||||
/// let list_size = 1024;
|
||||
/// let mut att_key_id = tdx_uuid_t{
|
||||
/// d: [0; 16usize],
|
||||
/// };
|
||||
/// let result = tdx_att_get_quote(Some(&tdx_report_data), Some(&att_key_id_list), Some(&mut att_key_id), 0);
|
||||
/// ```
|
||||
pub fn tdx_att_get_quote(
|
||||
tdx_report_data: Option<&tdx_report_data_t>,
|
||||
att_key_id_list: Option<&[tdx_uuid_t]>,
|
||||
att_key_id: Option<&mut tdx_uuid_t>,
|
||||
flags: u32,
|
||||
) -> (tdx_attest_error_t, Option<Vec<u8>>) {
|
||||
let p_tdx_report_data = match tdx_report_data {
|
||||
Some(p) => p as *const tdx_report_data_t,
|
||||
None => &tdx_report_data_t { d: [0; 64usize] },
|
||||
};
|
||||
let (p_att_key_id_list, att_key_id_list_size) = match att_key_id_list {
|
||||
Some(p) => (p.as_ptr() as *const tdx_uuid_t, p.len() as u32),
|
||||
None => (std::ptr::null(), 0u32),
|
||||
};
|
||||
let p_att_key_id = match att_key_id {
|
||||
Some(p) => p as *mut tdx_uuid_t,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
let mut buf = std::ptr::null_mut();
|
||||
let mut buf_len = 0;
|
||||
unsafe {
|
||||
let result = tdx_attest_sys::tdx_att_get_quote(
|
||||
p_tdx_report_data,
|
||||
p_att_key_id_list,
|
||||
att_key_id_list_size,
|
||||
p_att_key_id,
|
||||
&mut buf,
|
||||
&mut buf_len,
|
||||
flags,
|
||||
);
|
||||
match result {
|
||||
tdx_attest_error_t::TDX_ATTEST_SUCCESS => {
|
||||
assert!(!buf.is_null());
|
||||
assert!(buf_len > 0);
|
||||
let quote = std::slice::from_raw_parts(buf, buf_len as usize).to_vec();
|
||||
tdx_attest_sys::tdx_att_free_quote(buf);
|
||||
return (result, Some(quote));
|
||||
}
|
||||
_ => return (result, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Request a TDX Report of the calling TD.
|
||||
///
|
||||
/// # Param
|
||||
/// - **tdx_report_data**\
|
||||
/// A set of data that the caller/TD wants to cryptographically bind to the Quote, typically a hash. May be all zeros for the Report data.
|
||||
/// - **tdx_report**\
|
||||
/// the generated TDX Report.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***TDX_ATTEST_SUCCESS***\
|
||||
/// Successfully generate report.\
|
||||
/// - ***TDX_ATTEST_ERROR_INVALID_PARAMETER***\
|
||||
/// The parameter is incorrect.
|
||||
/// - ***TDX_ATTEST_ERROR_DEVICE_FAILURE***\
|
||||
/// Failed to acess tdx attest device.\
|
||||
/// - ***TDX_ATTEST_ERROR_REPORT_FAILURE***\
|
||||
/// Failed to get the TD Report.\
|
||||
/// - ***TDX_ATT_ERROR_UNEXPECTED***\
|
||||
/// An unexpected internal error occurred.\
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tdx_attest_rs::*;
|
||||
///
|
||||
/// let tdx_report_data = tdx_report_data_t{
|
||||
/// d: [0; 64usize],
|
||||
/// };
|
||||
/// let mut tdx_report =tdx_report_t{
|
||||
/// d: [0; 1024usize],
|
||||
/// };
|
||||
/// let result = tdx_att_get_report(Some(&tdx_report_data), &mut tdx_report);
|
||||
/// ```
|
||||
pub fn tdx_att_get_report(
|
||||
tdx_report_data: Option<&tdx_report_data_t>,
|
||||
tdx_report: &mut tdx_report_t,
|
||||
) -> tdx_attest_error_t {
|
||||
let p_tdx_report_data = match tdx_report_data {
|
||||
Some(p) => p as *const tdx_report_data_t,
|
||||
None => &tdx_report_data_t { d: [0; 64usize] },
|
||||
};
|
||||
unsafe { tdx_attest_sys::tdx_att_get_report(p_tdx_report_data, tdx_report) }
|
||||
}
|
||||
|
||||
/// Extend one of the TDX runtime measurement registers (RTMRs).
|
||||
///
|
||||
/// # Param
|
||||
/// - **rtmr_event**\
|
||||
/// A set of data that contains the index of the RTMR to extend, the data with which to extend it and a description of the data.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***TDX_ATTEST_SUCCESS***\
|
||||
/// Successfully extended the RTMR.\
|
||||
/// - ***TDX_ATTEST_ERROR_INVALID_PARAMETER***\
|
||||
/// The parameter is incorrect.
|
||||
/// - ***TDX_ATTEST_ERROR_DEVICE_FAILURE***\
|
||||
/// Failed to acess tdx attest device.\
|
||||
/// - ***TDX_ATTEST_ERROR_INVALID_RTMR_INDEX***\
|
||||
/// Only supported RTMR index is 2 and 3.\
|
||||
/// - ***TDX_ATTEST_ERROR_EXTEND_FAILURE***\
|
||||
/// Failed to extend data.\
|
||||
/// - ***TDX_ATTEST_ERROR_NOT_SUPPORTED***\
|
||||
/// rtmr_event.event_data_size != 0.\
|
||||
/// - ***TDX_ATT_ERROR_UNEXPECTED***\
|
||||
/// An unexpected internal error occurred.\
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tdx_attest_rs::*;
|
||||
///
|
||||
/// let rtmr_event = [0u8; 68usize];
|
||||
/// let result = tdx_att_extend(&rtmr_event);
|
||||
/// ```
|
||||
|
||||
pub fn tdx_att_extend(rtmr_event: &[u8]) -> tdx_attest_error_t {
|
||||
if rtmr_event.len() < mem::size_of::<tdx_rtmr_event_t>() {
|
||||
return tdx_attest_error_t::TDX_ATTEST_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
unsafe {
|
||||
let s: tdx_rtmr_event_t = std::ptr::read(rtmr_event.as_ptr() as *const _);
|
||||
if rtmr_event.len() - mem::size_of::<tdx_rtmr_event_t>() != s.event_data_size as usize {
|
||||
return tdx_attest_error_t::TDX_ATTEST_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
tdx_attest_sys::tdx_att_extend(rtmr_event.as_ptr() as *const tdx_rtmr_event_t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the list of attestation key IDs supported by the platform.
|
||||
///
|
||||
/// # Param
|
||||
///
|
||||
/// # Return
|
||||
/// - ***TDX_ATTEST_SUCCESS***\
|
||||
/// Successfully populated the att_key_id_list.\
|
||||
/// - ***TDX_ATT_ERROR_UNEXPECTED***\
|
||||
/// An unexpected internal error occurred.\
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tdx_attest_rs::*;
|
||||
/// let (result, att_key_id_list) = tdx_att_get_supported_att_key_ids();
|
||||
/// ```
|
||||
pub fn tdx_att_get_supported_att_key_ids() -> (tdx_attest_error_t, Option<Vec<tdx_uuid_t>>) {
|
||||
let mut list_count = 0;
|
||||
unsafe {
|
||||
let result = tdx_attest_sys::tdx_att_get_supported_att_key_ids(
|
||||
std::ptr::null_mut() as *mut tdx_uuid_t,
|
||||
&mut list_count,
|
||||
);
|
||||
match result {
|
||||
tdx_attest_error_t::TDX_ATTEST_SUCCESS => {
|
||||
let mut att_key_id_list = vec![tdx_uuid_t { d: [0; 16usize] }; list_count as usize];
|
||||
let result = tdx_attest_sys::tdx_att_get_supported_att_key_ids(
|
||||
att_key_id_list.as_mut_ptr(),
|
||||
&mut list_count,
|
||||
);
|
||||
match result {
|
||||
tdx_attest_error_t::TDX_ATTEST_SUCCESS => {
|
||||
return (result, Some(att_key_id_list))
|
||||
}
|
||||
_ => return (result, None),
|
||||
}
|
||||
}
|
||||
_ => return (result, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_tdx_att_get_report() {
|
||||
let tdx_report_data = tdx_report_data_t { d: [0; 64usize] };
|
||||
let mut tdx_report = tdx_report_t { d: [0; 1024usize] };
|
||||
let result = tdx_att_get_report(Some(&tdx_report_data), &mut tdx_report);
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_ERROR_DEVICE_FAILURE);
|
||||
let result = tdx_att_get_report(None, &mut tdx_report);
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_ERROR_DEVICE_FAILURE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tdx_att_get_quote() {
|
||||
let tdx_report_data = tdx_report_data_t { d: [0; 64usize] };
|
||||
let mut att_key_id = tdx_uuid_t { d: [0; 16usize] };
|
||||
let (result, quote) =
|
||||
tdx_att_get_quote(Some(&tdx_report_data), None, Some(&mut att_key_id), 0);
|
||||
println!("att_key_id {:?}", att_key_id.d);
|
||||
match quote {
|
||||
q => println!("quote {:?}", q),
|
||||
}
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_ERROR_DEVICE_FAILURE);
|
||||
let (result, _quote) = tdx_att_get_quote(None, None, None, 0);
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_ERROR_DEVICE_FAILURE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tdx_att_extend() {
|
||||
let mut rtmr_event = [0u8; mem::size_of::<tdx_rtmr_event_t>()];
|
||||
rtmr_event[0] = 1;
|
||||
let result = tdx_att_extend(&rtmr_event);
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_ERROR_DEVICE_FAILURE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tdx_att_get_supported_att_key_ids() {
|
||||
let (result, att_key_ids) = tdx_att_get_supported_att_key_ids();
|
||||
let ids = att_key_ids.unwrap();
|
||||
println!("att_key_id size {:?}", ids.len());
|
||||
for id in ids {
|
||||
println!("att_key_id {:?}", id.d);
|
||||
}
|
||||
assert_eq!(result, tdx_attest_error_t::TDX_ATTEST_SUCCESS);
|
||||
}
|
||||
}
|
286
crates/teepot-tdx-attest-sys/Cargo.lock
generated
Normal file
286
crates/teepot-tdx-attest-sys/Cargo.lock
generated
Normal file
|
@ -0,0 +1,286 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tdx-attest-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
17
crates/teepot-tdx-attest-sys/Cargo.toml
Normal file
17
crates/teepot-tdx-attest-sys/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Fork of the original crate: https://github.com/intel/SGXDataCenterAttestationPrimitives
|
||||
|
||||
[package]
|
||||
name = "teepot-tdx-attest-sys"
|
||||
version = "0.1.0"
|
||||
links = "tdx_attest"
|
||||
edition = "2021"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/matter-labs/teepot"
|
||||
homepage = "https://github.com/matter-labs/teepot"
|
||||
description = "Fork of tdx-attest-sys"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.70.1"
|
38
crates/teepot-tdx-attest-sys/License.txt
Normal file
38
crates/teepot-tdx-attest-sys/License.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
BSD License
|
||||
|
||||
Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
==============================================================
|
||||
|
||||
pce.signed.dll, qve.signed.dll,id_enclave.signed.dll and qe3.signed.dll,
|
||||
libsgx_pce.signed.so, libsgx_qve.signed.so, libsgx_id_enclave.signed.so,
|
||||
libsgx_qe3.signed.so and libsgx_tdqe.signed.so are licensed under
|
||||
3-Clause BSD License.
|
||||
|
46
crates/teepot-tdx-attest-sys/README.md
Normal file
46
crates/teepot-tdx-attest-sys/README.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# teepot-tdx-attest-sys
|
||||
|
||||
[](https://crates.io/crates/teepot-tdx-attest-sys)
|
||||
[](https://docs.rs/teepot-tdx-attest-sys)
|
||||
[](https://github.com/matter-labs/teepot/blob/main/crates/teepot-tdx-attest-sys/License.txt)
|
||||
|
||||
Raw FFI bindings to Intel TDX Attestation Library (`libtdx_attest`).
|
||||
|
||||
This crate provides low-level FFI bindings for Intel Trust Domain Extensions (TDX) attestation functionality. It is a fork of the original [tdx-attest-sys](https://github.com/intel/SGXDataCenterAttestationPrimitives) crate from Intel's SGX Data Center Attestation Primitives.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using this crate, you need to install:
|
||||
|
||||
- Intel® SGX DCAP Driver
|
||||
- Intel® SGX SDK
|
||||
- Intel® SGX DCAP Packages
|
||||
- Intel® SGX DCAP PCCS (Provisioning Certificate Caching Service)
|
||||
|
||||
Please refer to the [SGX DCAP Linux installation guide](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_SW_Installation_Guide_for_Linux.pdf) for detailed installation instructions.
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
teepot-tdx-attest-sys = "0.1.0"
|
||||
```
|
||||
|
||||
This crate provides raw FFI bindings. For a more ergonomic Rust API, consider using a higher-level wrapper crate.
|
||||
|
||||
## Building
|
||||
|
||||
The crate uses `bindgen` to generate Rust bindings from the C headers during build time. Make sure you have:
|
||||
|
||||
- The TDX attestation library (`libtdx_attest`) installed on your system
|
||||
- If using Intel SGX SDK, set the `SGX_SDK` environment variable to point to your SDK installation
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD-3-Clause License. See the [License.txt](License.txt) file for details.
|
||||
|
||||
## Repository
|
||||
|
||||
This crate is part of the [Teepot](https://github.com/matter-labs/teepot) project.
|
33
crates/teepot-tdx-attest-sys/bindings.h
Normal file
33
crates/teepot-tdx-attest-sys/bindings.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2022 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tdx_attest.h"
|
85
crates/teepot-tdx-attest-sys/build.rs
Normal file
85
crates/teepot-tdx-attest-sys/build.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2022 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
extern crate bindgen;
|
||||
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
// Tell cargo to tell rustc to link the system tdx_attest
|
||||
// shared library.
|
||||
println!("cargo:rustc-link-lib=tdx_attest");
|
||||
|
||||
// Tell cargo to invalidate the built crate whenever the wrapper changes
|
||||
println!("cargo:rerun-if-changed=bindings.h");
|
||||
|
||||
// Set sdk to search path if SGX_SDK is in environment variable
|
||||
let mut sdk_inc = String::from("");
|
||||
match env::var("SGX_SDK") {
|
||||
Ok(val) => {
|
||||
sdk_inc.push_str("-I");
|
||||
sdk_inc.push_str(&val);
|
||||
sdk_inc.push_str("/include/");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// The bindgen::Builder is the main entry point
|
||||
// to bindgen, and lets you build up options for
|
||||
// the resulting bindings.
|
||||
let bindings = bindgen::Builder::default()
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
.header("bindings.h")
|
||||
// Include search path
|
||||
.clang_arg(sdk_inc)
|
||||
// Convert C enum to Rust enum
|
||||
.rustified_enum("_tdx_attest_error_t")
|
||||
// Disable debug trait for packed C structures
|
||||
.no_debug("_tdx_uuid_t")
|
||||
.no_debug("_tdx_report_data_t")
|
||||
.no_debug("_tdx_report_t")
|
||||
.no_debug("_tdx_rtmr_event_t")
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
// included header files changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
// Finish the builder and generate the bindings.
|
||||
.generate()
|
||||
// Unwrap the Result and panic on failure.
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
50
crates/teepot-tdx-attest-sys/src/lib.rs
Normal file
50
crates/teepot-tdx-attest-sys/src/lib.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2022 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
//! Intel(R) Software Guard Extensions Data Center Attestation Primitives (Intel(R) SGX DCAP)
|
||||
//! Rust raw FFI bindings for TDX Attestation Library
|
||||
//! ================================================
|
||||
//!
|
||||
//! Please install the following prerequisite:
|
||||
//! * Intel(R) SGX DCAP Driver
|
||||
//! * Intel(R) SGX SDK
|
||||
//! * Intel(R) SGX DCAP Packages
|
||||
//! * Intel(R) SGX DCAP PCCS (Provisioning Certificate Caching Service)
|
||||
//!
|
||||
//! *Please refer to [SGX DCAP Linux installation guide](
|
||||
//! https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_SW_Installation_Guide_for_Linux.pdf)
|
||||
//! to install above dependencies.*
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
516
crates/teepot-tee-quote-verification-rs/Cargo.lock
generated
Normal file
516
crates/teepot-tee-quote-verification-rs/Cargo.lock
generated
Normal file
|
@ -0,0 +1,516 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.65.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "intel-tee-quote-verification-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93c8bc48d598fa48310e41f65a706e0beb2a74f5f9e5a26c5c2ca6cd83416fcc"
|
||||
dependencies = [
|
||||
"bindgen 0.65.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tdx-attest-rs"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"teepot-tdx-attest-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tdx-attest-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen 0.70.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "teepot-tee-quote-verification-rs"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"intel-tee-quote-verification-sys",
|
||||
"serde",
|
||||
"teepot-tdx-attest-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "teepot-tee-quote-verification-rs"
|
||||
version = "0.3.0"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/matter-labs/teepot"
|
||||
|
@ -14,3 +14,4 @@ serde = { version = "1", features = ["derive", "rc"] }
|
|||
|
||||
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
|
||||
intel-tee-quote-verification-sys = { version = "0.2.1" }
|
||||
teepot-tdx-attest-rs = { version = "0.1.2", path = "../teepot-tdx-attest-rs" }
|
||||
|
|
182
crates/teepot-tee-quote-verification-rs/README.md
Normal file
182
crates/teepot-tee-quote-verification-rs/README.md
Normal file
|
@ -0,0 +1,182 @@
|
|||
# teepot-tee-quote-verification-rs
|
||||
|
||||
[](https://crates.io/crates/teepot-tee-quote-verification-rs)
|
||||
[](https://docs.rs/teepot-tee-quote-verification-rs)
|
||||
[](https://github.com/matter-labs/teepot/blob/main/LICENSE)
|
||||
|
||||
A Rust wrapper for Intel® Software Guard Extensions (SGX) and Trust Domain Extensions (TDX) quote verification.
|
||||
|
||||
This crate is a fork of the original [intel-tee-quote-verification-rs](https://github.com/intel/SGXDataCenterAttestationPrimitives) crate, providing safe Rust bindings for the Intel Quote Verification Library (QVL).
|
||||
|
||||
## Features
|
||||
|
||||
- Safe Rust wrappers for SGX and TDX quote verification APIs
|
||||
- Support for both SGX ECDSA and TDX ECDSA quote verification
|
||||
- Collateral management for quote verification
|
||||
- Supplemental data handling
|
||||
- Cross-platform support (Linux x86_64)
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
teepot-tee-quote-verification-rs = "0.6.0"
|
||||
```
|
||||
|
||||
### Example: Verify an SGX Quote
|
||||
|
||||
```rust
|
||||
use teepot_tee_quote_verification_rs::*;
|
||||
|
||||
fn verify_sgx_quote(quote: &[u8]) -> Result<(), quote3_error_t> {
|
||||
// Get collateral for the quote
|
||||
let collateral = tee_qv_get_collateral(quote)?;
|
||||
|
||||
// Get supplemental data size
|
||||
let supp_data_size = sgx_qv_get_quote_supplemental_data_size()?;
|
||||
let mut supp_data = sgx_ql_qv_supplemental_t::default();
|
||||
|
||||
// Verify the quote
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
|
||||
let (expiration_status, verification_result) = sgx_qv_verify_quote(
|
||||
quote,
|
||||
Some(&collateral),
|
||||
current_time,
|
||||
None, // QvE report info (None for host-based verification)
|
||||
supp_data_size,
|
||||
Some(&mut supp_data),
|
||||
)?;
|
||||
|
||||
match verification_result {
|
||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => {
|
||||
println!("Quote verification passed!");
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
println!("Quote verification failed: {:?}", verification_result);
|
||||
Err(quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Verify a TDX Quote
|
||||
|
||||
```rust
|
||||
use teepot_tee_quote_verification_rs::*;
|
||||
|
||||
fn verify_tdx_quote(quote: &[u8]) -> Result<(), quote3_error_t> {
|
||||
// Get collateral for the quote
|
||||
let collateral = tee_qv_get_collateral(quote)?;
|
||||
|
||||
// Get supplemental data size
|
||||
let supp_data_size = tdx_qv_get_quote_supplemental_data_size()?;
|
||||
let mut supp_data = sgx_ql_qv_supplemental_t::default();
|
||||
|
||||
// Verify the quote
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
|
||||
let (expiration_status, verification_result) = tdx_qv_verify_quote(
|
||||
quote,
|
||||
Some(&collateral),
|
||||
current_time,
|
||||
None, // QvE report info
|
||||
supp_data_size,
|
||||
Some(&mut supp_data),
|
||||
)?;
|
||||
|
||||
match verification_result {
|
||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => {
|
||||
println!("TDX quote verification passed!");
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
println!("TDX quote verification failed: {:?}", verification_result);
|
||||
Err(quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Unified TEE Quote Verification
|
||||
|
||||
For a unified interface that works with both SGX and TDX quotes:
|
||||
|
||||
```rust
|
||||
use teepot_tee_quote_verification_rs::*;
|
||||
|
||||
fn verify_tee_quote(quote: &[u8]) -> Result<(), quote3_error_t> {
|
||||
// Get collateral
|
||||
let collateral = tee_qv_get_collateral(quote)?;
|
||||
|
||||
// Get supplemental data version and size
|
||||
let (version, data_size) = tee_get_supplemental_data_version_and_size(quote)?;
|
||||
|
||||
// Prepare supplemental data descriptor
|
||||
let mut supp_data_desc = tee_supp_data_descriptor_t {
|
||||
major_version: version,
|
||||
data_size,
|
||||
p_data: std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
// Allocate buffer for supplemental data if needed
|
||||
let mut supp_data_buffer = vec![0u8; data_size as usize];
|
||||
if data_size > 0 {
|
||||
supp_data_desc.p_data = supp_data_buffer.as_mut_ptr();
|
||||
}
|
||||
|
||||
// Verify quote
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
|
||||
let (expiration_status, verification_result) = tee_verify_quote(
|
||||
quote,
|
||||
Some(&collateral),
|
||||
current_time,
|
||||
None,
|
||||
Some(&mut supp_data_desc),
|
||||
)?;
|
||||
|
||||
println!("Verification result: {:?}", verification_result);
|
||||
println!("Collateral expiration status: {}", expiration_status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Support
|
||||
|
||||
This crate is currently supported on:
|
||||
- Linux x86_64
|
||||
|
||||
On other platforms, the crate will compile but provide stub implementations.
|
||||
|
||||
## Dependencies
|
||||
|
||||
On Linux x86_64, this crate depends on:
|
||||
- `intel-tee-quote-verification-sys`: System bindings for Intel QVL
|
||||
- `teepot-tdx-attest-rs`: TDX attestation support
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD-3-Clause License. See the [LICENSE](https://github.com/matter-labs/teepot/blob/main/LICENSE) file for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request to the [Teepot repository](https://github.com/matter-labs/teepot).
|
||||
|
||||
## Related Crates
|
||||
|
||||
- [intel-tee-quote-verification-rs](https://github.com/intel/SGXDataCenterAttestationPrimitives) - The original Intel crate
|
||||
- [teepot-tdx-attest-rs](https://crates.io/crates/teepot-tdx-attest-rs) - TDX attestation support
|
3
crates/teepot-tee-quote-verification-rs/src/empty.rs
Normal file
3
crates/teepot-tee-quote-verification-rs/src/empty.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
pub const NOTHING_TO_SEE_HERE: u8 = 0;
|
558
crates/teepot-tee-quote-verification-rs/src/intel.rs
Normal file
558
crates/teepot-tee-quote-verification-rs/src/intel.rs
Normal file
|
@ -0,0 +1,558 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
//! Intel(R) Software Guard Extensions Data Center Attestation Primitives (Intel(R) SGX DCAP)
|
||||
//! Rust wrapper for Quote Verification Library
|
||||
//! ================================================
|
||||
//!
|
||||
//! This is a safe wrapper for **sgx-dcap-quoteverify-sys**.
|
||||
|
||||
pub mod tdx_attest_rs {
|
||||
pub use teepot_tdx_attest_rs::*;
|
||||
}
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{marker::PhantomData, ops::Deref, slice};
|
||||
|
||||
use intel_tee_quote_verification_sys as qvl_sys;
|
||||
pub use qvl_sys::{
|
||||
quote3_error_t, sgx_ql_qe_report_info_t, sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t,
|
||||
sgx_ql_qve_collateral_t, sgx_ql_request_policy_t, sgx_qv_path_type_t, tdx_ql_qve_collateral_t,
|
||||
tee_qv_free_collateral, tee_supp_data_descriptor_t,
|
||||
};
|
||||
|
||||
/// When the Quoting Verification Library is linked to a process, it needs to know the proper enclave loading policy.
|
||||
/// The library may be linked with a long lived process, such as a service, where it can load the enclaves and leave
|
||||
/// them loaded (persistent). This better ensures that the enclaves will be available upon quote requests and not subject
|
||||
/// to EPC limitations if loaded on demand. However, if the Quoting library is linked with an application process, there
|
||||
/// may be many applications with the Quoting library and a better utilization of EPC is to load and unloaded the quoting
|
||||
/// enclaves on demand (ephemeral). The library will be shipped with a default policy of loading enclaves and leaving
|
||||
/// them loaded until the library is unloaded (PERSISTENT). If the policy is set to EPHEMERAL, then the QE and PCE will
|
||||
/// be loaded and unloaded on-demand. If either enclave is already loaded when the policy is change to EPHEMERAL, the
|
||||
/// enclaves will be unloaded before returning.
|
||||
///
|
||||
/// # Param
|
||||
/// - **policy**\
|
||||
/// Set the requested enclave loading policy to either *SGX_QL_PERSISTENT*, *SGX_QL_EPHEMERAL* or *SGX_QL_DEFAULT*.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***SGX_QL_SUCCESS***\
|
||||
/// Successfully set the enclave loading policy for the quoting library's enclaves.\
|
||||
/// - ***SGX_QL_UNSUPPORTED_LOADING_POLICY***\
|
||||
/// The selected policy is not support by the quoting library.\
|
||||
/// - ***SGX_QL_ERROR_UNEXPECTED***\
|
||||
/// Unexpected internal error.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let policy = sgx_ql_request_policy_t::SGX_QL_DEFAULT;
|
||||
/// let ret = sgx_qv_set_enclave_load_policy(policy);
|
||||
///
|
||||
/// assert_eq!(ret, quote3_error_t::SGX_QL_SUCCESS);
|
||||
/// ```
|
||||
pub fn sgx_qv_set_enclave_load_policy(policy: sgx_ql_request_policy_t) -> quote3_error_t {
|
||||
unsafe { qvl_sys::sgx_qv_set_enclave_load_policy(policy) }
|
||||
}
|
||||
|
||||
/// Get SGX supplemental data required size.
|
||||
///
|
||||
/// # Return
|
||||
/// Size of the supplemental data in bytes.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_ERROR_QVL_QVE_MISMATCH*
|
||||
/// - *SGX_QL_ENCLAVE_LOAD_ERROR*
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let data_size = sgx_qv_get_quote_supplemental_data_size().unwrap();
|
||||
///
|
||||
/// assert_eq!(data_size, std::mem::size_of::<sgx_ql_qv_supplemental_t>() as u32);
|
||||
/// ```
|
||||
pub fn sgx_qv_get_quote_supplemental_data_size() -> Result<u32, quote3_error_t> {
|
||||
let mut data_size = 0u32;
|
||||
unsafe {
|
||||
match qvl_sys::sgx_qv_get_quote_supplemental_data_size(&mut data_size) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok(data_size),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform SGX ECDSA quote verification.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supplemental_data_size**\
|
||||
/// Size of the supplemental data (in bytes).
|
||||
/// - **supplemental_data**\
|
||||
/// The parameter is optional. If it is None, supplemental_data_size must be 0.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn sgx_qv_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supplemental_data_size: u32,
|
||||
supplemental_data: Option<&mut sgx_ql_qv_supplemental_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = match qve_report_info {
|
||||
Some(p) => p,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
let p_supplemental_data = match supplemental_data {
|
||||
Some(p) => p as *mut sgx_ql_qv_supplemental_t as *mut u8,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::sgx_qv_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
supplemental_data_size,
|
||||
p_supplemental_data,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get TDX supplemental data required size.
|
||||
///
|
||||
/// # Return
|
||||
/// Size of the supplemental data in bytes.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_ERROR_QVL_QVE_MISMATCH*
|
||||
/// - *SGX_QL_ENCLAVE_LOAD_ERROR*
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let data_size = tdx_qv_get_quote_supplemental_data_size().unwrap();
|
||||
///
|
||||
/// assert_eq!(data_size, std::mem::size_of::<sgx_ql_qv_supplemental_t>() as u32);
|
||||
/// ```
|
||||
pub fn tdx_qv_get_quote_supplemental_data_size() -> Result<u32, quote3_error_t> {
|
||||
let mut data_size = 0u32;
|
||||
unsafe {
|
||||
match qvl_sys::tdx_qv_get_quote_supplemental_data_size(&mut data_size) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok(data_size),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform TDX ECDSA quote verification.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// TDX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supplemental_data_size**\
|
||||
/// Size of the supplemental data (in bytes).
|
||||
/// - **supplemental_data**\
|
||||
/// The parameter is optional. If it is None, supplemental_data_size must be 0.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tdx_qv_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supplemental_data_size: u32,
|
||||
supplemental_data: Option<&mut sgx_ql_qv_supplemental_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = match qve_report_info {
|
||||
Some(p) => p,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
let p_supplemental_data = match supplemental_data {
|
||||
Some(p) => p as *mut sgx_ql_qv_supplemental_t as *mut u8,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tdx_qv_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
supplemental_data_size,
|
||||
p_supplemental_data,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the full path of QVE and QPL library.\
|
||||
/// The function takes the enum and the corresponding full path.
|
||||
///
|
||||
/// # Param
|
||||
/// - **path_type**\
|
||||
/// The type of binary being passed in.
|
||||
/// - **path**\
|
||||
/// It should be a valid full path.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***SGX_QL_SUCCESS***\
|
||||
/// Successfully set the full path.
|
||||
/// - ***SGX_QL_ERROR_INVALID_PARAMETER***\
|
||||
/// Path is not a valid full path or the path is too long.
|
||||
///
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn sgx_qv_set_path(path_type: sgx_qv_path_type_t, path: &str) -> quote3_error_t {
|
||||
match std::ffi::CString::new(path) {
|
||||
Ok(path) => unsafe { qvl_sys::sgx_qv_set_path(path_type, path.as_ptr()) },
|
||||
_ => quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Collateral {
|
||||
pub major_version: u16,
|
||||
pub minor_version: u16,
|
||||
pub tee_type: u32,
|
||||
pub pck_crl_issuer_chain: Box<[u8]>,
|
||||
pub root_ca_crl: Box<[u8]>,
|
||||
pub pck_crl: Box<[u8]>,
|
||||
pub tcb_info_issuer_chain: Box<[u8]>,
|
||||
pub tcb_info: Box<[u8]>,
|
||||
pub qe_identity_issuer_chain: Box<[u8]>,
|
||||
pub qe_identity: Box<[u8]>,
|
||||
}
|
||||
|
||||
// referential struct
|
||||
struct SgxQlQveCollateralT<'a> {
|
||||
inner: sgx_ql_qve_collateral_t,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
// create the referential struct
|
||||
impl<'a> From<&'a Collateral> for SgxQlQveCollateralT<'a> {
|
||||
fn from(data: &'a Collateral) -> Self {
|
||||
let mut this = SgxQlQveCollateralT {
|
||||
inner: sgx_ql_qve_collateral_t {
|
||||
__bindgen_anon_1: Default::default(),
|
||||
tee_type: data.tee_type,
|
||||
pck_crl_issuer_chain: data.pck_crl_issuer_chain.as_ptr() as _,
|
||||
pck_crl_issuer_chain_size: data.pck_crl_issuer_chain.len() as _,
|
||||
root_ca_crl: data.root_ca_crl.as_ptr() as _,
|
||||
root_ca_crl_size: data.root_ca_crl.len() as _,
|
||||
pck_crl: data.pck_crl.as_ptr() as _,
|
||||
pck_crl_size: data.pck_crl.len() as _,
|
||||
tcb_info_issuer_chain: data.tcb_info_issuer_chain.as_ptr() as _,
|
||||
tcb_info_issuer_chain_size: data.tcb_info_issuer_chain.len() as _,
|
||||
tcb_info: data.tcb_info.as_ptr() as _,
|
||||
tcb_info_size: data.tcb_info.len() as _,
|
||||
qe_identity_issuer_chain: data.qe_identity_issuer_chain.as_ptr() as _,
|
||||
qe_identity_issuer_chain_size: data.qe_identity_issuer_chain.len() as _,
|
||||
qe_identity: data.qe_identity.as_ptr() as _,
|
||||
qe_identity_size: data.qe_identity.len() as _,
|
||||
},
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
this.inner.__bindgen_anon_1.__bindgen_anon_1.major_version = data.major_version;
|
||||
this.inner.__bindgen_anon_1.__bindgen_anon_1.minor_version = data.minor_version;
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SgxQlQveCollateralT<'_> {
|
||||
type Target = sgx_ql_qve_collateral_t;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Get quote verification collateral.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of quote_collateral.
|
||||
///
|
||||
/// - **quote_collateral**\
|
||||
/// This is the Quote Certification Collateral retrieved based on Quote.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_PLATFORM_LIB_UNAVAILABLE*
|
||||
/// - *SGX_QL_PCK_CERT_CHAIN_ERROR*
|
||||
/// - *SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_OUT_OF_MEMORY*
|
||||
/// - *SGX_QL_NO_QUOTE_COLLATERAL_DATA*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tee_qv_get_collateral(quote: &[u8]) -> Result<Collateral, quote3_error_t> {
|
||||
fn try_into_collateral(
|
||||
buf: *const sgx_ql_qve_collateral_t,
|
||||
buf_len: u32,
|
||||
) -> Result<Collateral, quote3_error_t> {
|
||||
fn try_into_boxed_slice(
|
||||
p: *mut ::std::os::raw::c_char,
|
||||
size: u32,
|
||||
) -> Result<Box<[u8]>, quote3_error_t> {
|
||||
if p.is_null() || !p.is_aligned() {
|
||||
return Err(quote3_error_t::SGX_QL_ERROR_MAX);
|
||||
}
|
||||
Ok(Box::from(unsafe {
|
||||
slice::from_raw_parts(p as _, size as _)
|
||||
}))
|
||||
}
|
||||
|
||||
if buf.is_null()
|
||||
|| (buf_len as usize) < size_of::<sgx_ql_qve_collateral_t>()
|
||||
|| !buf.is_aligned()
|
||||
{
|
||||
return Err(quote3_error_t::SGX_QL_ERROR_MAX);
|
||||
}
|
||||
|
||||
// SAFETY: buf is not null, buf_len is not zero, and buf is aligned.
|
||||
let collateral = unsafe { *buf };
|
||||
|
||||
Ok(Collateral {
|
||||
major_version: unsafe { collateral.__bindgen_anon_1.__bindgen_anon_1.major_version },
|
||||
minor_version: unsafe { collateral.__bindgen_anon_1.__bindgen_anon_1.minor_version },
|
||||
tee_type: collateral.tee_type,
|
||||
pck_crl_issuer_chain: try_into_boxed_slice(
|
||||
collateral.pck_crl_issuer_chain,
|
||||
collateral.pck_crl_issuer_chain_size,
|
||||
)?,
|
||||
root_ca_crl: try_into_boxed_slice(collateral.root_ca_crl, collateral.root_ca_crl_size)?,
|
||||
pck_crl: try_into_boxed_slice(collateral.pck_crl, collateral.pck_crl_size)?,
|
||||
tcb_info_issuer_chain: try_into_boxed_slice(
|
||||
collateral.tcb_info_issuer_chain,
|
||||
collateral.tcb_info_issuer_chain_size,
|
||||
)?,
|
||||
tcb_info: try_into_boxed_slice(collateral.tcb_info, collateral.tcb_info_size)?,
|
||||
qe_identity_issuer_chain: try_into_boxed_slice(
|
||||
collateral.qe_identity_issuer_chain,
|
||||
collateral.qe_identity_issuer_chain_size,
|
||||
)?,
|
||||
qe_identity: try_into_boxed_slice(collateral.qe_identity, collateral.qe_identity_size)?,
|
||||
})
|
||||
}
|
||||
|
||||
let mut buf = std::ptr::null_mut();
|
||||
let mut buf_len = 0u32;
|
||||
|
||||
match unsafe {
|
||||
qvl_sys::tee_qv_get_collateral(quote.as_ptr(), quote.len() as u32, &mut buf, &mut buf_len)
|
||||
} {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
let collateral = try_into_collateral(buf as _, buf_len);
|
||||
|
||||
match unsafe { tee_qv_free_collateral(buf) } {
|
||||
quote3_error_t::SGX_QL_SUCCESS => collateral,
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get supplemental data latest version and required size, support both SGX and TDX.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (version, data_size) tuple.
|
||||
///
|
||||
/// - **version**\
|
||||
/// Latest version of the supplemental data.
|
||||
/// - **data_size**\
|
||||
/// The size of the buffer in bytes required to contain all of the supplemental data.
|
||||
///
|
||||
pub fn tee_get_supplemental_data_version_and_size(
|
||||
quote: &[u8],
|
||||
) -> Result<(u32, u32), quote3_error_t> {
|
||||
let mut version = 0u32;
|
||||
let mut data_size = 0u32;
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tee_get_supplemental_data_version_and_size(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
&mut version,
|
||||
&mut data_size,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok((version, data_size)),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform quote verification for SGX and TDX.\
|
||||
/// This API works the same as the old one, but takes a new parameter to describe the supplemental data (supp_data_descriptor).
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supp_datal_descriptor**\
|
||||
/// *tee_supp_data_descriptor_t* structure.\
|
||||
/// You can specify the major version of supplemental data by setting supp_datal_descriptor.major_version.\
|
||||
/// If supp_datal_descriptor is None, no supplemental data is returned.\
|
||||
/// If supp_datal_descriptor.major_version == 0, then return the latest version of the *sgx_ql_qv_supplemental_t* structure.\
|
||||
/// If supp_datal_descriptor.major_version <= latest supported version, return the latest minor version associated with that major version.\
|
||||
/// If supp_datal_descriptor.major_version > latest supported version, return an error *SGX_QL_SUPPLEMENTAL_DATA_VERSION_NOT_SUPPORTED*.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tee_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supp_data_descriptor: Option<&mut tee_supp_data_descriptor_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = qve_report_info.map_or(std::ptr::null_mut(), |p| p);
|
||||
|
||||
let p_supp_data_descriptor = supp_data_descriptor.map_or(std::ptr::null_mut(), |p| p);
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tee_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral as _,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
p_supp_data_descriptor,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,557 +1,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2024-2025 Matter Labs
|
||||
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
//! Intel(R) Software Guard Extensions Data Center Attestation Primitives (Intel(R) SGX DCAP)
|
||||
//! Rust wrapper for Quote Verification Library
|
||||
//! ================================================
|
||||
//!
|
||||
//! This is a safe wrapper for **sgx-dcap-quoteverify-sys**.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{marker::PhantomData, ops::Deref, slice};
|
||||
#[cfg_attr(all(target_os = "linux", target_arch = "x86_64"), path = "intel.rs")]
|
||||
#[cfg_attr(
|
||||
not(all(target_os = "linux", target_arch = "x86_64")),
|
||||
path = "empty.rs"
|
||||
)]
|
||||
mod os;
|
||||
|
||||
use intel_tee_quote_verification_sys as qvl_sys;
|
||||
pub use qvl_sys::{
|
||||
quote3_error_t, sgx_ql_qe_report_info_t, sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t,
|
||||
sgx_ql_qve_collateral_t, sgx_ql_request_policy_t, sgx_qv_path_type_t, tdx_ql_qve_collateral_t,
|
||||
tee_qv_free_collateral, tee_supp_data_descriptor_t,
|
||||
};
|
||||
|
||||
/// When the Quoting Verification Library is linked to a process, it needs to know the proper enclave loading policy.
|
||||
/// The library may be linked with a long lived process, such as a service, where it can load the enclaves and leave
|
||||
/// them loaded (persistent). This better ensures that the enclaves will be available upon quote requests and not subject
|
||||
/// to EPC limitations if loaded on demand. However, if the Quoting library is linked with an application process, there
|
||||
/// may be many applications with the Quoting library and a better utilization of EPC is to load and unloaded the quoting
|
||||
/// enclaves on demand (ephemeral). The library will be shipped with a default policy of loading enclaves and leaving
|
||||
/// them loaded until the library is unloaded (PERSISTENT). If the policy is set to EPHEMERAL, then the QE and PCE will
|
||||
/// be loaded and unloaded on-demand. If either enclave is already loaded when the policy is change to EPHEMERAL, the
|
||||
/// enclaves will be unloaded before returning.
|
||||
///
|
||||
/// # Param
|
||||
/// - **policy**\
|
||||
/// Set the requested enclave loading policy to either *SGX_QL_PERSISTENT*, *SGX_QL_EPHEMERAL* or *SGX_QL_DEFAULT*.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***SGX_QL_SUCCESS***\
|
||||
/// Successfully set the enclave loading policy for the quoting library's enclaves.\
|
||||
/// - ***SGX_QL_UNSUPPORTED_LOADING_POLICY***\
|
||||
/// The selected policy is not support by the quoting library.\
|
||||
/// - ***SGX_QL_ERROR_UNEXPECTED***\
|
||||
/// Unexpected internal error.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let policy = sgx_ql_request_policy_t::SGX_QL_DEFAULT;
|
||||
/// let ret = sgx_qv_set_enclave_load_policy(policy);
|
||||
///
|
||||
/// assert_eq!(ret, quote3_error_t::SGX_QL_SUCCESS);
|
||||
/// ```
|
||||
pub fn sgx_qv_set_enclave_load_policy(policy: sgx_ql_request_policy_t) -> quote3_error_t {
|
||||
unsafe { qvl_sys::sgx_qv_set_enclave_load_policy(policy) }
|
||||
}
|
||||
|
||||
/// Get SGX supplemental data required size.
|
||||
///
|
||||
/// # Return
|
||||
/// Size of the supplemental data in bytes.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_ERROR_QVL_QVE_MISMATCH*
|
||||
/// - *SGX_QL_ENCLAVE_LOAD_ERROR*
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let data_size = sgx_qv_get_quote_supplemental_data_size().unwrap();
|
||||
///
|
||||
/// assert_eq!(data_size, std::mem::size_of::<sgx_ql_qv_supplemental_t>() as u32);
|
||||
/// ```
|
||||
pub fn sgx_qv_get_quote_supplemental_data_size() -> Result<u32, quote3_error_t> {
|
||||
let mut data_size = 0u32;
|
||||
unsafe {
|
||||
match qvl_sys::sgx_qv_get_quote_supplemental_data_size(&mut data_size) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok(data_size),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform SGX ECDSA quote verification.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supplemental_data_size**\
|
||||
/// Size of the supplemental data (in bytes).
|
||||
/// - **supplemental_data**\
|
||||
/// The parameter is optional. If it is None, supplemental_data_size must be 0.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn sgx_qv_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supplemental_data_size: u32,
|
||||
supplemental_data: Option<&mut sgx_ql_qv_supplemental_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = match qve_report_info {
|
||||
Some(p) => p,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
let p_supplemental_data = match supplemental_data {
|
||||
Some(p) => p as *mut sgx_ql_qv_supplemental_t as *mut u8,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::sgx_qv_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
supplemental_data_size,
|
||||
p_supplemental_data,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get TDX supplemental data required size.
|
||||
///
|
||||
/// # Return
|
||||
/// Size of the supplemental data in bytes.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_ERROR_QVL_QVE_MISMATCH*
|
||||
/// - *SGX_QL_ENCLAVE_LOAD_ERROR*
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use teepot_tee_quote_verification_rs::*;
|
||||
///
|
||||
/// let data_size = tdx_qv_get_quote_supplemental_data_size().unwrap();
|
||||
///
|
||||
/// assert_eq!(data_size, std::mem::size_of::<sgx_ql_qv_supplemental_t>() as u32);
|
||||
/// ```
|
||||
pub fn tdx_qv_get_quote_supplemental_data_size() -> Result<u32, quote3_error_t> {
|
||||
let mut data_size = 0u32;
|
||||
unsafe {
|
||||
match qvl_sys::tdx_qv_get_quote_supplemental_data_size(&mut data_size) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok(data_size),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform TDX ECDSA quote verification.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// TDX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supplemental_data_size**\
|
||||
/// Size of the supplemental data (in bytes).
|
||||
/// - **supplemental_data**\
|
||||
/// The parameter is optional. If it is None, supplemental_data_size must be 0.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tdx_qv_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supplemental_data_size: u32,
|
||||
supplemental_data: Option<&mut sgx_ql_qv_supplemental_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = match qve_report_info {
|
||||
Some(p) => p,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
let p_supplemental_data = match supplemental_data {
|
||||
Some(p) => p as *mut sgx_ql_qv_supplemental_t as *mut u8,
|
||||
None => std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tdx_qv_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
supplemental_data_size,
|
||||
p_supplemental_data,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the full path of QVE and QPL library.\
|
||||
/// The function takes the enum and the corresponding full path.
|
||||
///
|
||||
/// # Param
|
||||
/// - **path_type**\
|
||||
/// The type of binary being passed in.
|
||||
/// - **path**\
|
||||
/// It should be a valid full path.
|
||||
///
|
||||
/// # Return
|
||||
/// - ***SGX_QL_SUCCESS***\
|
||||
/// Successfully set the full path.
|
||||
/// - ***SGX_QL_ERROR_INVALID_PARAMETER***\
|
||||
/// Path is not a valid full path or the path is too long.
|
||||
///
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn sgx_qv_set_path(path_type: sgx_qv_path_type_t, path: &str) -> quote3_error_t {
|
||||
match std::ffi::CString::new(path) {
|
||||
Ok(path) => unsafe { qvl_sys::sgx_qv_set_path(path_type, path.as_ptr()) },
|
||||
_ => quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Collateral {
|
||||
pub major_version: u16,
|
||||
pub minor_version: u16,
|
||||
pub tee_type: u32,
|
||||
pub pck_crl_issuer_chain: Box<[u8]>,
|
||||
pub root_ca_crl: Box<[u8]>,
|
||||
pub pck_crl: Box<[u8]>,
|
||||
pub tcb_info_issuer_chain: Box<[u8]>,
|
||||
pub tcb_info: Box<[u8]>,
|
||||
pub qe_identity_issuer_chain: Box<[u8]>,
|
||||
pub qe_identity: Box<[u8]>,
|
||||
}
|
||||
|
||||
// referential struct
|
||||
struct SgxQlQveCollateralT<'a> {
|
||||
inner: sgx_ql_qve_collateral_t,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
// create the referential struct
|
||||
impl<'a> From<&'a Collateral> for SgxQlQveCollateralT<'a> {
|
||||
fn from(data: &'a Collateral) -> Self {
|
||||
let mut this = SgxQlQveCollateralT {
|
||||
inner: sgx_ql_qve_collateral_t {
|
||||
__bindgen_anon_1: Default::default(),
|
||||
tee_type: data.tee_type,
|
||||
pck_crl_issuer_chain: data.pck_crl_issuer_chain.as_ptr() as _,
|
||||
pck_crl_issuer_chain_size: data.pck_crl_issuer_chain.len() as _,
|
||||
root_ca_crl: data.root_ca_crl.as_ptr() as _,
|
||||
root_ca_crl_size: data.root_ca_crl.len() as _,
|
||||
pck_crl: data.pck_crl.as_ptr() as _,
|
||||
pck_crl_size: data.pck_crl.len() as _,
|
||||
tcb_info_issuer_chain: data.tcb_info_issuer_chain.as_ptr() as _,
|
||||
tcb_info_issuer_chain_size: data.tcb_info_issuer_chain.len() as _,
|
||||
tcb_info: data.tcb_info.as_ptr() as _,
|
||||
tcb_info_size: data.tcb_info.len() as _,
|
||||
qe_identity_issuer_chain: data.qe_identity_issuer_chain.as_ptr() as _,
|
||||
qe_identity_issuer_chain_size: data.qe_identity_issuer_chain.len() as _,
|
||||
qe_identity: data.qe_identity.as_ptr() as _,
|
||||
qe_identity_size: data.qe_identity.len() as _,
|
||||
},
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
this.inner.__bindgen_anon_1.__bindgen_anon_1.major_version = data.major_version;
|
||||
this.inner.__bindgen_anon_1.__bindgen_anon_1.minor_version = data.minor_version;
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SgxQlQveCollateralT<'_> {
|
||||
type Target = sgx_ql_qve_collateral_t;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Get quote verification collateral.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of quote_collateral.
|
||||
///
|
||||
/// - **quote_collateral**\
|
||||
/// This is the Quote Certification Collateral retrieved based on Quote.
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_PLATFORM_LIB_UNAVAILABLE*
|
||||
/// - *SGX_QL_PCK_CERT_CHAIN_ERROR*
|
||||
/// - *SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_OUT_OF_MEMORY*
|
||||
/// - *SGX_QL_NO_QUOTE_COLLATERAL_DATA*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tee_qv_get_collateral(quote: &[u8]) -> Result<Collateral, quote3_error_t> {
|
||||
fn try_into_collateral(
|
||||
buf: *const sgx_ql_qve_collateral_t,
|
||||
buf_len: u32,
|
||||
) -> Result<Collateral, quote3_error_t> {
|
||||
fn try_into_boxed_slice(
|
||||
p: *mut ::std::os::raw::c_char,
|
||||
size: u32,
|
||||
) -> Result<Box<[u8]>, quote3_error_t> {
|
||||
if p.is_null() || !p.is_aligned() {
|
||||
return Err(quote3_error_t::SGX_QL_ERROR_MAX);
|
||||
}
|
||||
Ok(Box::from(unsafe {
|
||||
slice::from_raw_parts(p as _, size as _)
|
||||
}))
|
||||
}
|
||||
|
||||
if buf.is_null()
|
||||
|| (buf_len as usize) < size_of::<sgx_ql_qve_collateral_t>()
|
||||
|| !buf.is_aligned()
|
||||
{
|
||||
return Err(quote3_error_t::SGX_QL_ERROR_MAX);
|
||||
}
|
||||
|
||||
// SAFETY: buf is not null, buf_len is not zero, and buf is aligned.
|
||||
let collateral = unsafe { *buf };
|
||||
|
||||
Ok(Collateral {
|
||||
major_version: unsafe { collateral.__bindgen_anon_1.__bindgen_anon_1.major_version },
|
||||
minor_version: unsafe { collateral.__bindgen_anon_1.__bindgen_anon_1.minor_version },
|
||||
tee_type: collateral.tee_type,
|
||||
pck_crl_issuer_chain: try_into_boxed_slice(
|
||||
collateral.pck_crl_issuer_chain,
|
||||
collateral.pck_crl_issuer_chain_size,
|
||||
)?,
|
||||
root_ca_crl: try_into_boxed_slice(collateral.root_ca_crl, collateral.root_ca_crl_size)?,
|
||||
pck_crl: try_into_boxed_slice(collateral.pck_crl, collateral.pck_crl_size)?,
|
||||
tcb_info_issuer_chain: try_into_boxed_slice(
|
||||
collateral.tcb_info_issuer_chain,
|
||||
collateral.tcb_info_issuer_chain_size,
|
||||
)?,
|
||||
tcb_info: try_into_boxed_slice(collateral.tcb_info, collateral.tcb_info_size)?,
|
||||
qe_identity_issuer_chain: try_into_boxed_slice(
|
||||
collateral.qe_identity_issuer_chain,
|
||||
collateral.qe_identity_issuer_chain_size,
|
||||
)?,
|
||||
qe_identity: try_into_boxed_slice(collateral.qe_identity, collateral.qe_identity_size)?,
|
||||
})
|
||||
}
|
||||
|
||||
let mut buf = std::ptr::null_mut();
|
||||
let mut buf_len = 0u32;
|
||||
|
||||
match unsafe {
|
||||
qvl_sys::tee_qv_get_collateral(quote.as_ptr(), quote.len() as u32, &mut buf, &mut buf_len)
|
||||
} {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
let collateral = try_into_collateral(buf as _, buf_len);
|
||||
|
||||
match unsafe { tee_qv_free_collateral(buf) } {
|
||||
quote3_error_t::SGX_QL_SUCCESS => collateral,
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get supplemental data latest version and required size, support both SGX and TDX.
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (version, data_size) tuple.
|
||||
///
|
||||
/// - **version**\
|
||||
/// Latest version of the supplemental data.
|
||||
/// - **data_size**\
|
||||
/// The size of the buffer in bytes required to contain all of the supplemental data.
|
||||
///
|
||||
pub fn tee_get_supplemental_data_version_and_size(
|
||||
quote: &[u8],
|
||||
) -> Result<(u32, u32), quote3_error_t> {
|
||||
let mut version = 0u32;
|
||||
let mut data_size = 0u32;
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tee_get_supplemental_data_version_and_size(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
&mut version,
|
||||
&mut data_size,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => Ok((version, data_size)),
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform quote verification for SGX and TDX.\
|
||||
/// This API works the same as the old one, but takes a new parameter to describe the supplemental data (supp_data_descriptor).
|
||||
///
|
||||
/// # Param
|
||||
/// - **quote**\
|
||||
/// SGX/TDX Quote, presented as u8 vector.
|
||||
/// - **quote_collateral**\
|
||||
/// Quote Certification Collateral provided by the caller.
|
||||
/// - **expiration_check_date**\
|
||||
/// This is the date that the QvE will use to determine if any of the inputted collateral have expired.
|
||||
/// - **qve_report_info**\
|
||||
/// This parameter can be used in 2 ways.\
|
||||
/// - If qve_report_info is NOT None, the API will use Intel QvE to perform quote verification, and QvE will generate a report using the target_info in sgx_ql_qe_report_info_t structure.\
|
||||
/// - if qve_report_info is None, the API will use QVL library to perform quote verification, note that the results can not be cryptographically authenticated in this mode.
|
||||
/// - **supp_datal_descriptor**\
|
||||
/// *tee_supp_data_descriptor_t* structure.\
|
||||
/// You can specify the major version of supplemental data by setting supp_datal_descriptor.major_version.\
|
||||
/// If supp_datal_descriptor is None, no supplemental data is returned.\
|
||||
/// If supp_datal_descriptor.major_version == 0, then return the latest version of the *sgx_ql_qv_supplemental_t* structure.\
|
||||
/// If supp_datal_descriptor.major_version <= latest supported version, return the latest minor version associated with that major version.\
|
||||
/// If supp_datal_descriptor.major_version > latest supported version, return an error *SGX_QL_SUPPLEMENTAL_DATA_VERSION_NOT_SUPPORTED*.
|
||||
///
|
||||
/// # Return
|
||||
/// Result type of (collateral_expiration_status, verification_result).
|
||||
///
|
||||
/// Status code of the operation, one of:
|
||||
/// - *SGX_QL_ERROR_INVALID_PARAMETER*
|
||||
/// - *SGX_QL_QUOTE_FORMAT_UNSUPPORTED*
|
||||
/// - *SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED*
|
||||
/// - *SGX_QL_UNABLE_TO_GENERATE_REPORT*
|
||||
/// - *SGX_QL_CRL_UNSUPPORTED_FORMAT*
|
||||
/// - *SGX_QL_ERROR_UNEXPECTED*
|
||||
///
|
||||
pub fn tee_verify_quote(
|
||||
quote: &[u8],
|
||||
quote_collateral: Option<&Collateral>,
|
||||
expiration_check_date: i64,
|
||||
qve_report_info: Option<&mut sgx_ql_qe_report_info_t>,
|
||||
supp_data_descriptor: Option<&mut tee_supp_data_descriptor_t>,
|
||||
) -> Result<(u32, sgx_ql_qv_result_t), quote3_error_t> {
|
||||
let mut collateral_expiration_status = 1u32;
|
||||
let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED;
|
||||
|
||||
let quote_collateral = quote_collateral.map(SgxQlQveCollateralT::from);
|
||||
let p_quote_collateral = quote_collateral.as_deref().map_or(std::ptr::null(), |p| p);
|
||||
|
||||
let p_qve_report_info = qve_report_info.map_or(std::ptr::null_mut(), |p| p);
|
||||
|
||||
let p_supp_data_descriptor = supp_data_descriptor.map_or(std::ptr::null_mut(), |p| p);
|
||||
|
||||
unsafe {
|
||||
match qvl_sys::tee_verify_quote(
|
||||
quote.as_ptr(),
|
||||
quote.len() as u32,
|
||||
p_quote_collateral as _,
|
||||
expiration_check_date,
|
||||
&mut collateral_expiration_status,
|
||||
&mut quote_verification_result,
|
||||
p_qve_report_info,
|
||||
p_supp_data_descriptor,
|
||||
) {
|
||||
quote3_error_t::SGX_QL_SUCCESS => {
|
||||
Ok((collateral_expiration_status, quote_verification_result))
|
||||
}
|
||||
error_code => Err(error_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use os::*;
|
||||
|
|
|
@ -8,14 +8,14 @@ authors.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-http = "3"
|
||||
actix-http.workspace = true
|
||||
actix-web.workspace = true
|
||||
anyhow.workspace = true
|
||||
awc.workspace = true
|
||||
bytes.workspace = true
|
||||
clap.workspace = true
|
||||
const-oid.workspace = true
|
||||
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
||||
futures-core = { workspace = true, features = ["alloc"] }
|
||||
hex.workspace = true
|
||||
pgp.workspace = true
|
||||
rustls.workspace = true
|
||||
|
@ -26,7 +26,7 @@ sha2.workspace = true
|
|||
teepot.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
webpki-roots = "0.26.1"
|
||||
webpki-roots.workspace = true
|
||||
x509-cert.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
134
crates/teepot-vault/README.md
Normal file
134
crates/teepot-vault/README.md
Normal file
|
@ -0,0 +1,134 @@
|
|||
# teepot-vault
|
||||
|
||||
[](https://crates.io/crates/teepot-vault)
|
||||
[](https://docs.rs/teepot-vault)
|
||||
[](LICENSE)
|
||||
|
||||
A TEE (Trusted Execution Environment) secret manager that provides secure storage and retrieval of secrets for TEE applications, with a focus on Intel SGX enclaves.
|
||||
|
||||
## Features
|
||||
|
||||
- **Remote Attestation**: Verify Intel SGX enclaves and other TEEs using attestation reports
|
||||
- **Secure Communication**: Establish TLS connections with custom certificate verification based on TEE attestation
|
||||
- **HashiCorp Vault Integration**: Store and retrieve secrets with TEE-specific access controls
|
||||
- **Multi-signature Support**: PGP-based multi-signature verification for administrative commands
|
||||
- **Configurable TCB Levels**: Support for different Trusted Computing Base security levels
|
||||
|
||||
## Installation
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
teepot-vault = "0.6.0"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating a Vault Connection
|
||||
|
||||
```rust
|
||||
use teepot_vault::client::{AttestationArgs, VaultConnection};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = AttestationArgs {
|
||||
sgx_mrsigner: Some("your_mrsigner_hex".to_string()),
|
||||
sgx_mrenclave: Some("your_mrenclave_hex".to_string()),
|
||||
server: "https://vault.example.com".to_string(),
|
||||
sgx_allowed_tcb_levels: Some(vec!["Ok".to_string(), "ConfigNeeded".to_string()]),
|
||||
};
|
||||
|
||||
let vault_conn = VaultConnection::new(&args, "my-tee-app".to_string()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Storing and Retrieving Secrets
|
||||
|
||||
```rust
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MySecret {
|
||||
api_key: String,
|
||||
private_key: Vec<u8>,
|
||||
}
|
||||
|
||||
// Store a secret
|
||||
let my_secret = MySecret {
|
||||
api_key: "secret-key".to_string(),
|
||||
private_key: vec![1, 2, 3, 4],
|
||||
};
|
||||
vault_conn.store_secret(my_secret, "secrets/my-app/config").await?;
|
||||
|
||||
// Retrieve a secret
|
||||
let secret: MySecret = vault_conn.load_secret("secrets/my-app/config").await?.unwrap();
|
||||
```
|
||||
|
||||
### Custom TEE Connections
|
||||
|
||||
For more control over the connection and custom operations:
|
||||
|
||||
```rust
|
||||
use teepot_vault::client::TeeConnection;
|
||||
|
||||
let tee_conn = TeeConnection::new(&args);
|
||||
let client = tee_conn.client(); // Get the HTTP client for custom requests
|
||||
|
||||
// Perform custom authenticated requests
|
||||
let response = client
|
||||
.get("https://vault.example.com/custom/endpoint")
|
||||
.send()
|
||||
.await?;
|
||||
```
|
||||
|
||||
## Server Components
|
||||
|
||||
The crate also provides server-side utilities for building TEE-aware services:
|
||||
|
||||
```rust
|
||||
use teepot_vault::server::{HttpResponseError, Status};
|
||||
use actix_web::{web, App, HttpServer, Result};
|
||||
|
||||
async fn handler() -> Result<String, HttpResponseError> {
|
||||
// Your TEE service logic here
|
||||
Ok("Secure response".to_string())
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.route("/api/secure", web::get().to(handler))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Rust 1.70 or later
|
||||
- For SGX support: Intel SGX SDK and PSW (Platform Software)
|
||||
- HashiCorp Vault instance (for vault operations)
|
||||
- TEE environment (Intel SGX, Intel TDX, or compatible)
|
||||
|
||||
## Security Considerations
|
||||
|
||||
This crate is designed for use in high-security environments. When using it:
|
||||
|
||||
1. Always verify attestation reports before trusting a TEE
|
||||
2. Use appropriate TCB levels for your security requirements
|
||||
3. Ensure proper key management for PGP signatures
|
||||
4. Follow HashiCorp Vault best practices for secret management
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use pgp::{types::PublicKeyTrait, Deserializable, SignedPublicKey};
|
||||
use pgp::{
|
||||
composed::{Deserializable, SignedPublicKey},
|
||||
types::KeyDetails,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
default::Default,
|
||||
|
|
|
@ -31,7 +31,7 @@ pub use teepot::quote::{
|
|||
tcblevel::{parse_tcb_levels, EnumSet, TcbLevel},
|
||||
verify_quote_with_collateral, QuoteVerificationResult,
|
||||
};
|
||||
use teepot::{quote::Report, sgx::Quote};
|
||||
use teepot::quote::{Quote, Report};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use x509_cert::{
|
||||
der::{Decode as _, Encode as _},
|
||||
|
@ -174,17 +174,17 @@ impl TeeConnection {
|
|||
debug!("Failed to get collateral in certificate");
|
||||
}
|
||||
|
||||
let quote = Quote::try_from_bytes(quote_bytes).map_err(|e| {
|
||||
let quote = Quote::parse(quote_bytes).map_err(|e| {
|
||||
Error::General(format!("Failed get quote in certificate {e:?}"))
|
||||
})?;
|
||||
|
||||
if "e.report_body.reportdata[..32] != hash.as_slice() {
|
||||
if "e.get_report_data()[..32] != hash.as_slice() {
|
||||
error!("Report data mismatch");
|
||||
return Err(Error::General("Report data mismatch".to_string()));
|
||||
} else {
|
||||
info!(
|
||||
"Report data matches `{}`",
|
||||
hex::encode("e.report_body.reportdata[..32])
|
||||
hex::encode("e.get_report_data()[..32])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
// Copyright (c) 2023-2025 Matter Labs
|
||||
|
||||
//! Signature checking utilities
|
||||
|
||||
use crate::json::secrets::AdminConfig;
|
||||
use crate::server::{HttpResponseError, Status as _};
|
||||
use crate::{
|
||||
json::secrets::AdminConfig,
|
||||
server::{HttpResponseError, Status as _},
|
||||
};
|
||||
use actix_web::http::StatusCode;
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use pgp::types::PublicKeyTrait;
|
||||
use pgp::{Deserializable, SignedPublicKey, StandaloneSignature};
|
||||
use pgp::{
|
||||
composed::{Deserializable, SignedPublicKey, StandaloneSignature},
|
||||
types::PublicKeyTrait,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
/// Verify a pgp signature for some message given some public keys
|
||||
|
@ -91,7 +95,7 @@ impl VerifySig for AdminConfig {
|
|||
mod tests {
|
||||
use super::verify_sig;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use pgp::{Deserializable, SignedPublicKey};
|
||||
use pgp::composed::{Deserializable, SignedPublicKey};
|
||||
|
||||
const TEST_DATA: &str = include_str!("../../tests/data/test.json");
|
||||
|
||||
|
|
|
@ -10,24 +10,25 @@ edition.workspace = true
|
|||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
|
||||
tdx-attest-rs = { version = "0.1.2", git = "https://github.com/intel/SGXDataCenterAttestationPrimitives.git", rev = "aa239d25a437a28f3f4de92c38f5b6809faac842" }
|
||||
teepot-tee-quote-verification-rs = { path = "../teepot-tee-quote-verification-rs" }
|
||||
|
||||
[target.'cfg(not(all(target_os = "linux", target_arch = "x86_64")))'.dependencies]
|
||||
dcap-qvl = "0.2.3"
|
||||
chrono = "0.4.40"
|
||||
dcap-qvl.workspace = true
|
||||
chrono.workspace = true
|
||||
bytes.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["quote_op"]
|
||||
quote_op = ["dep:teepot-tee-quote-verification-rs"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
asn1_der.workspace = true
|
||||
async-trait.workspace = true
|
||||
bytemuck.workspace = true
|
||||
clap.workspace = true
|
||||
config.workspace = true
|
||||
const-oid.workspace = true
|
||||
enumset.workspace = true
|
||||
futures = "0.3.31"
|
||||
futures.workspace = true
|
||||
getrandom.workspace = true
|
||||
hex.workspace = true
|
||||
num-integer.workspace = true
|
||||
|
@ -48,6 +49,7 @@ serde_json.workspace = true
|
|||
sha2.workspace = true
|
||||
sha3.workspace = true
|
||||
signature.workspace = true
|
||||
teepot-tee-quote-verification-rs = { workspace = true, optional = true }
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
|
72
crates/teepot/README.md
Normal file
72
crates/teepot/README.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# teepot
|
||||
|
||||
TEE (Trusted Execution Environment) utilities for Intel SGX and TDX attestation.
|
||||
|
||||
## Overview
|
||||
|
||||
Teepot provides comprehensive support for generating and verifying attestation quotes from Intel SGX enclaves and TDX trust domains. It handles the complete attestation workflow including quote generation, collateral fetching, and verification with detailed TCB (Trusted Computing Base) status reporting.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-TEE Support**: Works with both Intel SGX and Intel TDX
|
||||
- **Attestation Quote Generation**: Generate quotes with custom report data
|
||||
- **Quote Verification**: Verify quotes with automatic collateral fetching
|
||||
- **TCB Level Management**: Filter quotes by security level
|
||||
- **Cross-Platform**: Native support for Linux x86_64, fallback implementation for other platforms
|
||||
- **Gramine SGX Support**: Special support for Gramine-based SGX enclaves
|
||||
- **Comprehensive Error Handling**: Detailed error context for debugging
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Quote Generation and Verification
|
||||
|
||||
```rust
|
||||
use teepot::quote::{get_quote, get_collateral, verify_quote_with_collateral};
|
||||
|
||||
// Generate a quote with custom data
|
||||
let report_data = [0u8; 64]; // Your custom data here
|
||||
let quote = get_quote(&report_data)?;
|
||||
|
||||
// Fetch collateral for verification
|
||||
let collateral = get_collateral("e)?;
|
||||
|
||||
// Verify the quote
|
||||
let result = verify_quote_with_collateral("e, collateral.as_ref(), None)?;
|
||||
println!("TCB Level: {:?}", result.tcb_level);
|
||||
```
|
||||
|
||||
### High-Level Attestation API
|
||||
|
||||
```rust
|
||||
use teepot::quote::attestation::get_quote_and_collateral;
|
||||
|
||||
// Generate quote and fetch collateral in one call
|
||||
let (quote, collateral, result) = get_quote_and_collateral(&report_data)?;
|
||||
```
|
||||
|
||||
### TCB Level Filtering
|
||||
|
||||
```rust
|
||||
use teepot::quote::{TcbLevel, verify_quote_with_collateral};
|
||||
|
||||
// Only accept quotes with up-to-date TCB
|
||||
let accepted_levels = vec![TcbLevel::Ok];
|
||||
let result = verify_quote_with_collateral("e, collateral.as_ref(), Some(&accepted_levels))?;
|
||||
```
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- **Full support**: Linux x86_64 with Intel SGX/TDX drivers
|
||||
- **Verification only**: All other platforms via `dcap-qvl`
|
||||
|
||||
## Dependencies
|
||||
|
||||
On Linux x86_64, the crate uses Intel's DCAP libraries for quote generation. Make sure you have:
|
||||
- Intel SGX DCAP Quote Generation Library
|
||||
- Intel SGX DCAP Quote Verification Library
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details.
|
||||
|
||||
Note: Some code is derived from the [Enarx project](https://github.com/enarx/).
|
|
@ -312,13 +312,13 @@ fn init_telemetry(
|
|||
if config.logging.console {
|
||||
// Optionally configure JSON logging
|
||||
if config.logging.json {
|
||||
subscriber.with(fmt_layer.json()).init()
|
||||
subscriber.with(fmt_layer.json()).init();
|
||||
} else {
|
||||
subscriber.with(fmt_layer.pretty()).init()
|
||||
subscriber.with(fmt_layer.pretty()).init();
|
||||
}
|
||||
} else {
|
||||
subscriber.init()
|
||||
};
|
||||
subscriber.init();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ use sha3::{Digest, Keccak256};
|
|||
pub fn recover_signer(sig: &[u8; 65], root_hash: &Message) -> Result<[u8; 20]> {
|
||||
let sig = RecoverableSignature::from_compact(
|
||||
&sig[0..64],
|
||||
RecoveryId::try_from(sig[64] as i32 - 27)?,
|
||||
RecoveryId::try_from(i32::from(sig[64]) - 27)?,
|
||||
)?;
|
||||
let public = SECP256K1.recover_ecdsa(root_hash, &sig)?;
|
||||
let public = SECP256K1.recover_ecdsa(*root_hash, &sig)?;
|
||||
Ok(public_key_to_ethereum_address(&public))
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ mod tests {
|
|||
|
||||
/// Signs the message in Ethereum-compatible format for on-chain verification.
|
||||
fn sign_message(sec: &SecretKey, message: Message) -> Result<[u8; 65]> {
|
||||
let s = SECP256K1.sign_ecdsa_recoverable(&message, sec);
|
||||
let s = SECP256K1.sign_ecdsa_recoverable(message, sec);
|
||||
let (rec_id, data) = s.serialize_compact();
|
||||
|
||||
let mut signature = [0u8; 65];
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
pub mod config;
|
||||
pub mod ethereum;
|
||||
pub mod log;
|
||||
#[cfg(feature = "quote_op")]
|
||||
pub mod pki;
|
||||
pub mod prover;
|
||||
pub mod quote;
|
||||
|
|
|
@ -53,10 +53,7 @@ pub fn setup_logging(
|
|||
.try_from_env()
|
||||
.unwrap_or(match *log_level {
|
||||
LevelFilter::OFF => EnvFilter::new("off"),
|
||||
_ => EnvFilter::new(format!(
|
||||
"warn,{crate_name}={log_level},teepot={log_level}",
|
||||
log_level = log_level
|
||||
)),
|
||||
_ => EnvFilter::new(format!("warn,{crate_name}={log_level},teepot={log_level}")),
|
||||
});
|
||||
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
|
|
|
@ -37,8 +37,7 @@ pub enum ReportData {
|
|||
fn report_data_to_bytes(data: &[u8], version: u8) -> [u8; REPORT_DATA_LENGTH] {
|
||||
debug_assert!(
|
||||
data.len() < REPORT_DATA_LENGTH, // Ensure there is space for the version byte
|
||||
"Data length exceeds maximum of {} bytes",
|
||||
REPORT_DATA_LENGTH
|
||||
"Data length exceeds maximum of {REPORT_DATA_LENGTH} bytes",
|
||||
);
|
||||
let mut bytes = [0u8; REPORT_DATA_LENGTH];
|
||||
bytes[..data.len()].copy_from_slice(data);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
//! Quote Error type
|
||||
|
||||
use std::io;
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
use tdx_attest_rs::tdx_attest_error_t;
|
||||
#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))]
|
||||
use teepot_tee_quote_verification_rs::tdx_attest_rs::tdx_attest_error_t;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Quote parsing error
|
||||
|
@ -22,7 +22,7 @@ pub enum QuoteError {
|
|||
InvalidTeeType,
|
||||
#[error("unsupported body type")]
|
||||
UnsupportedBodyType,
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))]
|
||||
#[error("tdx_att_get_quote error {msg}: {inner:?}")]
|
||||
TdxAttGetQuote {
|
||||
inner: tdx_attest_error_t,
|
||||
|
@ -58,7 +58,7 @@ pub enum QuoteError {
|
|||
CrlUnsupportedFormat(String),
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))]
|
||||
impl From<tdx_attest_error_t> for QuoteError {
|
||||
fn from(code: tdx_attest_error_t) -> Self {
|
||||
Self::TdxAttGetQuote {
|
||||
|
@ -104,11 +104,11 @@ pub trait QuoteContextErr {
|
|||
impl<T, E: std::fmt::Display> QuoteContextErr for Result<T, E> {
|
||||
type Ok = T;
|
||||
fn str_context<I: std::fmt::Display>(self, msg: I) -> Result<T, QuoteError> {
|
||||
self.map_err(|e| QuoteError::Unexpected(format!("{}: {}", msg, e)))
|
||||
self.map_err(|e| QuoteError::Unexpected(format!("{msg}: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))]
|
||||
impl<T> QuoteContext for Result<T, tdx_attest_error_t> {
|
||||
type Ok = T;
|
||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue