Compare commits

...

52 commits

Author SHA1 Message Date
Harald Hoyer
8773078d5a
Merge pull request #328 from matter-labs/renovate/cachix-install-nix-action-31.x
Some checks failed
lint / check-spdx-headers (push) Failing after 35s
lint / taplo (push) Failing after 13s
nix / check (push) Failing after 13s
nix / build (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-self-attestation-test-sgx-azure]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-self-attestation-test-sgx-dcap]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-tdx-test]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-vault-admin-sgx-azure]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-vault-admin]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-vault-sgx-azure]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-vault-unseal-sgx-azure]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-vault-unseal]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-verify-attestation-sgx]) (push) Has been skipped
nix / push_to_docker (map[nixpackage:container-verify-era-proof-attestation-sgx]) (push) Has been skipped
nix-non-x86 / macos-latest (push) Has been cancelled
chore(deps): update cachix/install-nix-action action to v31
2025-07-09 10:44:28 +02:00
renovate[bot]
ce9560cff0
chore(deps): update cachix/install-nix-action action to v31 2025-06-27 15:14:32 +00:00
Harald Hoyer
093d6c44ed
Merge pull request #339 from matter-labs/crates_io_readme
docs: add README files for teepot-related crates
2025-06-27 17:12:38 +02:00
Harald Hoyer
ddbf099e45
docs: add README files for teepot-related crates
- Added comprehensive README files for the following new crates:
  - `teepot`
  - `teepot-tdx-attest-rs`
  - `teepot-tdx-attest-sys`
  - `teepot-tee-quote-verification-rs`
  - `teepot-vault`
- Each includes an overview, usage examples, installation instructions, and licensing details.
2025-06-25 13:55:00 +02:00
Harald Hoyer
18ed1aa769
Merge pull request #338 from matter-labs/bv0.6.0-ver
chore(deps): update `teepot` crates to version 0.6.0
2025-06-25 11:52:31 +02:00
Harald Hoyer
49bb4a3bef
chore(deps): update teepot crates to version 0.6.0
- Set `teepot`, `teepot-tee-quote-verification-rs`, and `teepot-vault` crate versions to 0.6.0 in `Cargo.toml`.
- Ensures consistency with the planned 0.6.0 release preparation.
2025-06-25 09:31:29 +02:00
Harald Hoyer
c34ff7ad27
Merge pull request #336 from matter-labs/bv0.6.0
chore(deps): prepare release 0.6.0
2025-06-24 17:25:09 +02:00
Harald Hoyer
b4e0014e4e
chore(deps): prepare release 0.6.0
- vendor unpublished tdx-attest-rs and tdx-attest-sys crates
  to be able to publish to crates.io
- Updated package versions in `Cargo.toml` and `Cargo.lock` to 0.6.0.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-06-24 16:39:00 +02:00
Harald Hoyer
8d965aa388
Merge pull request #333 from matter-labs/feature/update-dependencies
chore(deps): Update all dependencies to latest
2025-06-23 12:24:19 +02:00
Lucille L. Blumire
412e3b1698
chore(deps): Update all dependencies to latest 2025-06-23 10:09:50 +01:00
Harald Hoyer
f7c3717241
Merge pull request #327 from matter-labs/renovate/docker-login-action-3.x
chore(deps): update docker/login-action action to v3.4.0
2025-06-04 17:20:04 +02:00
renovate[bot]
c42d692863
chore(deps): update docker/login-action action to v3.4.0 2025-06-04 15:07:28 +00:00
Harald Hoyer
626dbbf846
Merge pull request #325 from matter-labs/cargo_update
chore(deps): update crates and nix flakes
2025-06-04 17:05:10 +02:00
Harald Hoyer
8c7922ae39
Merge branch 'main' into cargo_update 2025-06-04 16:02:47 +02:00
Harald Hoyer
716c782e6f
chore(deps): update crates and nix flakes
- Updated multiple Rust dependencies, including `opentelemetry`, `const-oid`, and `webpki-roots` for enhanced features and bug fixes.
- Upgraded `nixpkgs` and `crane` in the nix flake configuration.
- Removed unused dependencies and introduced missing dependencies for improved build integrity.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-30 17:54:30 +02:00
Harald Hoyer
e78fb22f88
Merge pull request #311 from matter-labs/renovate/enarx-spdx-digest
chore(deps): update enarx/spdx digest to d4020ee
2025-05-30 09:13:27 +02:00
renovate[bot]
a7e2939a54
chore(deps): update enarx/spdx digest to d4020ee 2025-05-30 06:43:14 +00:00
Harald Hoyer
37e7f7f8e2
Merge pull request #323 from matter-labs/intel-dcap-api-impr
feat(intel-dcap-api): add automatic retry logic for 429 rate limiting
2025-05-30 08:41:24 +02:00
Harald Hoyer
7c133c4e4b
ci(nix): disable sandbox in nix-non-x86 workflow
otherwise the mockito tests fail, because it cannot bind to 127.0.0.1 0

- Updated `nix build` command to include `--no-sandbox` flag.
2025-05-28 13:31:15 +02:00
Harald Hoyer
bb9c5b195e
feat(intel-dcap-api): add automatic retry logic for 429 rate limiting
- Add `max_retries` field to ApiClient with default of 3 retries
- Implement `execute_with_retry()` helper method in helpers.rs
- Update all HTTP requests to use retry wrapper for automatic 429 handling
- Add `TooManyRequests` error variant with request_id and retry_after fields
- Respect Retry-After header duration before retrying requests
- Add `set_max_retries()` method to configure retry behavior (0 disables)
- Update documentation and add handle_rate_limit example
- Enhanced error handling in check_status() for 429 responses

The client now transparently handles Intel API rate limiting while remaining
configurable for users who need different retry behavior or manual handling.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-28 11:52:32 +02:00
Harald Hoyer
205113ecfa
feat(intel-dcap-api): add comprehensive testing infrastructure and examples
- Add mock tests using real Intel API response data (25 tests)
- Create fetch_test_data tool to retrieve real API responses for testing
- Add integration_test example covering 17 API endpoints
- Add common_usage example demonstrating attestation verification patterns
- Add issuer chain validation checks to ensure signature verification is possible
- Add comprehensive documentation in CLAUDE.md

The test suite now covers all major Intel DCAP API functionality including
TCB info, enclave identities, PCK CRLs, FMSPCs, and evaluation data numbers
for both SGX and TDX platforms across API v3 and v4.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-28 11:52:31 +02:00
renovate[bot]
aeff962224
chore(deps): update rust crate enumset to v1.1.6 (#313) 2025-05-23 14:33:54 +01:00
renovate[bot]
c8692df37a
fix(deps): update rust crate chrono to v0.4.41 (#320) 2025-05-23 14:01:22 +01:00
renovate[bot]
7c655d151c
chore(deps): update rust crate reqwest to v0.12.15 (#315) 2025-05-23 12:13:46 +00:00
renovate[bot]
426e22138e
chore(deps): update rust crate sha2 to v0.10.9 (#318) 2025-05-23 11:40:12 +00:00
renovate[bot]
b16592ec34
chore(deps): update rust crate async-trait to v0.1.88 (#286) 2025-05-23 11:06:13 +00:00
renovate[bot]
119c2abe09
chore(deps): update rust crate bytes to v1.10.1 (#312) 2025-05-23 11:32:13 +01:00
renovate[bot]
5789fdd433
chore(deps): update rust crate getrandom to v0.3.3 (#314) 2025-05-22 16:49:26 +00:00
renovate[bot]
de010fd093
chore(deps): update rust crate thiserror to v2.0.12 (#287) 2025-05-22 16:14:06 +00:00
renovate[bot]
e039adf158
chore(deps): update rust crate tracing-actix-web to v0.7.18 (#280) 2025-05-22 15:40:23 +00:00
renovate[bot]
f2718456ef
chore(deps): update rust crate serde_json to v1.0.140 (#274) 2025-05-22 15:05:49 +00:00
renovate[bot]
bef406c456
chore(deps): update rust crate anyhow to v1.0.98 (#273) 2025-05-22 14:28:51 +00:00
renovate[bot]
bfd895e8f7
chore(deps): update rust crate clap to v4.5.38 (#266)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.30` -> `4.5.38` |

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.38`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4538---2025-05-11)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.37...v4.5.38)

##### Fixes

-   *(help)* When showing aliases, include leading `--` or `-`

###
[`v4.5.37`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4537---2025-04-18)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.36...v4.5.37)

##### Features

-   Added `ArgMatches::try_clear_id()`

###
[`v4.5.36`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4536---2025-04-11)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.35...v4.5.36)

##### Fixes

- *(help)* Revert 4.5.35's "Don't leave space for shorts if there are
none" for now

###
[`v4.5.35`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4535---2025-04-01)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.34...v4.5.35)

##### Fixes

- *(help)* Align positionals and flags when put in the same
`help_heading`
-   *(help)* Don't leave space for shorts if there are none

###
[`v4.5.34`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4534---2025-03-27)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.33...v4.5.34)

##### Fixes

- *(help)* Don't add extra blank lines with `flatten_help(true)` and
subcommands without arguments

###
[`v4.5.33`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4533---2025-03-26)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.32...v4.5.33)

##### Fixes

- *(error)* When showing the usage of a suggestion for an unknown
argument, don't show the group

###
[`v4.5.32`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4532---2025-03-10)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.31...v4.5.32)

##### Features

-   Add `Error::remove`

##### Documentation

-   *(cookbook)* Switch from `humantime` to `jiff`
-   *(tutorial)* Better cover required vs optional

##### Internal

-   Update `pulldown-cmark`

###
[`v4.5.31`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4531---2025-02-24)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.30...v4.5.31)

##### Features

-   Add `ValueParserFactory` for `Saturating<T>`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/matter-labs/teepot).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNjQuMSIsInVwZGF0ZWRJblZlciI6IjQwLjE2LjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 14:52:46 +01:00
Harald Hoyer
8b01d8d5b0
Merge pull request #267 from matter-labs/renovate/trufflesecurity-trufflehog-3.x
chore(deps): update trufflesecurity/trufflehog action to v3.88.30
2025-05-22 09:08:54 +02:00
renovate[bot]
ad26c5e9ae
chore(deps): update trufflesecurity/trufflehog action to v3.88.30 2025-05-16 21:21:53 +00:00
Harald Hoyer
336576d812
Merge pull request #310 from matter-labs/add-dcap-collateral-updater
feat(teepot): add `Quote::tee_type` method for TEE type determination
2025-05-06 13:46:58 +02:00
Harald Hoyer
6379e9aa9e
feat(teepot): add Quote::tee_type method for TEE type determination
- Introduced `tee_type` method to extract TEE type from the quote header.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-06 13:18:17 +02:00
Harald Hoyer
1536e00d63
Merge pull request #309 from matter-labs/platform
feat: add platform-specific implementations for quote verification
2025-05-06 13:08:45 +02:00
Harald Hoyer
2a8614c08f
feat: add platform-specific implementations for quote verification
- Introduced conditional compilation for Intel SGX/TDX quote verification based on target OS and architecture.
- Moved Intel-specific logic to a separate module and added a fallback for unsupported platforms.

This is done, so we can pull in the `teepot` crate even on `linux-x86_64`
without the Intel SGX SDK lib dependency.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-06 12:36:01 +02:00
Harald Hoyer
905487dac8
Merge pull request #307 from matter-labs/fmspc
feat(quote): add FMSPC and CPUSVN extraction support
2025-05-06 12:31:15 +02:00
Harald Hoyer
2bbfb2415c
feat(quote): add FMSPC and CPUSVN extraction support
- Introduced new types `Fmspc`, `CpuSvn`, and `Svn` for SGX metadata.
- Added methods to extract raw certificate chains and FMSPC from SGX quotes.
- Created new test file for validating FMSPC extraction with example quotes.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2025-05-06 11:43:51 +02:00
Harald Hoyer
fca60adc1a
Merge pull request #306 from matter-labs/rm_dupl
refactor: replace custom Quote parsing with library version
2025-05-06 11:11:08 +02:00
Harald Hoyer
2118466a8a
refactor: replace custom Quote parsing with library version
- Removed custom `Quote` structure and parsing logic in `teepot/src/sgx/mod.rs`.
- Updated references to use the library-provided `Quote` methods, such as `Quote::parse` and `get_report_data`.
- Simplified code and reduced redundancy by leveraging existing library functionality.
2025-05-05 14:54:41 +02:00
Lucille Blumire
9bd0e9c36e
Merge pull request #305 from matter-labs/small-quality
refactor: many small code quality improvements
2025-04-17 17:43:22 +01:00
Lucille L. Blumire
d54f7b14ad
refactor: remove redundant continue 2025-04-17 16:53:01 +01:00
Lucille L. Blumire
2ca0b47169
refactor: improve punctuation readability 2025-04-17 16:52:59 +01:00
Lucille L. Blumire
6a9e035d19
refactor: combine equivalent match branches 2025-04-17 16:52:59 +01:00
Lucille L. Blumire
36afc85d38
refactor: prefer if let to single variant match 2025-04-17 16:52:57 +01:00
Lucille L. Blumire
2ff169da9f
refactor: improve type ergonomics 2025-04-17 16:52:56 +01:00
Lucille L. Blumire
0768b0ad67
refactor: prefer conversion methods to infallable casts 2025-04-17 16:52:54 +01:00
Lucille L. Blumire
2dea589c0e
refactor: prefer inline format args 2025-04-17 16:52:53 +01:00
Lucille L. Blumire
71a04ad4e2
refactor: bring items to top level of files 2025-04-17 16:52:49 +01:00
116 changed files with 10093 additions and 1943 deletions

View file

@ -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

View file

@ -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

View file

@ -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 }}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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"

View file

@ -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
```

View file

@ -1,5 +1,6 @@
[package]
name = "rtmr-calc"
publish = false
version.workspace = true
edition.workspace = true
authors.workspace = true

View file

@ -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 &section_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;

View file

@ -1,5 +1,6 @@
[package]
name = "sha384-extend"
publish = false
version.workspace = true
edition.workspace = true
authors.workspace = true

View file

@ -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()))

View file

@ -6,6 +6,7 @@ authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
publish = false
[dependencies]
anyhow.workspace = true

View file

@ -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);

View file

@ -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(())
}

View file

@ -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!("{:#}", &quote.report);
}

View file

@ -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

View file

@ -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}"))
})
}
}

View file

@ -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))
}
}

View file

@ -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"),

View file

@ -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(),
}
}

View file

@ -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}")))
}
}
},

View file

@ -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;
}
}
}

View file

@ -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));

View file

@ -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 {

View file

@ -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));

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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!");
}
}

View file

@ -24,7 +24,7 @@ impl PolicyEnforcer {
match &quote.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 &quote_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 &quote_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 &quote_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));
}

View file

@ -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;
};

View 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

View file

@ -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"]

View 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(())
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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].

View 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].

View file

@ -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?;

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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(

View file

@ -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?;

View file

@ -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 {

View file

@ -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};

View 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");
}

View 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"),
}
}

View 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\"}]"
}

View 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\"}]"
}

View 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\"}]"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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

View file

@ -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"
}

View file

@ -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\"}"
}

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
View 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"

View 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" }

View 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.

View file

@ -0,0 +1,149 @@
# teepot-tdx-attest-rs
[![Crates.io](https://img.shields.io/crates/v/teepot-tdx-attest-rs.svg)](https://crates.io/crates/teepot-tdx-attest-rs)
[![Documentation](https://docs.rs/teepot-tdx-attest-rs/badge.svg)](https://docs.rs/teepot-tdx-attest-rs)
[![License](https://img.shields.io/crates/l/teepot-tdx-attest-rs.svg)](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).

View 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
View 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"

View 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"

View 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.

View file

@ -0,0 +1,46 @@
# teepot-tdx-attest-sys
[![Crates.io](https://img.shields.io/crates/v/teepot-tdx-attest-sys.svg)](https://crates.io/crates/teepot-tdx-attest-sys)
[![Documentation](https://docs.rs/teepot-tdx-attest-sys/badge.svg)](https://docs.rs/teepot-tdx-attest-sys)
[![License](https://img.shields.io/crates/l/teepot-tdx-attest-sys.svg)](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.

View 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"

View 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!");
}

View 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"));

View 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"

View file

@ -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" }

View file

@ -0,0 +1,182 @@
# teepot-tee-quote-verification-rs
[![Crates.io](https://img.shields.io/crates/v/teepot-tee-quote-verification-rs.svg)](https://crates.io/crates/teepot-tee-quote-verification-rs)
[![Documentation](https://docs.rs/teepot-tee-quote-verification-rs/badge.svg)](https://docs.rs/teepot-tee-quote-verification-rs)
[![License](https://img.shields.io/crates/l/teepot-tee-quote-verification-rs.svg)](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

View file

@ -0,0 +1,3 @@
// SPDX-License-Identifier: BSD-3-Clause
pub const NOTHING_TO_SEE_HERE: u8 = 0;

View 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),
}
}
}

View file

@ -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::*;

View file

@ -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]

View file

@ -0,0 +1,134 @@
# teepot-vault
[![Crates.io](https://img.shields.io/crates/v/teepot-vault.svg)](https://crates.io/crates/teepot-vault)
[![Documentation](https://docs.rs/teepot-vault/badge.svg)](https://docs.rs/teepot-vault)
[![License](https://img.shields.io/crates/l/teepot-vault.svg)](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.

View file

@ -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,

View file

@ -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 &quote.report_body.reportdata[..32] != hash.as_slice() {
if &quote.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(&quote.report_body.reportdata[..32])
hex::encode(&quote.get_report_data()[..32])
);
}

View file

@ -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");

View file

@ -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
View 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(&quote)?;
// Verify the quote
let result = verify_quote_with_collateral(&quote, 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(&quote, 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/).

View file

@ -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(())
}

View file

@ -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];

View file

@ -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;

View file

@ -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()

View file

@ -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);

View file

@ -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