diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e4a5757..35718cb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,15 +15,18 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - uses: enarx/spdx@master + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: enarx/spdx@d4020ee98e3101dd487c5184f27c6a6fb4f88709 with: - licenses: Apache-2.0 BSD-3-Clause MIT + licenses: |- + Apache-2.0 + BSD-3-Clause + MIT taplo: name: taplo runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - uses: cachix/install-nix-action@v27 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: cachix/install-nix-action@v31 - run: nix run nixpkgs#taplo -- fmt --check diff --git a/.github/workflows/nix-non-x86.yml b/.github/workflows/nix-non-x86.yml new file mode 100644 index 0000000..b80cf6a --- /dev/null +++ b/.github/workflows/nix-non-x86.yml @@ -0,0 +1,38 @@ +name: nix-non-x86 + +permissions: + contents: read + pull-requests: read + +on: + pull_request: + branches: ["main"] + push: + branches: ["main"] + tags: ["*"] + +jobs: + macos-latest: + runs-on: macos-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + access-tokens = github.com=${{ github.token }} + trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= tee-pot:SS6HcrpG87S1M6HZGPsfo7d1xJccCGev7/tXc5+I4jg= + substituters = https://cache.nixos.org/ https://attic.teepot.org/tee-pot + sandbox = true + - name: Setup Attic cache + uses: ryanccn/attic-action@v0 + with: + endpoint: https://attic.teepot.org/ + cache: tee-pot + token: ${{ secrets.ATTIC_TOKEN }} + + - name: nixci + # 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 --no-sandbox + diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 7300c28..3d850ea 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -2,10 +2,10 @@ name: nix on: pull_request: - branches: [ "main" ] + branches: ["main"] push: - branches: [ "main" ] - tags: [ "*-sgx-*" ] + branches: ["main"] + tags: ["*"] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -15,9 +15,10 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - uses: cachix/install-nix-action@v27 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - 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= @@ -29,18 +30,17 @@ jobs: endpoint: https://attic.teepot.org/ cache: tee-pot token: ${{ secrets.ATTIC_TOKEN }} - - name: Enable magic Nix cache - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix flake check -L --show-trace --keep-going build: needs: check - runs-on: [ matterlabs-default-infra-runners ] + runs-on: [matterlabs-default-infra-runners] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - uses: cachix/install-nix-action@v27 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - 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= @@ -52,15 +52,13 @@ jobs: endpoint: https://attic.teepot.org/ cache: tee-pot token: ${{ secrets.ATTIC_TOKEN }} - - name: Enable magic Nix cache - uses: DeterminateSystems/magic-nix-cache-action@main - name: nix build run: nix run github:nixos/nixpkgs/nixos-23.11#nixci push_to_docker: needs: build - runs-on: [ matterlabs-default-infra-runners ] + runs-on: [matterlabs-default-infra-runners] concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.config.nixpackage }} cancel-in-progress: true @@ -77,10 +75,12 @@ jobs: - { nixpackage: 'container-self-attestation-test-sgx-azure' } - { nixpackage: 'container-verify-attestation-sgx' } - { nixpackage: 'container-verify-era-proof-attestation-sgx' } + - { nixpackage: 'container-tdx-test' } steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v27 + - 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= @@ -92,14 +92,13 @@ jobs: endpoint: https://attic.teepot.org/ cache: tee-pot token: ${{ secrets.ATTIC_TOKEN }} - - name: Enable magic Nix cache - uses: DeterminateSystems/magic-nix-cache-action@main - - name: Log in to Docker Hub - uses: docker/login-action@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Load container id: build @@ -111,15 +110,21 @@ jobs: - name: Push container run: | - echo "Pushing image ${{ steps.build.outputs.IMAGE_TAG }} to Docker Hub" - docker tag "${{ steps.build.outputs.IMAGE_TAG }}" matterlabsrobot/"${{ steps.build.outputs.IMAGE_TAG }}" - docker push matterlabsrobot/"${{ steps.build.outputs.IMAGE_TAG }}" + echo "Pushing image ${{ steps.build.outputs.IMAGE_TAG }} to GitHub Container Registry" + docker tag "${{ steps.build.outputs.IMAGE_TAG }}" "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_TAG }}" + docker push "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_TAG }}" - name: Tag container as latest if: ${{ github.event_name == 'push' }} run: | - docker tag "${{ steps.build.outputs.IMAGE_TAG }}" matterlabsrobot/"${{ steps.build.outputs.IMAGE_NAME }}:latest" - docker push matterlabsrobot/"${{ steps.build.outputs.IMAGE_NAME }}:latest" + docker tag "${{ steps.build.outputs.IMAGE_TAG }}" "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_NAME }}:latest" + docker push "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_NAME }}:latest" + + - name: Tag container with tag + if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }} + run: | + docker tag "${{ steps.build.outputs.IMAGE_TAG }}" "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_NAME }}:$GITHUB_REF_NAME" + docker push "ghcr.io/${{ github.repository_owner }}"/"${{ steps.build.outputs.IMAGE_NAME }}:$GITHUB_REF_NAME" - name: Generate build ID for Flux Image Automation id: flux diff --git a/.github/workflows/secrets_scanner.yaml b/.github/workflows/secrets_scanner.yaml index 814a625..dfeadc8 100644 --- a/.github/workflows/secrets_scanner.yaml +++ b/.github/workflows/secrets_scanner.yaml @@ -5,11 +5,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: fetch-depth: 0 - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@06bbd6fd493fcac4a6db0e4850a92bcf932fafed # v3.81.10 + uses: trufflesecurity/trufflehog@c8921694a53d95ce424af6ae76dbebf3b6a83aef # v3.88.30 with: path: ./ base: ${{ github.event.repository.default_branch }} diff --git a/Cargo.lock b/Cargo.lock index 7ed6d55..df7f24f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "actix-codec" @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytes", "futures-core", "futures-sink", @@ -21,24 +21,24 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-tls", "actix-utils", - "ahash", - "base64 0.22.1", - "bitflags 2.6.0", + "base64", + "bitflags 2.9.1", "brotli", "bytes", "bytestring", - "derive_more 0.99.18", + "derive_more 2.0.1", "encoding_rs", "flate2", + "foldhash", "futures-core", "h2 0.3.26", "http 0.2.12", @@ -50,7 +50,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.9.1", "sha1", "smallvec", "tokio", @@ -66,7 +66,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" dependencies = [ "actix-rt", "actix-service", @@ -113,12 +113,11 @@ dependencies = [ [[package]] name = "actix-service" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" dependencies = [ "futures-core", - "paste", "pin-project-lite", ] @@ -133,15 +132,15 @@ dependencies = [ "actix-utils", "futures-core", "http 0.2.12", - "http 1.1.0", + "http 1.3.1", "impl-more", "pin-project-lite", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls", "tokio-util", "tracing", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -156,9 +155,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.9.0" +version = "4.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" dependencies = [ "actix-codec", "actix-http", @@ -170,13 +169,13 @@ dependencies = [ "actix-tls", "actix-utils", "actix-web-codegen", - "ahash", "bytes", "bytestring", "cfg-if", "cookie", - "derive_more 0.99.18", + "derive_more 2.0.1", "encoding_rs", + "foldhash", "futures-core", "futures-util", "impl-more", @@ -194,6 +193,7 @@ dependencies = [ "smallvec", "socket2", "time", + "tracing", "url", ] @@ -206,23 +206,23 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -230,6 +230,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ + "bytes", "crypto-common", "generic-array", ] @@ -260,16 +261,12 @@ dependencies = [ ] [[package]] -name = "ahash" -version = "0.8.11" +name = "aes-kw" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", + "aes", ] [[package]] @@ -313,15 +310,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "argon2" @@ -333,8 +330,15 @@ dependencies = [ "blake2", "cpufeatures", "password-hash", + "zeroize", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayvec" version = "0.7.6" @@ -342,14 +346,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "async-trait" -version = "0.1.82" +name = "asn1_der" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -360,15 +380,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "awc" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79049b2461279b886e46f1107efc347ebecc7b88d74d023dda010551a124967b" +checksum = "e76d68b4f02400c2f9110437f254873e8f265b35ea87352f142bc7c8e878115a" dependencies = [ "actix-codec", "actix-http", @@ -376,11 +396,11 @@ dependencies = [ "actix-service", "actix-tls", "actix-utils", - "base64 0.22.1", + "base64", "bytes", "cfg-if", "cookie", - "derive_more 0.99.18", + "derive_more 2.0.1", "futures-core", "futures-util", "h2 0.3.26", @@ -390,8 +410,8 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", - "rustls 0.22.4", + "rand 0.9.1", + "rustls", "serde", "serde_json", "serde_urlencoded", @@ -400,81 +420,32 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.9.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" dependencies = [ "aws-lc-sys", - "mirai-annotations", - "paste", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.21.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", "fs_extra", - "libc", - "paste", -] - -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 0.1.2", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", ] [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -485,24 +456,12 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -511,29 +470,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - -[[package]] -name = "bigdecimal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bindgen" @@ -552,19 +491,19 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.104", "which", ] [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.12.1", @@ -575,23 +514,68 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.104", "which", ] [[package]] -name = "bit-vec" -version = "0.6.3" +name = "bindgen" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.104", +] [[package]] -name = "bitfield" +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + +[[package]] +name = "bitfields" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d84268bbf9b487d31fe4b849edbefcd3911422d7a07de855a2da1f70ab3d1c" +dependencies = [ + "bitfields-impl", +] + +[[package]] +name = "bitfields-impl" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c93edde7bb4416c35c85048e34f78999dcb47d199bde3b1d79286156f3e2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "thiserror 2.0.12", +] [[package]] name = "bitflags" @@ -601,9 +585,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitvec" @@ -656,9 +640,9 @@ dependencies = [ [[package]] name = "brotli" -version = "6.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -667,24 +651,14 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] -[[package]] -name = "bstr" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "buffer-redux" version = "1.0.2" @@ -696,34 +670,34 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -734,15 +708,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytestring" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ "bytes", ] @@ -768,9 +742,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.19" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -803,9 +777,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -815,9 +789,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -825,7 +799,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -851,9 +825,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -861,9 +835,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstyle", "clap_lex", @@ -872,21 +846,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cmac" @@ -901,13 +875,34 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] +[[package]] +name = "cms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" +dependencies = [ + "const-oid", + "der", + "spki", + "x509-cert", +] + +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "combine" version = "4.6.7" @@ -924,6 +919,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bed69047ed42e52c7e38d6421eeb8ceefb4f2a2b52eed59137f7bad7908f6800" +[[package]] +name = "config" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595aae20e65c3be792d05818e8c63025294ac3cb7e200f11459063a352a6ef80" +dependencies = [ + "async-trait", + "pathdiff", + "serde", + "serde_json", + "toml", + "winnow", + "yaml-rust2", +] + +[[package]] +name = "const-decoder" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381abde2cdc1bc3817e394b24e05667a2dc89f37570cbd34d9c397d99e56e3f" +dependencies = [ + "compile-fmt", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -931,10 +950,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "convert_case" -version = "0.4.0" +name = "const_format" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] [[package]] name = "cookie" @@ -957,6 +990,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -965,13 +1008,28 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc24" version = "0.1.6" @@ -987,38 +1045,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" @@ -1027,7 +1058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1039,10 +1070,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.104", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1052,16 +1093,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "ctrlc" -version = "3.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" -dependencies = [ - "nix", - "windows-sys 0.59.0", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1086,7 +1117,24 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", +] + +[[package]] +name = "cx448" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c0cf476284b03eb6c10e78787b21c7abb7d7d43cb2f02532ba6b831ed892fa" +dependencies = [ + "crypto-bigint", + "elliptic-curve", + "pkcs8", + "rand_core 0.6.4", + "serdect 0.3.0", + "sha3", + "signature", + "subtle", + "zeroize", ] [[package]] @@ -1101,12 +1149,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -1125,16 +1173,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -1150,15 +1198,21 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.10", + "darling_core 0.20.11", "quote", - "syn 2.0.77", + "syn 2.0.104", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + [[package]] name = "dbl" version = "0.3.2" @@ -1169,35 +1223,45 @@ dependencies = [ ] [[package]] -name = "debugid" -version = "0.8.0" +name = "dcap-qvl" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "7c8049b28b1fe35e94bd52f4c20edf7b80b6eeb378faccdd685d5cd82cbc192e" dependencies = [ + "anyhow", + "asn1_der", + "base64", + "byteorder", + "chrono", "const-oid", - "zeroize", + "der", + "futures", + "hex", + "log", + "parity-scale-codec", + "pem", + "reqwest", + "ring", + "rustls-webpki 0.102.8", + "scale-info", + "serde", + "serde-human-bytes", + "serde_json", + "tracing", + "urlencoding", + "x509-cert", ] [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -1209,14 +1273,14 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -1224,66 +1288,73 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.77", + "derive_more-impl 1.0.0", ] [[package]] name = "derive_more" -version = "1.0.0-beta.6" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7abbfc297053be59290e3152f8cbcd52c8642e0728b69ee187d991d4c1af08d" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ - "derive_more-impl", + "derive_more-impl 2.0.1", ] [[package]] name = "derive_more-impl" -version = "1.0.0-beta.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", "unicode-xid", ] @@ -1308,6 +1379,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "dsa" version = "0.6.3" @@ -1317,18 +1399,18 @@ dependencies = [ "digest", "num-bigint-dig", "num-traits", - "pkcs8 0.10.2", - "rfc6979 0.4.0", + "pkcs8", + "rfc6979", "sha2", - "signature 2.2.0", + "signature", "zeroize", ] [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dunce" @@ -1336,6 +1418,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "eax" version = "0.5.0" @@ -1349,30 +1437,18 @@ dependencies = [ "subtle", ] -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der", "digest", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.2.0", - "spki 0.7.3", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -1381,8 +1457,8 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8 0.10.2", - "signature 2.2.0", + "pkcs8", + "signature", ] [[package]] @@ -1393,6 +1469,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", + "rand_core 0.6.4", "serde", "sha2", "subtle", @@ -1401,29 +1478,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core", - "sec1 0.3.0", - "subtle", - "zeroize", -] +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -1431,44 +1488,60 @@ version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", + "base16ct", + "base64ct", + "crypto-bigint", "digest", - "ff 0.13.0", + "ff", "generic-array", - "group 0.13.0", + "group", "hkdf", - "pem-rfc7468", - "pkcs8 0.10.2", - "rand_core", - "sec1 0.7.3", + "pem-rfc7468 0.7.0", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serde_json", + "serdect 0.2.0", "subtle", + "tap", "zeroize", ] [[package]] name = "elsa" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98e71ae4df57d214182a2e5cb90230c0192c6ddfcaa05c36453d46a54713e10" +checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e" dependencies = [ "stable_deref_trait", ] [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "enumset" -version = "1.1.5" +name = "enum-as-inner" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "enumset" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" dependencies = [ "enumset_derive", "serde", @@ -1476,14 +1549,14 @@ dependencies = [ [[package]] name = "enumset_derive" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -1497,18 +1570,18 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1524,7 +1597,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror", + "thiserror 1.0.69", "uint", ] @@ -1557,27 +1630,18 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", + "bitvec", + "rand_core 0.6.4", "subtle", ] @@ -1587,18 +1651,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "findshlibs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" -dependencies = [ - "cc", - "lazy_static", - "libc", - "winapi", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -1606,30 +1658,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flagset" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.0.33" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -1639,6 +1686,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1677,9 +1730,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1692,9 +1745,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1702,15 +1755,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1719,32 +1772,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -1758,9 +1811,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1787,13 +1840,29 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1808,32 +1877,32 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gloo-net" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils", - "http 0.2.12", + "http 1.3.1", "js-sys", "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1865,14 +1934,15 @@ dependencies = [ ] [[package]] -name = "group" -version = "0.12.1" +name = "gpt" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "3696fafb1ecdcc2ae3ce337de73e9202806068594b77d22fdf2f3573c5ec2219" dependencies = [ - "ff 0.12.1", - "rand_core", - "subtle", + "bitflags 2.9.1", + "crc", + "simple-bytes", + "uuid", ] [[package]] @@ -1881,8 +1951,8 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", - "rand_core", + "ff", + "rand_core 0.6.4", "subtle", ] @@ -1898,7 +1968,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.5.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1907,17 +1977,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.5.0", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1932,15 +2002,21 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] [[package]] -name = "heck" -version = "0.4.1" +name = "hashlink" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.4", +] [[package]] name = "heck" @@ -1948,18 +2024,66 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hickory-proto" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.8.5", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand 0.8.5", + "resolv-conf", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -1980,22 +2104,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -2011,26 +2124,15 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -2038,27 +2140,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "futures-core", + "http 1.3.1", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -2068,43 +2170,20 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.30" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", + "h2 0.4.10", + "http 1.3.1", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "httparse", - "itoa", - "pin-project-lite", "smallvec", "tokio", "want", @@ -2112,45 +2191,33 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.3.1", + "hyper", "hyper-util", "log", - "rustls 0.23.13", + "rustls", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tower-service", + "webpki-roots 1.0.1", ] [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 0.14.30", + "hyper", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.30", - "native-tls", - "tokio", - "tokio-native-tls", + "tower-service", ] [[package]] @@ -2161,7 +2228,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2171,34 +2238,41 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", + "http 1.3.1", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", - "tower", "tower-service", "tracing", + "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -2212,6 +2286,92 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "idea" version = "0.5.1" @@ -2229,12 +2389,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2248,9 +2419,9 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "impl-rlp" @@ -2272,13 +2443,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.104", ] [[package]] @@ -2294,24 +2465,41 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.4", "serde", ] [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] +[[package]] +name = "intel-dcap-api" +version = "0.6.0" +dependencies = [ + "base64", + "hex", + "mockito", + "percent-encoding", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "url", + "x509-cert", +] + [[package]] name = "intel-tee-quote-verification-sys" version = "0.2.1" @@ -2322,24 +2510,31 @@ dependencies = [ ] [[package]] -name = "ipnet" -version = "2.10.0" +name = "ipconfig" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" - -[[package]] -name = "iter-read" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "either", + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", ] [[package]] @@ -2352,23 +2547,34 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.11" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jni" -version = "0.19.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -2379,33 +2585,35 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonrpsee" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b089779ad7f80768693755a031cc14a7766aba707cbe886674e3f79e9b7e47" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-http-client", "jsonrpsee-proc-macros", - "jsonrpsee-types", + "jsonrpsee-types 0.24.9", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tracing", @@ -2413,24 +2621,24 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08163edd8bcc466c33d79e10f695cdc98c00d1e6ddfb95cec41b6b0279dd5432" +checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" dependencies = [ - "base64 0.22.1", + "base64", "futures-channel", "futures-util", "gloo-net", - "http 1.1.0", + "http 1.3.1", "jsonrpsee-core", "pin-project", - "rustls 0.23.13", + "rustls", "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror", + "thiserror 1.0.69", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tokio-util", "tracing", "url", @@ -2438,25 +2646,23 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79712302e737d23ca0daa178e752c9334846b08321d439fd89af9a384f8c830b" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" dependencies = [ - "anyhow", "async-trait", - "beef", "bytes", "futures-timer", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http 1.3.1", + "http-body", "http-body-util", - "jsonrpsee-types", + "jsonrpsee-types 0.24.9", "pin-project", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -2465,103 +2671,102 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d90064e04fb9d7282b1c71044ea94d0bbc6eff5621c66f1a0bce9e9de7cf3ac" +checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" dependencies = [ "async-trait", - "base64 0.22.1", - "http-body 1.0.1", - "hyper 1.4.1", + "base64", + "http-body", + "hyper", "hyper-rustls", "hyper-util", "jsonrpsee-core", - "jsonrpsee-types", - "rustls 0.23.13", + "jsonrpsee-types 0.24.9", + "rustls", "rustls-platform-verifier", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" dependencies = [ - "heck 0.5.0", - "proc-macro-crate 3.2.0", + "heck", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "jsonrpsee-types" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" dependencies = [ - "beef", - "http 1.1.0", + "http 1.3.1", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df7256371c45621b3b7d2fb23aea923d577616b9c0e9c0b950a6ea5c2be0ca" +dependencies = [ + "http 1.3.1", + "serde", + "serde_json", + "thiserror 2.0.12", ] [[package]] name = "jsonrpsee-wasm-client" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4727ac037f834c6f04c0912cada7532dbddb54e92fbc64e33d6cb8c24af313c9" +checksum = "e6558a9586cad43019dafd0b6311d0938f46efc116b34b28c74778bc11a2edf6" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-types 0.24.9", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.23.2" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c28759775f5cb2f1ea9667672d3fe2b0e701d1f4b7b67954e60afe7fd058b5e" +checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" dependencies = [ - "http 1.1.0", + "http 1.3.1", "jsonrpsee-client-transport", "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-types 0.24.9", "url", ] [[package]] name = "k256" -version = "0.11.6" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2", -] - -[[package]] -name = "k256" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" -dependencies = [ - "cfg-if", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "ecdsa", + "elliptic-curve", "once_cell", "sha2", - "signature 2.2.0", + "signature", ] [[package]] @@ -2596,51 +2801,58 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.2", ] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] -name = "linkme" -version = "0.3.28" +name = "libz-rs-sys" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c943daedff228392b791b33bba32e75737756e80a613e32e246c6ce9cbab20a" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" dependencies = [ - "linkme-impl", + "zlib-rs", ] [[package]] -name = "linkme-impl" -version = "0.3.28" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "local-channel" @@ -2661,9 +2873,9 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2671,47 +2883,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "logos" -version = "0.13.0" +name = "lru-cache" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "logos-derive", + "linked-hash-map", ] [[package]] -name = "logos-codegen" -version = "0.13.0" +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 2.0.77", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" @@ -2722,12 +2911,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "md-5" version = "0.10.6" @@ -2740,32 +2923,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "miette" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" -dependencies = [ - "miette-derive", - "once_cell", - "thiserror", - "unicode-width", -] - -[[package]] -name = "miette-derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -2781,49 +2941,60 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ - "hermit-abi", "libc", "log", - "wasi", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] -name = "mirai-annotations" -version = "1.12.0" +name = "mockito" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.9.1", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] [[package]] name = "mutually_exclusive_features" -version = "0.0.3" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d02c0b00610773bb7fc61d85e13d86c7858cbdf00e1a120bfc41bc055dbaa0e" +checksum = "e94e1e6445d314f972ff7395df2de295fe51b71821694f0b0e1e79c4f12c8577" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -2831,23 +3002,11 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -2868,31 +3027,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "serde", -] - [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -2905,22 +3039,12 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "serde", "smallvec", "zeroize", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", - "serde", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -2947,18 +3071,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2971,51 +3083,31 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ - "num_enum_derive 0.6.1", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive 0.7.3", + "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -3034,9 +3126,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" @@ -3046,11 +3138,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -3067,20 +3159,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -3090,137 +3182,100 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.20.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" +checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6" dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68f63eca5fad47e570e00e893094fc17be959c80c79a7d6ec1abdd5ae6ffc16" +dependencies = [ + "log", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", ] [[package]] name = "opentelemetry-http" -version = "0.9.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7594ec0e11d8e33faf03530a4c49af7064ebba81c1480e01be67d90b356508b" +checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d" dependencies = [ "async-trait", "bytes", - "http 0.2.12", - "opentelemetry_api", - "reqwest 0.11.27", + "http 1.3.1", + "opentelemetry", + "reqwest", ] [[package]] name = "opentelemetry-otlp" -version = "0.13.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5e5a5c4135864099f3faafbe939eb4d7f9b80ebf68a8448da961b32a7c1275" +checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b" dependencies = [ - "async-trait", - "futures-core", - "http 0.2.12", + "http 1.3.1", + "opentelemetry", "opentelemetry-http", "opentelemetry-proto", - "opentelemetry-semantic-conventions", - "opentelemetry_api", "opentelemetry_sdk", - "prost 0.11.9", - "reqwest 0.11.27", - "thiserror", + "prost", + "reqwest", + "thiserror 2.0.12", "tokio", "tonic", + "tracing", ] [[package]] name = "opentelemetry-proto" -version = "0.3.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" +checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc" dependencies = [ - "opentelemetry_api", + "opentelemetry", "opentelemetry_sdk", - "prost 0.11.9", + "prost", "tonic", ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.12.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" -dependencies = [ - "opentelemetry", -] - -[[package]] -name = "opentelemetry_api" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" -dependencies = [ - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] +checksum = "83d059a296a47436748557a353c5e6c5705b9470ef6c95cfc52c21a8814ddac2" [[package]] name = "opentelemetry_sdk" -version = "0.20.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" +checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b" dependencies = [ - "async-trait", - "crossbeam-channel", "futures-channel", "futures-executor", "futures-util", - "once_cell", - "opentelemetry_api", - "ordered-float 3.9.2", + "opentelemetry", "percent-encoding", - "rand", - "regex", + "rand 0.9.1", "serde_json", - "thiserror", + "thiserror 2.0.12", "tokio", "tokio-stream", ] -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "3.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" -dependencies = [ - "num-traits", -] - -[[package]] -name = "os_info" -version = "3.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - [[package]] name = "overload" version = "0.1.1" @@ -3233,20 +3288,20 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "ecdsa", + "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "ecdsa", + "elliptic-curve", "primeorder", "sha2", ] @@ -3257,45 +3312,47 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ - "base16ct 0.2.0", - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", + "base16ct", + "ecdsa", + "elliptic-curve", "primeorder", - "rand_core", + "rand_core 0.6.4", "sha2", ] [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", + "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.104", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -3303,9 +3360,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -3321,15 +3378,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] -name = "paste" -version = "1.0.15" +name = "pathdiff" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pe-sign" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c04c052a5cf901a229d69fb8804b04c8017c143712529c6e8277aac243fc2989" +dependencies = [ + "chrono", + "cms", + "der", + "digest", + "num-traits", + "pem-rfc7468 1.0.0-rc.3", + "reqwest", + "rsa", + "sha1", + "sha2", + "x509-cert", +] [[package]] name = "peeking_take_while" @@ -3337,6 +3413,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -3346,38 +3432,39 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e58fab693c712c0d4e88f8eb3087b6521d060bcaf76aeb20cb192d809115ba" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.5.0", -] - [[package]] name = "pgp" -version = "0.13.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6c842436d5fa2b59eac1e9b3d142b50bfff99c1744c816b1f4c2ac55a20754" +checksum = "f91d320242d9b686612b15526fe38711afdf856e112eaa4775ce25b0d9b12b11" dependencies = [ + "aead", "aes", "aes-gcm", + "aes-kw", "argon2", - "base64 0.22.1", - "bitfield", + "base64", + "bitfields", "block-padding", "blowfish", - "bstr", "buffer-redux", "byteorder", + "bytes", "camellia", "cast5", "cfb-mode", @@ -3386,41 +3473,43 @@ dependencies = [ "const-oid", "crc24", "curve25519-dalek", + "cx448", "derive_builder", + "derive_more 2.0.1", "des", "digest", "dsa", "eax", - "ecdsa 0.16.9", + "ecdsa", "ed25519-dalek", - "elliptic-curve 0.13.8", + "elliptic-curve", "flate2", "generic-array", "hex", "hkdf", "idea", - "iter-read", - "k256 0.13.3", + "k256", "log", "md-5", "nom", "num-bigint-dig", "num-traits", - "num_enum 0.7.3", + "num_enum", "ocb3", "p256", "p384", "p521", - "rand", + "rand 0.8.5", + "regex", "ripemd", "rsa", "sha1", "sha1-checked", "sha2", "sha3", - "signature 2.2.0", + "signature", "smallvec", - "thiserror", + "snafu", "twofish", "x25519-dalek", "zeroize", @@ -3428,29 +3517,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3464,19 +3553,9 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.9", - "pkcs8 0.10.2", - "spki 0.7.3", -] - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", + "der", + "pkcs8", + "spki", ] [[package]] @@ -3485,15 +3564,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", - "spki 0.7.3", + "der", + "spki", ] [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polyval" @@ -3507,6 +3586,15 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3515,21 +3603,21 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -3538,7 +3626,7 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve 0.13.8", + "elliptic-curve", ] [[package]] @@ -3556,37 +3644,27 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit 0.22.20", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus-client" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c" dependencies = [ "dtoa", "itoa", @@ -3602,146 +3680,102 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "prost" -version = "0.11.9" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - -[[package]] -name = "prost-build" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" -dependencies = [ - "bytes", - "heck 0.5.0", - "itertools 0.12.1", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.12.6", - "prost-types", - "regex", - "syn 2.0.77", - "tempfile", + "prost-derive", ] [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.104", ] [[package]] -name = "prost-derive" -version = "0.12.6" +name = "quinn" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "prost-reflect" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" -dependencies = [ - "base64 0.21.7", - "logos", - "miette", - "once_cell", - "prost 0.12.6", - "prost-types", - "serde", - "serde-value", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost 0.12.6", -] - -[[package]] -name = "protox" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bb76c5f6221de491fe2c8f39b106330bbd9762c6511119c07940e10eb9ff11" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", - "miette", - "prost 0.12.6", - "prost-reflect", - "prost-types", - "protox-parse", - "thiserror", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", ] [[package]] -name = "protox-parse" -version = "0.5.0" +name = "quinn-proto" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4581f441c58863525a3e6bec7b8de98188cf75239a56c725a3e7288450a33f" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ - "logos", - "miette", - "prost-types", - "thiserror", + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", ] [[package]] -name = "quick-protobuf" -version = "0.8.1" +name = "quinn-udp" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ - "byteorder", + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -3755,8 +3789,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -3766,7 +3810,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -3775,28 +3829,57 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -3810,13 +3893,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -3833,71 +3916,31 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "reqwest" -version = "0.12.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" -dependencies = [ - "base64 0.22.1", + "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", + "h2 0.4.10", + "hickory-resolver", + "http 1.3.1", + "http-body", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-rustls", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", @@ -3905,32 +3948,31 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.3", + "quinn", + "rustls", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration 0.6.1", + "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls", + "tower 0.5.2", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", + "webpki-roots 1.0.1", ] [[package]] -name = "rfc6979" -version = "0.3.1" +name = "resolv-conf" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] +checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "rfc6979" @@ -3944,15 +3986,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -3978,9 +4019,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest", @@ -3988,20 +4029,34 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8 0.10.2", - "rand_core", + "pkcs8", + "rand_core 0.6.4", "sha2", - "signature 2.2.0", - "spki 0.7.3", + "signature", + "spki", "subtle", "zeroize", ] +[[package]] +name = "rtmr-calc" +version = "0.6.0" +dependencies = [ + "anyhow", + "clap", + "gpt", + "hex", + "pe-sign", + "sha2", + "teepot", + "tracing", +] + [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -4009,6 +4064,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -4026,104 +4087,87 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] -name = "rustls" -version = "0.22.4" +name = "rustix" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", + "security-framework 3.2.0", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] [[package]] name = "rustls-platform-verifier" -version = "0.3.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "jni", "log", "once_cell", - "rustls 0.23.13", + "rustls", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", + "rustls-webpki 0.103.3", + "security-framework 3.2.0", "security-framework-sys", - "webpki-roots", - "winapi", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", ] [[package]] @@ -4137,6 +4181,17 @@ name = "rustls-webpki" version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "aws-lc-rs", "ring", @@ -4146,15 +4201,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -4166,44 +4221,68 @@ dependencies = [ ] [[package]] -name = "schannel" -version = "0.1.24" +name = "scale-info" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more 1.0.0", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct 0.2.0", - "der 0.7.9", + "base16ct", + "der", "generic-array", - "pkcs8 0.10.2", + "pkcs8", + "serdect 0.2.0", "subtle", "zeroize", ] @@ -4219,12 +4298,13 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.1" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "6a3dff2d01c9aa65c3186a45ff846bfea52cbe6de3b6320ed2a358d90dad0d76" dependencies = [ - "rand", - "secp256k1-sys 0.10.1", + "bitcoin_hashes", + "rand 0.9.1", + "secp256k1-sys 0.11.0", ] [[package]] @@ -4238,18 +4318,18 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" dependencies = [ "cc", ] [[package]] name = "secrecy" -version = "0.8.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" dependencies = [ "zeroize", ] @@ -4260,19 +4340,31 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.9.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", - "num-bigint", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -4280,9 +4372,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "send_wrapper" @@ -4290,156 +4382,58 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" -[[package]] -name = "sentry" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce4b57f1b521f674df7a1d200be8ff5d74e3712020ee25b553146657b5377d5" -dependencies = [ - "httpdate", - "native-tls", - "reqwest 0.11.27", - "sentry-backtrace", - "sentry-contexts", - "sentry-core", - "sentry-debug-images", - "sentry-panic", - "sentry-tracing", - "tokio", - "ureq", -] - -[[package]] -name = "sentry-backtrace" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cc8d4e04a73de8f718dc703943666d03f25d3e9e4d0fb271ca0b8c76dfa00e" -dependencies = [ - "backtrace", - "once_cell", - "regex", - "sentry-core", -] - -[[package]] -name = "sentry-contexts" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6436c1bad22cdeb02179ea8ef116ffc217797c028927def303bc593d9320c0d1" -dependencies = [ - "hostname", - "libc", - "os_info", - "rustc_version", - "sentry-core", - "uname", -] - -[[package]] -name = "sentry-core" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901f761681f97db3db836ef9e094acdd8756c40215326c194201941947164ef1" -dependencies = [ - "once_cell", - "rand", - "sentry-types", - "serde", - "serde_json", -] - -[[package]] -name = "sentry-debug-images" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdb263e73d22f39946f6022ed455b7561b22ff5553aca9be3c6a047fa39c328" -dependencies = [ - "findshlibs", - "once_cell", - "sentry-core", -] - -[[package]] -name = "sentry-panic" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fbf1c163f8b6a9d05912e1b272afa27c652e8b47ea60cb9a57ad5e481eea99" -dependencies = [ - "sentry-backtrace", - "sentry-core", -] - -[[package]] -name = "sentry-tracing" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82eabcab0a047040befd44599a1da73d3adb228ff53b5ed9795ae04535577704" -dependencies = [ - "sentry-backtrace", - "sentry-core", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sentry-types" -version = "0.31.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da956cca56e0101998c8688bc65ce1a96f00673a0e58e663664023d4c7911e82" -dependencies = [ - "debugid", - "hex", - "rand", - "serde", - "serde_json", - "thiserror", - "time", - "url", - "uuid", -] - [[package]] name = "serde" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] -name = "serde-value" -version = "0.7.0" +name = "serde-human-bytes" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +checksum = "78ef65cb41f3f9cef63c431193229067e8b98b53c4d4c4ed38a8ca87c4d07676" dependencies = [ - "ordered-float 2.10.1", + "hex", "serde", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ + "indexmap 2.9.0", "itoa", "memchr", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4458,25 +4452,27 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ + "hex", "serde", "serde_with_macros 1.5.2", ] [[package]] name = "serde_with" -version = "3.9.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ - "base64 0.22.1", + "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.9.0", + "schemars", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.9.0", + "serde_with_macros 3.13.0", "time", ] @@ -4494,14 +4490,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -4510,13 +4506,33 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", "itoa", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "serdect" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4536,24 +4552,14 @@ checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ "digest", "sha1", + "zeroize", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2_ce" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca2daa77078f4ddff27e75c4bf59e4c2697525f56dbb3c842d34a5d1f2b04a2" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -4571,13 +4577,14 @@ dependencies = [ ] [[package]] -name = "sha3_ce" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c9a08202c50378d8a07a5f458193a5f542d2828ac6640263dbc0c2533ea25e" +name = "sha384-extend" +version = "0.6.0" dependencies = [ - "digest", - "keccak", + "anyhow", + "clap", + "hex", + "sha2", + "teepot", ] [[package]] @@ -4597,23 +4604,13 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest", - "rand_core", -] - [[package]] name = "signature" version = "2.2.0" @@ -4621,29 +4618,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "simple-bytes" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11532d9d241904f095185f35dcdaf930b1427a94d5b01d7002d74ba19b44cc4" + [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "snafu" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4651,16 +4678,16 @@ dependencies = [ [[package]] name = "soketto" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", ] @@ -4670,16 +4697,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - [[package]] name = "spki" version = "0.7.3" @@ -4687,7 +4704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der", ] [[package]] @@ -4716,24 +4733,24 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.24.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", - "syn 1.0.109", + "syn 2.0.104", ] [[package]] @@ -4755,9 +4772,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -4766,28 +4783,22 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -4796,19 +4807,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "bitflags 2.9.1", + "core-foundation 0.9.4", + "system-configuration-sys", ] [[package]] @@ -4828,63 +4829,86 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "tee-key-preexec" -version = "0.3.0" +name = "tdx-extend" +version = "0.6.0" dependencies = [ "anyhow", "clap", - "rand", - "secp256k1 0.29.1", + "hex", "teepot", "tracing", - "tracing-log 0.2.0", +] + +[[package]] +name = "tdx-test" +version = "0.6.0" +dependencies = [ + "anyhow", + "serde", + "teepot", + "thiserror 2.0.12", + "tokio", + "tracing", +] + +[[package]] +name = "tee-key-preexec" +version = "0.6.0" +dependencies = [ + "anyhow", + "clap", + "secp256k1 0.31.0", + "teepot", + "tracing", + "tracing-log", "tracing-subscriber", ] [[package]] name = "tee-ratls-preexec" -version = "0.3.0" +version = "0.6.0" dependencies = [ "anyhow", "clap", "rsa", "teepot", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", "x509-cert", ] [[package]] name = "tee-self-attestation-test" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", - "base64 0.22.1", + "base64", "teepot", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] [[package]] name = "tee-stress-client" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", "clap", "serde", "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] [[package]] name = "tee-vault-admin" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", @@ -4892,130 +4916,185 @@ dependencies = [ "bytemuck", "clap", "hex", - "rustls 0.22.4", + "rustls", "serde_json", "sha2", "teepot", + "teepot-vault", "tracing", "tracing-actix-web", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] [[package]] name = "tee-vault-unseal" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", "awc", "clap", - "rustls 0.22.4", + "rustls", "serde_json", "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] [[package]] name = "teepot" -version = "0.3.0" +version = "0.6.0" dependencies = [ - "actix-http", - "actix-web", "anyhow", - "awc", - "base64 0.22.1", + "asn1_der", + "async-trait", + "base64", "bytemuck", "bytes", + "chrono", "clap", + "config", "const-oid", + "dcap-qvl", "enumset", - "futures-core", - "getrandom", + "futures", + "getrandom 0.3.3", "hex", "num-integer", "num-traits", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", "p256", - "pgp", - "pkcs8 0.10.2", - "rand", + "pkcs8", + "reqwest", "rsa", - "rustls 0.22.4", + "rustls", + "secp256k1 0.31.0", "serde", "serde_json", - "serde_with 3.9.0", "sha2", - "signature 2.2.0", + "sha3", + "signature", "teepot-tee-quote-verification-rs", "testaso", - "thiserror", + "thiserror 2.0.12", + "tokio", "tracing", - "webpki-roots", + "tracing-futures", + "tracing-log", + "tracing-subscriber", + "tracing-test", "x509-cert", "zeroize", ] [[package]] name = "teepot-read" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", "awc", "clap", "serde_json", - "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] +[[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.3.0" +version = "0.6.0" dependencies = [ "intel-tee-quote-verification-sys", "serde", + "teepot-tdx-attest-rs", +] + +[[package]] +name = "teepot-vault" +version = "0.6.0" +dependencies = [ + "actix-http", + "actix-web", + "anyhow", + "awc", + "base64", + "bytes", + "clap", + "const-oid", + "futures-core", + "hex", + "pgp", + "rustls", + "serde", + "serde_json", + "serde_with 3.13.0", + "sha2", + "teepot", + "thiserror 2.0.12", + "tracing", + "webpki-roots 1.0.1", + "x509-cert", ] [[package]] name = "teepot-write" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", "awc", "clap", "serde_json", - "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] [[package]] name = "tempfile" -version = "3.12.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.3", "once_cell", - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix", - "windows-sys 0.48.0", + "rustix 1.0.7", + "windows-sys 0.59.0", ] [[package]] @@ -5026,39 +5105,58 @@ checksum = "63b4d2149a2f578665ca39f8115084635847e9dd6921b5442dcafc7f87bb8e99" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -5071,15 +5169,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -5095,10 +5193,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -5111,9 +5219,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tls_codec" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" dependencies = [ "tls_codec_derive", "zeroize", @@ -5121,20 +5229,20 @@ dependencies = [ [[package]] name = "tls_codec_derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "tokio" -version = "1.40.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", @@ -5148,25 +5256,15 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -5181,31 +5279,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls 0.23.13", - "rustls-pki-types", + "rustls", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -5214,9 +5300,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -5227,56 +5313,60 @@ dependencies = [ ] [[package]] -name = "toml_datetime" -version = "0.6.8" +name = "toml" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] [[package]] -name = "toml_edit" -version = "0.19.15" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ - "indexmap 2.5.0", - "toml_datetime", - "winnow 0.5.40", + "serde", ] [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.9.0", + "serde", + "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] name = "tonic" -version = "0.9.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" dependencies = [ "async-trait", - "axum", - "base64 0.21.7", + "base64", "bytes", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", - "prost 0.11.9", + "prost", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -5290,11 +5380,25 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 2.9.0", + "pin-project-lite", "slab", + "sync_wrapper", "tokio", "tokio-util", "tower-layer", @@ -5302,6 +5406,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-util", + "http 1.3.1", + "http-body", + "iri-string", + "pin-project-lite", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -5316,9 +5438,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -5328,9 +5450,9 @@ dependencies = [ [[package]] name = "tracing-actix-web" -version = "0.7.12" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284586dc201db407be8c9d721abad1b3a6dacbbce5cccecd4fd15a37db95ab0d" +checksum = "2340b7722695166c7fc9b3e3cd1166e7c74fedb9075b8f0c74d3822d2e41caf5" dependencies = [ "actix-web", "mutually_exclusive_features", @@ -5341,34 +5463,33 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", ] [[package]] -name = "tracing-log" -version = "0.1.4" +name = "tracing-futures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "log", - "once_cell", - "tracing-core", + "pin-project", + "tracing", ] [[package]] @@ -5382,27 +5503,11 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" -dependencies = [ - "once_cell", - "opentelemetry", - "opentelemetry_sdk", - "smallvec", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-subscriber", -] - [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -5410,9 +5515,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -5423,13 +5528,33 @@ dependencies = [ "sharded-slab", "smallvec", "thread_local", - "time", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", "tracing-serde", ] +[[package]] +name = "tracing-test" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +dependencies = [ + "quote", + "syn 2.0.104", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -5447,9 +5572,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uint" @@ -5463,47 +5588,17 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -5527,24 +5622,11 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" -dependencies = [ - "base64 0.22.1", - "log", - "native-tls", - "once_cell", - "url", -] - [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -5559,24 +5641,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] -name = "uuid" -version = "1.10.0" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom", - "serde", + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vault-admin" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", @@ -5586,23 +5675,22 @@ dependencies = [ "pgp", "serde_json", "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", - "tracing-subscriber", ] [[package]] name = "vault-unseal" -version = "0.3.0" +version = "0.6.0" dependencies = [ "actix-web", "anyhow", - "base64 0.22.1", + "base64", "clap", "serde_json", - "teepot", + "teepot-vault", "tracing", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", ] @@ -5614,32 +5702,33 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "verify-attestation" -version = "0.3.0" +version = "0.6.0" dependencies = [ "anyhow", "clap", - "hex", - "secp256k1 0.29.1", "teepot", - "zksync_basic_types", ] [[package]] name = "verify-era-proof-attestation" -version = "0.3.0" +version = "0.6.0" dependencies = [ - "anyhow", + "bytes", "clap", - "ctrlc", + "enumset", "hex", - "jsonrpsee-types", - "reqwest 0.12.7", - "secp256k1 0.29.1", + "jsonrpsee-types 0.25.1", + "reqwest", + "secp256k1 0.31.0", "serde", + "serde_json", + "serde_with 3.13.0", + "serde_yaml", "teepot", + "thiserror 2.0.12", "tokio", + "tokio-util", "tracing", - "tracing-log 0.2.0", "tracing-subscriber", "url", "zksync_basic_types", @@ -5655,40 +5744,27 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vise" -version = "0.1.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229baafe01d5177b63c6ee1def80d8e39a2365e64caf69ddb05a57594b15647c" +checksum = "cec485349f926890c1d90a27e8f3147b552bf16cfd80212cd5ff72e8645056fd" dependencies = [ "compile-fmt", + "ctor", "elsa", - "linkme", "once_cell", "prometheus-client", "vise-macros", ] -[[package]] -name = "vise-exporter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23981b18d697026f5430249ab01ba739ef2edc463e400042394331cb2bb63494" -dependencies = [ - "hyper 0.14.30", - "once_cell", - "tokio", - "tracing", - "vise", -] - [[package]] name = "vise-macros" -version = "0.1.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb19c33cd5f04dcf4e767635e058a998edbc2b7fca32ade0a4a1cea0f8e9b34" +checksum = "fe466916e9bf50cc69aba41affc11970885e9dd2b9e448e20e1fb8fc726132ed" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] @@ -5712,53 +5788,63 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5766,38 +5852,78 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "webpki-roots" -version = "0.26.5" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.1", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.1", +] + +[[package]] +name = "webpki-roots" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" dependencies = [ "rustls-pki-types", ] @@ -5811,9 +5937,15 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] +[[package]] +name = "widestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" + [[package]] name = "winapi" version = "0.3.9" @@ -5847,41 +5979,81 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-registry" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +dependencies = [ + "windows-link", "windows-result", "windows-strings", - "windows-targets 0.52.6", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -5911,6 +6083,30 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -5935,13 +6131,35 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5954,6 +6172,18 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -5966,6 +6196,18 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -5978,12 +6220,30 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -5996,6 +6256,18 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6008,6 +6280,18 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6020,6 +6304,18 @@ 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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6033,19 +6329,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.5.40" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.6.18" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -6060,6 +6353,21 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "wyz" version = "0.5.1" @@ -6076,7 +6384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "serde", "zeroize", ] @@ -6088,32 +6396,87 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", - "der 0.7.9", + "der", "sha1", - "signature 2.2.0", - "spki 0.7.3", + "signature", + "spki", "tls_codec", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "yaml-rust2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", ] [[package]] @@ -6134,301 +6497,163 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.104", ] [[package]] -name = "zk_evm" -version = "0.133.0" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af08e9284686a1b0c89ec4931eb915ac0729367f1247abd06164874fe738106" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ - "anyhow", - "lazy_static", - "num", - "serde", - "serde_json", - "static_assertions", - "zk_evm_abstractions", - "zkevm_opcode_defs", + "displaydoc", + "yoke", + "zerofrom", ] [[package]] -name = "zk_evm_abstractions" -version = "0.140.0" +name = "zerovec" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be696258861eba4e6625a5665084b2266720bb67f4ba69819469700ac5c6a401" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ - "anyhow", - "num_enum 0.6.1", - "serde", - "static_assertions", - "zkevm_opcode_defs", + "yoke", + "zerofrom", + "zerovec-derive", ] [[package]] -name = "zkevm_opcode_defs" -version = "0.132.0" +name = "zerovec-derive" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0769f7b27d8fb06e715da3290c575cac5d04d10a557faef180e847afce50ac4" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ - "bitflags 2.6.0", - "blake2", - "ethereum-types", - "k256 0.11.6", - "lazy_static", - "sha2_ce", - "sha3_ce", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] name = "zksync_basic_types" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4e4a41a89d72763b860245ea3a323ac0f6b7b9289a07b481f47168f2973171" +checksum = "1fd013c73df5c418a482e66d7bc260af7217170d2537c7c204c96516eb4ebf15" dependencies = [ "anyhow", "chrono", + "const-decoder", "ethabi", "hex", - "num_enum 0.7.3", + "num_enum", + "secrecy", "serde", "serde_json", "serde_with 1.14.0", + "sha2", "strum", - "thiserror", + "thiserror 2.0.12", "tiny-keccak", "url", ] -[[package]] -name = "zksync_concurrency" -version = "0.1.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1af85d9a31c534a29877c88474cf5f1c46ad25f7c48efff61ea40f4aa83c5459" -dependencies = [ - "anyhow", - "once_cell", - "pin-project", - "rand", - "sha3", - "thiserror", - "time", - "tokio", - "tracing", - "tracing-subscriber", - "vise", -] - -[[package]] -name = "zksync_config" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "663261a7c6064c6d52eafa254057b0d96493280ef28f5fb14efe90faa73aff25" -dependencies = [ - "anyhow", - "rand", - "secrecy", - "serde", - "url", - "zksync_basic_types", - "zksync_concurrency", - "zksync_consensus_utils", - "zksync_crypto_primitives", -] - -[[package]] -name = "zksync_consensus_utils" -version = "0.1.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587de103f745d0b88b49a9fb98cb002c4b7ce6ad042e17845091dce67b8aa984" -dependencies = [ - "anyhow", - "rand", - "thiserror", - "zksync_concurrency", -] - [[package]] name = "zksync_contracts" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "facdc0d63d452d1a7992e0dae773d6f73e4c2a5fda29c2b5119b8ee2be71d98a" +checksum = "2e37376b4a06b82af7459ab7c0a4981c65bbd2c8e1646deb524988b59b0d901d" dependencies = [ "envy", - "ethabi", "hex", "once_cell", "serde", "serde_json", + "zksync_basic_types", "zksync_utils", ] [[package]] name = "zksync_crypto_primitives" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cf6a098fa3e479285f8bad8d4f9ef4852bbd13e2d5f7c8e652ce6c50899453" +checksum = "edc75467647cb0b9b18818f49c60a9ad83d638db88cd244be3fda78dcd8f5b5c" dependencies = [ "anyhow", "blake2", "hex", - "rand", + "rand 0.8.5", "secp256k1 0.27.0", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 2.0.12", "zksync_basic_types", - "zksync_utils", ] [[package]] name = "zksync_mini_merkle_tree" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b34e4e287bc40bf7f6c3ef9bb075401755614b27c64bdf67ac243658ee607b" +checksum = "7cb18556e92bf099bdf2275ec78e1023456d542e435142d420ff234d4735be35" dependencies = [ "once_cell", "zksync_basic_types", "zksync_crypto_primitives", ] -[[package]] -name = "zksync_protobuf" -version = "0.1.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86baa84d8bbbbeea269c0f99aca88364e4fd2a08e6ae7051ff87317132b4ef9" -dependencies = [ - "anyhow", - "bit-vec", - "once_cell", - "prost 0.12.6", - "prost-reflect", - "quick-protobuf", - "rand", - "serde", - "serde_json", - "serde_yaml", - "zksync_concurrency", - "zksync_consensus_utils", - "zksync_protobuf_build", -] - -[[package]] -name = "zksync_protobuf_build" -version = "0.1.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f221ce83f4622c3d8732d09f4461d116d7b10f1cc9d1d1cd014c1fa836c168e6" -dependencies = [ - "anyhow", - "heck 0.5.0", - "prettyplease", - "proc-macro2", - "prost-build", - "prost-reflect", - "protox", - "quote", - "syn 2.0.77", -] - [[package]] name = "zksync_system_constants" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b9f2537f81bddfb67af6e6ae3f1df216e552e89170edd0ed8aa0e0e58fb9d" +checksum = "5196ee6615e7b6c4f29d12c25d7f5dbd1ceb646d25cc245aeb34b0fc8285d950" dependencies = [ "once_cell", "zksync_basic_types", - "zksync_utils", ] [[package]] name = "zksync_types" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463b6b22f788347b746b276b1f4420173e5570d533d5fd10a5d1e9ca1d01c1f3" +checksum = "75a5383dacdc4ce427c937073d028300c006442a9c6d91bdb3a686cf4f155800" dependencies = [ "anyhow", - "bigdecimal", + "async-trait", "blake2", "chrono", - "derive_more 1.0.0-beta.6", + "derive_more 2.0.1", "hex", - "itertools 0.10.5", - "num", - "num_enum 0.7.3", + "num_enum", "once_cell", - "prost 0.12.6", "rlp", - "secp256k1 0.27.0", "serde", "serde_json", - "strum", - "thiserror", + "serde_with 1.14.0", + "thiserror 2.0.12", "tracing", "zksync_basic_types", - "zksync_config", "zksync_contracts", "zksync_crypto_primitives", "zksync_mini_merkle_tree", - "zksync_protobuf", - "zksync_protobuf_build", "zksync_system_constants", - "zksync_utils", ] [[package]] name = "zksync_utils" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2046c95fbcca2789cdfd133a4111197739b45504d887f9ff17e4e8706bcaabaf" +checksum = "246b8013f18bb8721b7d9f9f1d56cb5ff7863ee2f54cdbc22a0e41ff158f1208" dependencies = [ "anyhow", - "bigdecimal", - "futures", - "hex", - "itertools 0.10.5", - "num", "once_cell", - "reqwest 0.12.7", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "zk_evm", - "zksync_basic_types", - "zksync_vlog", -] - -[[package]] -name = "zksync_vlog" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff02e5df2e986592b916077f210b28a35e63d947936f99431041ad79289306e8" -dependencies = [ - "anyhow", - "chrono", - "opentelemetry", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "sentry", - "serde", "serde_json", "tokio", - "tracing", - "tracing-opentelemetry", - "tracing-subscriber", - "vise", - "vise-exporter", ] [[package]] name = "zksync_web3_decl" -version = "0.1.0" +version = "28.6.0-non-semver-compat" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a6342270ad3220b287b01675875c450c365e4a4495c84a2f2376e7f18f973f" +checksum = "58417285e72f11ad2e421fef394445dca54e77f8221faa26f1d993bc004c21e2" dependencies = [ "anyhow", "async-trait", @@ -6436,40 +6661,45 @@ dependencies = [ "jsonrpsee", "pin-project-lite", "rlp", - "rustls 0.23.13", + "rustls", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", "vise", - "zksync_config", "zksync_types", ] [[package]] -name = "zstd" -version = "0.13.2" +name = "zlib-rs" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 7064332..a49e38b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,19 @@ [workspace] -members = ["crates/*", "bin/*"] +members = ["crates/*", "bin/*", "crates/teepot-vault/bin/*"] 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 "] # rest of the workspace, if not specified in the package section @@ -17,56 +24,68 @@ homepage = "https://github.com/matter-labs/teepot" [workspace.dependencies] actix-http = "3" -actix-tls = "3" -actix-web = { version = "4.5", features = ["rustls-0_22"] } +actix-web = { version = "4.5", features = ["rustls-0_23"] } anyhow = "1.0.82" -awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] } +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" -bitflags = "2.5" 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 } -const-oid = { version = "0.9", default-features = false } -ctrlc = "3.4" -der = "0.7.9" +config = { version = "0.15.8", default-features = false, features = ["yaml", "json", "toml", "async"] } +const-oid = { version = "0.9.6", default-features = false } +dcap-qvl = "0.2.3" enumset = { version = "1.1", features = ["serde"] } -futures-core = { version = "0.3.30", features = ["alloc"], default-features = false } -getrandom = "0.2.14" +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-tee-quote-verification-rs = { package = "teepot-tee-quote-verification-rs", path = "crates/teepot-tee-quote-verification-rs", version = "0.3.0" } -intel-tee-quote-verification-sys = { version = "0.2.1" } -jsonrpsee-types = { version = "0.23", default-features = false } -log = "0.4" +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.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" -pgp = "0.13" +pe-sign = "0.1.10" +percent-encoding = "2.3.1" +pgp = { version = "0.16", default-features = false } pkcs8 = { version = "0.10" } -rand = "0.8" reqwest = { version = "0.12", features = ["json"] } -ring = { version = "0.17.8", features = ["std"], default-features = false } rsa = { version = "0.9.6", features = ["sha2", "pem"] } -rustls = { version = "0.22" } -rustls-pemfile = "2" -sec1 = { version = "0.7.3", features = ["der"], default-features = false } -secp256k1 = { version = "0.29", features = ["rand-std", "global-context"] } +rustls = { version = "0.23.20", default-features = false, features = ["std", "logging", "tls12", "ring"] } +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" -teepot = { path = "crates/teepot" } +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 = "1.0.59" -tokio = { version = "1", features = ["sync", "macros", "rt-multi-thread", "fs", "time"] } +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"] } tracing-log = "0.2" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } +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 = "0.26.1" -x509-cert = { version = "0.2", features = ["builder", "signature"] } +webpki-roots = "1.0.0" +x509-cert = { version = "0.2", features = ["builder", "signature", "default"] } zeroize = { version = "1.7.0", features = ["serde"] } -zksync_basic_types = "=0.1.0" -zksync_types = "=0.1.0" -zksync_web3_decl = "=0.1.0" +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" diff --git a/README.md b/README.md index b1b5319..1608503 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,37 @@ # teepot -Key Value store in a TEE with Remote Attestation for Authentication - -## Introduction - -This project is a key-value store that runs in a Trusted Execution Environment (TEE) and uses Remote Attestation for -Authentication. -The key-value store is implemented using Hashicorp Vault running in an Intel SGX enclave via the Gramine runtime. - ## Parts of this project -- `teepot`: The main rust crate that abstracts TEEs and key-value stores. -- `tee-vault-unseal`: An enclave that uses the Vault API to unseal a vault as a proxy. -- `vault-unseal`: A client utility, that talks to `tee-vault-unseal` to unseal a vault. -- `tee-vault-admin`: An enclave that uses the Vault API to administer a vault as a proxy. -- `vault-admin`: A client utility, that talks to `tee-vault-admin` to administer a vault. -- `teepot-read` : A pre-exec utility that reads from the key-value store and passes the key-value pairs as environment - variables to the enclave. -- `teepot-write` : A pre-exec utility that reads key-values from the environment variables and writes them to the - key-value store. -- `verify-attestation`: A client utility that verifies the attestation of an enclave. -- `tee-key-preexec`: A pre-exec utility that generates a p256 secret key and passes it as an environment variable to the - enclave along with the attestation quote containing the hash of the public key. +### teepot - lib + +- `teepot`: The main rust crate that abstracts TEEs. + - `verify-attestation`: A client utility that verifies the attestation of an enclave. + - `tee-key-preexec`: A pre-exec utility that generates a p256 secret key and passes it as an environment variable to + the + enclave along with the attestation quote containing the hash of the public key. + - `tdx_google`: A base VM running on Google Cloud TDX. It receives a container URL via the instance metadata, + measures the sha384 of the URL to RTMR3 and launches the container. + - `tdx-extend`: A utility to extend an RTMR register with a hash value. + - `rtmr-calc`: A utility to calculate RTMR1 and RTMR2 from a GPT disk, the linux kernel, the linux initrd + and a UKI (unified kernel image). + - `sha384-extend`: A utility to calculate RTMR registers after extending them with a digest. + +### Vault + +Part of this project is a key-value store that runs in a Trusted Execution Environment (TEE) and uses Remote Attestation +for Authentication. The key-value store is implemented using Hashicorp Vault running in an Intel SGX enclave via the +Gramine runtime. + +- `teepot-vault`: A crate lib with for the TEE key-value store components: + - `tee-vault-unseal`: An enclave that uses the Vault API to unseal a vault as a proxy. + - `vault-unseal`: A client utility, that talks to `tee-vault-unseal` to unseal a vault. + - `tee-vault-admin`: An enclave that uses the Vault API to administer a vault as a proxy. + - `vault-admin`: A client utility, that talks to `tee-vault-admin` to administer a vault. + - `teepot-read` : A pre-exec utility that reads from the key-value store and passes the key-value pairs as + environment + variables to the enclave. + - `teepot-write` : A pre-exec utility that reads key-values from the environment variables and writes them to the + key-value store. ## Development @@ -73,7 +83,7 @@ $ nix run .#fmt ### Build as the CI would ```shell -$ nix run github:nixos/nixpkgs/nixos-23.11#nixci +$ nix run github:nixos/nixpkgs/nixos-24.11#nixci -- build ``` ### Build and test individual container @@ -96,3 +106,18 @@ Attributes: isv_svn: 0 debug_enclave: False ``` + +### TDX VM testing + +```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 +``` diff --git a/assets/config.json b/assets/config.json new file mode 100644 index 0000000..a69a474 --- /dev/null +++ b/assets/config.json @@ -0,0 +1,4 @@ +{ + "foo": "bar", + "bar": "baz" +} diff --git a/assets/gcloud-deploy.sh b/assets/gcloud-deploy.sh new file mode 100755 index 0000000..5d6e6bf --- /dev/null +++ b/assets/gcloud-deploy.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2025 Matter Labs +# + +set -ex + +BASE_DIR=${0%/*} + +NO=${NO:-1} + +ZONE=${ZONE:-us-central1-c} + +nix build -L .#tdx_google + +gsutil cp result/tdx_base_1.vmdk gs://tdx_vms/ + +gcloud migration vms image-imports create \ + --location=us-central1 \ + --target-project=tdx-pilot \ + --project=tdx-pilot \ + --skip-os-adaptation \ + --source-file=gs://tdx_vms/tdx_base_1.vmdk \ + tdx-img-pre-"${NO}" + +gcloud compute instances stop tdx-pilot --zone ${ZONE} --project tdx-pilot || : +gcloud compute instances delete tdx-pilot --zone ${ZONE} --project tdx-pilot || : + +while gcloud migration vms image-imports list --location=us-central1 --project=tdx-pilot | grep -F RUNNING; do + sleep 1 +done + +gcloud compute images create \ + --project tdx-pilot \ + --guest-os-features=UEFI_COMPATIBLE,TDX_CAPABLE,GVNIC,VIRTIO_SCSI_MULTIQUEUE \ + --storage-location=us-central1 \ + --source-image=tdx-img-pre-"${NO}" \ + tdx-img-f-"${NO}" + +gcloud compute instances create tdx-pilot \ + --machine-type c3-standard-4 --zone ${ZONE} \ + --confidential-compute-type=TDX \ + --maintenance-policy=TERMINATE \ + --image-project=tdx-pilot \ + --project tdx-pilot \ + --metadata=container_hub="docker.io",container_image="ghcr.io/matter-labs/test-tdx:117p5y281limw0w7b03v802ij00c5gzw" \ + --metadata-from-file=container_config=$BASE_DIR/config.json \ + --image tdx-img-f-"${NO}" diff --git a/crates/teepot/tests/data/gpgkey.asc b/assets/gpgkey.asc similarity index 100% rename from crates/teepot/tests/data/gpgkey.asc rename to assets/gpgkey.asc diff --git a/crates/teepot/tests/data/gpgkey.pub b/assets/gpgkey.pub similarity index 100% rename from crates/teepot/tests/data/gpgkey.pub rename to assets/gpgkey.pub diff --git a/bin/rtmr-calc/Cargo.toml b/bin/rtmr-calc/Cargo.toml new file mode 100644 index 0000000..f60255f --- /dev/null +++ b/bin/rtmr-calc/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "rtmr-calc" +publish = false +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +gpt.workspace = true +hex.workspace = true +pe-sign.workspace = true +sha2.workspace = true +teepot.workspace = true +tracing.workspace = true diff --git a/bin/rtmr-calc/src/main.rs b/bin/rtmr-calc/src/main.rs new file mode 100644 index 0000000..564d351 --- /dev/null +++ b/bin/rtmr-calc/src/main.rs @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +use anyhow::{anyhow, Result}; +use clap::Parser; +use pesign::PE; +use sha2::{Digest, Sha384}; +use std::{ + fmt::{Display, Formatter}, + io::{Error, Read, Seek, SeekFrom}, + path::PathBuf, +}; +use teepot::{ + log::{setup_logging, LogLevelParser}, + tdx::UEFI_MARKER_DIGEST_BYTES, +}; +use tracing::{debug, info, level_filters::LevelFilter}; + +/// Precalculate rtmr1 and rtmr2 values. +/// +/// Currently tested with the Google confidential compute engines. +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Arguments { + /// disk image to measure the GPT table from + #[arg(long)] + image: PathBuf, + /// path to the used UKI EFI binary + #[arg(long)] + bootefi: PathBuf, + /// path to the used linux kernel EFI binary (contained in the UKI) + #[arg(long)] + kernel: PathBuf, + /// Log level for the log output. + /// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace` + #[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)] + pub log_level: LevelFilter, +} + +struct Rtmr { + state: Vec, +} + +impl Rtmr { + pub fn extend(&mut self, hash: &[u8]) -> &[u8] { + self.state.extend(hash); + let bytes = Sha384::digest(&self.state); + self.state.resize(48, 0); + self.state.copy_from_slice(&bytes); + &self.state + } +} + +impl Default for Rtmr { + fn default() -> Self { + Self { + state: [0u8; 48].to_vec(), + } + } +} + +impl Display for Rtmr { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.state)) + } +} + +const CHUNK_SIZE: u64 = 1024 * 128; + +fn main() -> Result<()> { + let args = Arguments::parse(); + tracing::subscriber::set_global_default(setup_logging( + env!("CARGO_CRATE_NAME"), + &args.log_level, + )?)?; + + let mut rtmr1 = Rtmr::default(); + let mut rtmr2 = Rtmr::default(); + + /* + - pcr_index: 1 + event: efiaction + digests: + - method: sha384 + digest: 77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71 + digest_verification_status: verified + data: Q2FsbGluZyBFRkkgQXBwbGljYXRpb24gZnJvbSBCb290IE9wdGlvbg== + parsed_data: + Ok: + text: Calling EFI Application from Boot Option + */ + rtmr1.extend(&hex::decode("77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71")?); + + /* + - pcr_index: 1 + event: separator + digests: + - method: sha384 + digest: 394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0 + digest_verification_status: verified + data: AAAAAA== + parsed_data: + Ok: + validseparator: UEFI + */ + rtmr1.extend(&UEFI_MARKER_DIGEST_BYTES); + + // Open disk image. + let cfg = gpt::GptConfig::new().writable(false); + let disk = cfg.open(args.image)?; + + // Print GPT layout. + info!("Disk (primary) header: {:#?}", disk.primary_header()); + info!("Partition layout: {:#?}", disk.partitions()); + + let header = disk.primary_header()?; + let mut msr = Vec::::new(); + let lb_size = disk.logical_block_size(); + let mut device = disk.device_ref(); + device.seek(SeekFrom::Start(lb_size.as_u64()))?; + let mut buf = [0u8; 92]; + device.read_exact(&mut buf)?; + msr.extend_from_slice(&buf); + + let pstart = header + .part_start + .checked_mul(lb_size.as_u64()) + .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 < u32::from(u8::MAX)); + + let empty_bytes = [0u8; 128]; + + msr.extend_from_slice(&disk.partitions().len().to_le_bytes()); + + for _ in 0..header.num_parts { + let mut bytes = empty_bytes; + + device.read_exact(&mut bytes)?; + if bytes.eq(&empty_bytes) { + continue; + } + msr.extend_from_slice(&bytes); + } + + let mut hasher = Sha384::new(); + hasher.update(&msr); + let result = hasher.finalize(); + info!("GPT hash: {:x}", result); + + rtmr1.extend(&result); + + let mut pe = PE::from_path(&args.bootefi)?; + + let hash = pe.calc_authenticode(pesign::cert::Algorithm::Sha384)?; + info!("hash of {:?}: {hash}", args.bootefi); + rtmr1.extend(&hex::decode(&hash)?); + + let section_table = pe.get_section_table()?; + + for section in §ion_table { + debug!(section_name = ?section.name()?); + } + + for sect in [".linux", ".osrel", ".cmdline", ".initrd", ".uname", ".sbat"] { + let mut hasher = Sha384::new(); + hasher.update(sect.as_bytes()); + hasher.update([0u8]); + let out = hasher.finalize(); + debug!(sect, "name: {out:x}"); + rtmr2.extend(&out); + + let s = section_table + .iter() + .find(|s| s.name().unwrap().eq(sect)) + .ok_or(anyhow!("Failed to find section `{sect}`"))?; + + 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(); + + loop { + if start >= end { + break; + } + + let mut buf = vec![0; CHUNK_SIZE.min(end - start) as _]; + pe.read_exact_at(start, buf.as_mut_slice())?; + hasher.update(buf.as_slice()); + + start += CHUNK_SIZE; + } + let digest = hasher.finalize(); + debug!(sect, "binary: {digest:x}"); + rtmr2.extend(&digest); + } + + let hash = PE::from_path(&args.kernel)?.calc_authenticode(pesign::cert::Algorithm::Sha384)?; + info!("hash of {:?}: {hash}", args.kernel); + rtmr1.extend(&hex::decode(&hash)?); + + /* + - pcr_index: 1 + event: efiaction + digests: + - method: sha384 + digest: 214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0 + digest_verification_status: verified + data: RXhpdCBCb290IFNlcnZpY2VzIEludm9jYXRpb24= + parsed_data: + Ok: + text: Exit Boot Services Invocation + */ + rtmr1.extend(&hex::decode("214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0")?); + + /* + - pcr_index: 1 + event: efiaction + digests: + - method: sha384 + digest: 0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74 + digest_verification_status: verified + data: RXhpdCBCb290IFNlcnZpY2VzIFJldHVybmVkIHdpdGggU3VjY2Vzcw== + parsed_data: + Ok: + text: Exit Boot Services Returned with Success + */ + rtmr1.extend(&hex::decode("0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74")?); + + println!("{{"); + println!("\t\"rtmr1\": \"{rtmr1}\","); + println!("\t\"rtmr2\": \"{rtmr2}\""); + println!("}}"); + + Ok(()) +} diff --git a/bin/sha384-extend/Cargo.toml b/bin/sha384-extend/Cargo.toml new file mode 100644 index 0000000..fac9e4c --- /dev/null +++ b/bin/sha384-extend/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sha384-extend" +publish = false +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +hex.workspace = true +sha2.workspace = true +teepot.workspace = true diff --git a/bin/sha384-extend/src/main.rs b/bin/sha384-extend/src/main.rs new file mode 100644 index 0000000..be496f0 --- /dev/null +++ b/bin/sha384-extend/src/main.rs @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +//! A tool for extending SHA384 digests, commonly used in TPM and TDX operations +//! +//! # Overview +//! This utility implements the extend operation used in Trusted Platform Module (TPM) +//! Platform Configuration Registers (PCRs) and Intel Trust Domain Extensions (TDX) +//! Runtime Measurement Registers (RTMRs). The extend operation combines two SHA384 +//! digests by concatenating and then hashing them. +//! +//! # Usage +//! ```shell +//! sha384-extend [--base ] +//! ``` +//! Where: +//! - `extend-value`: SHA384 digest in hex format to extend with +//! - `initial-value`: Optional initial SHA384 digest in hex format (defaults to "00") +//! +//! # Example +//! ```shell +//! sha384-extend --base 01 26bb0c +//! ``` + +#![deny(missing_docs)] +#![deny(clippy::all)] + +use anyhow::{Context, Result}; +use clap::Parser; +use sha2::Digest; +use teepot::util::pad; + +/// Calculate e.g. a TDX RTMR or TPM PCR SHA384 digest by extending it with another +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Arguments { + /// The SHA384 digest (in hex format) to extend the base value with. + /// Must be a valid hex string that can be padded to 48 bytes (384 bits). + extend: String, + + /// The initial SHA384 digest (in hex format) to extend from. + /// Must be a valid hex string that can be padded to 48 bytes (384 bits). + #[arg(long, default_value = "00", required = false)] + base: String, +} + +/// Extends a base SHA384 digest with another digest +/// +/// # Arguments +/// * `base` - Base hex string to extend from +/// * `extend` - Hex string to extend with +/// +/// # Returns +/// * `Result` - The resulting SHA384 digest as a hex string +/// +/// # Examples +/// ``` +/// let result = extend_sha384("00", "aa").unwrap(); +/// ``` +pub fn extend_sha384(base: &str, extend: &str) -> Result { + let mut hasher = sha2::Sha384::new(); + + hasher.update(pad::<48>(&hex::decode(base).context(format!( + "Failed to decode base digest '{base}' - expected hex string", + ))?)?); + + hasher.update(pad::<48>(&hex::decode(extend).context(format!( + "Failed to decode extend digest '{extend}' - expected hex string", + ))?)?); + + Ok(hex::encode(hasher.finalize())) +} + +fn main() -> Result<()> { + let args = Arguments::parse(); + let hex = extend_sha384(&args.base, &args.extend)?; + println!("{hex}"); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_BASE: &str = "00"; + const TEST_EXTEND: &str = "d3a665eb2bf8a6c4e6cee0ccfa663ee4098fc4903725b1823d8d0316126bb0cb"; + const EXPECTED_RESULT: &str = "971fb52f90ec98a234301ca9b8fc30b613c33e3dd9c0cc42dcb8003d4a95d8fb218b75baf028b70a3cabcb947e1ca453"; + + const EXPECTED_RESULT_00: &str = "f57bb7ed82c6ae4a29e6c9879338c592c7d42a39135583e8ccbe3940f2344b0eb6eb8503db0ffd6a39ddd00cd07d8317"; + + #[test] + fn test_extend_sha384_with_test_vectors() { + let result = extend_sha384(TEST_BASE, TEST_EXTEND).unwrap(); + assert_eq!( + result, EXPECTED_RESULT, + "SHA384 extend result didn't match expected value" + ); + + // Test with empty base + let result = extend_sha384("", TEST_EXTEND).unwrap(); + assert_eq!( + result, EXPECTED_RESULT, + "SHA384 extend result didn't match expected value" + ); + + // Test with empty base + let result = extend_sha384("", "").unwrap(); + assert_eq!( + result, EXPECTED_RESULT_00, + "SHA384 extend result didn't match expected value" + ); + } + + #[test] + fn test_extend_sha384_with_invalid_base() { + // Test with invalid hex in base + let result = extend_sha384("not_hex", TEST_EXTEND); + assert!(result.is_err(), "Should fail with invalid base hex"); + + // Test with odd length hex string + let result = extend_sha384("0", TEST_EXTEND); + assert!(result.is_err(), "Should fail with odd-length hex string"); + } + + #[test] + fn test_extend_sha384_with_invalid_extend() { + // Test with invalid hex in extend + let result = extend_sha384(TEST_BASE, "not_hex"); + assert!(result.is_err(), "Should fail with invalid extend hex"); + + // Test with odd length hex string + let result = extend_sha384(TEST_BASE, "0"); + assert!(result.is_err(), "Should fail with odd-length hex string"); + } + + #[test] + fn test_extend_sha384_with_oversized_input() { + // Create a hex string that's too long (more than 48 bytes when decoded) + let oversized = "00".repeat(49); // 49 bytes when decoded + + let result = extend_sha384(TEST_BASE, &oversized); + assert!(result.is_err(), "Should fail with oversized extend value"); + + let result = extend_sha384(&oversized, TEST_EXTEND); + assert!(result.is_err(), "Should fail with oversized base value"); + } + + #[test] + fn test_extend_sha384_idempotent() { + // Test that extending with the same values produces the same result + let result1 = extend_sha384(TEST_BASE, TEST_EXTEND).unwrap(); + let result2 = extend_sha384(TEST_BASE, TEST_EXTEND).unwrap(); + assert_eq!(result1, result2, "Same inputs should produce same output"); + } + + #[test] + fn test_extend_sha384_case_sensitivity() { + // Test that upper and lower case hex strings produce the same result + let upper_extend = TEST_EXTEND.to_uppercase(); + let result1 = extend_sha384(TEST_BASE, TEST_EXTEND).unwrap(); + let result2 = extend_sha384(TEST_BASE, &upper_extend).unwrap(); + assert_eq!(result1, result2, "Case should not affect the result"); + } +} diff --git a/bin/tdx-extend/Cargo.toml b/bin/tdx-extend/Cargo.toml new file mode 100644 index 0000000..a34e597 --- /dev/null +++ b/bin/tdx-extend/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tdx-extend" +publish = false +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +hex.workspace = true +teepot.workspace = true +tracing.workspace = true diff --git a/bin/tdx-extend/src/main.rs b/bin/tdx-extend/src/main.rs new file mode 100644 index 0000000..31aca89 --- /dev/null +++ b/bin/tdx-extend/src/main.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +//! Extend the TDX measurement + +#![deny(missing_docs)] +#![deny(clippy::all)] + +use tracing::error; + +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +mod os { + use anyhow::{Context as _, Result}; + use clap::Parser; + use teepot::{ + log::{setup_logging, LogLevelParser}, + tdx::rtmr::TdxRtmrEvent, + util::pad, + }; + use tracing::level_filters::LevelFilter; + + /// Extend a TDX rtmr with a hash digest for measured boot. + #[derive(Parser, Debug)] + #[command(author, version, about, long_about = None)] + struct Arguments { + /// digest in hex to extend the rtmr with + #[arg(long)] + digest: String, + /// the number or the rtmr + #[arg(long, default_value = "2")] + rtmr: u64, + /// Log level for the log output. + /// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace` + #[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)] + pub log_level: LevelFilter, + } + + pub fn main_with_error() -> Result<()> { + let args = Arguments::parse(); + tracing::subscriber::set_global_default(setup_logging( + env!("CARGO_CRATE_NAME"), + &args.log_level, + )?)?; + + // Parse the digest string as a hex array + let digest_bytes = hex::decode(&args.digest).context("Invalid digest format")?; + let extend_data: [u8; 48] = pad(&digest_bytes).context("Invalid digest length")?; + + // Extend the TDX measurement with the extend data + TdxRtmrEvent::default() + .with_extend_data(extend_data) + .with_rtmr_index(args.rtmr) + .extend()?; + + Ok(()) + } +} + +#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))] +mod os { + pub fn main_with_error() -> anyhow::Result<()> { + anyhow::bail!("OS or architecture not supported"); + } +} +fn main() -> anyhow::Result<()> { + let ret = os::main_with_error(); + if let Err(e) = &ret { + error!(error = %e, "Execution failed"); + } + ret +} diff --git a/bin/tdx-test/Cargo.toml b/bin/tdx-test/Cargo.toml new file mode 100644 index 0000000..debc34f --- /dev/null +++ b/bin/tdx-test/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tdx-test" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +publish = false + +[dependencies] +anyhow.workspace = true +serde.workspace = true +teepot.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true diff --git a/bin/tdx-test/src/main.rs b/bin/tdx-test/src/main.rs new file mode 100644 index 0000000..539f859 --- /dev/null +++ b/bin/tdx-test/src/main.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use anyhow::Result; +use serde::{Deserialize, Serialize}; +use teepot::config::{load_config_with_telemetry, TelemetryConfig}; +use thiserror::Error; +use tracing::{debug, error, info, trace, warn}; + +// Configuration struct +#[derive(Debug, Serialize, Deserialize)] +struct AppConfig { + server: ServerConfig, + telemetry: TelemetryConfig, +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + server: ServerConfig::default(), + telemetry: TelemetryConfig::new( + env!("CARGO_CRATE_NAME").into(), + env!("CARGO_PKG_VERSION").into(), + ), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct ServerConfig { + port: u16, +} + +impl Default for ServerConfig { + fn default() -> Self { + Self { port: 8080 } + } +} + +// Error handling +#[derive(Error, Debug)] +enum AppError { + #[error("Internal server error")] + Internal(#[from] anyhow::Error), +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let config = + load_config_with_telemetry("APP".into(), |config: &AppConfig| &config.telemetry).await?; + + loop { + error!(?config, "error test!"); + warn!(?config, "warn test!"); + info!(?config, "info test!"); + debug!(?config, "debug test!"); + trace!(?config, "trace test!"); + tokio::time::sleep(std::time::Duration::from_secs(60)).await; + } +} diff --git a/bin/tee-key-preexec/Cargo.toml b/bin/tee-key-preexec/Cargo.toml index e5a62cc..60a7b93 100644 --- a/bin/tee-key-preexec/Cargo.toml +++ b/bin/tee-key-preexec/Cargo.toml @@ -12,7 +12,6 @@ repository.workspace = true [dependencies] anyhow.workspace = true clap.workspace = true -rand.workspace = true secp256k1.workspace = true teepot.workspace = true tracing.workspace = true diff --git a/bin/tee-key-preexec/src/main.rs b/bin/tee-key-preexec/src/main.rs index 6af0c09..758310d 100644 --- a/bin/tee-key-preexec/src/main.rs +++ b/bin/tee-key-preexec/src/main.rs @@ -1,23 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2024 Matter Labs +// Copyright (c) 2024-2025 Matter Labs //! Pre-exec for binary running in a TEE needing attestation of a secret signing key #![deny(missing_docs)] #![deny(clippy::all)] -use anyhow::{Context, Result}; +use anyhow::Result; use clap::Parser; -use secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey}; use std::ffi::OsString; -use std::os::unix::process::CommandExt; -use std::process::Command; -use teepot::quote::get_quote; use tracing::error; -use tracing_log::LogTracer; -use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; - -const TEE_QUOTE_FILE: &str = "/tmp/tee_quote"; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -30,7 +22,20 @@ struct Args { cmd_args: Vec, } +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] fn main_with_error() -> Result<()> { + use anyhow::Context; + use secp256k1::{rand, Secp256k1}; + use std::{os::unix::process::CommandExt, process::Command}; + use teepot::{ + ethereum::public_key_to_ethereum_address, prover::reportdata::ReportDataV1, + quote::get_quote, tdx::rtmr::TdxRtmrEvent, + }; + use tracing_log::LogTracer; + use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; + + const TEE_QUOTE_FILE: &str = "/tmp/tee_quote"; + LogTracer::init().context("Failed to set logger")?; let subscriber = Registry::default() @@ -39,23 +44,34 @@ 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 keypair = Keypair::new(&secp, &mut rng); - let signing_key = SecretKey::from_keypair(&keypair); - let verifying_key = PublicKey::from_keypair(&keypair); - let verifying_key_bytes = verifying_key.serialize(); - let tee_type = match get_quote(verifying_key_bytes.as_ref()) { - Ok(quote) => { + let (signing_key, verifying_key) = secp.generate_keypair(&mut rng); + let ethereum_address = public_key_to_ethereum_address(&verifying_key); + let report_data = ReportDataV1 { ethereum_address }; + let report_data_bytes: [u8; 64] = report_data.into(); + let tee_type = match get_quote(&report_data_bytes) { + Ok((teepot::quote::TEEType::TDX, quote)) => { + // In the case of TDX, we want to advance RTMR 3 after getting the quote, + // so that any breach can't generate a new attestation with the expected RTMRs + TdxRtmrEvent::default() + .with_rtmr_index(3) + .with_extend_data(teepot::tdx::UEFI_MARKER_DIGEST_BYTES) + .extend()?; + // save quote to file - std::fs::write(TEE_QUOTE_FILE, quote)?; - "sgx" + std::fs::write(TEE_QUOTE_FILE, quote).context(TEE_QUOTE_FILE)?; + teepot::quote::TEEType::TDX.to_string() + } + Ok((tee_type, quote)) => { + // save quote to file + std::fs::write(TEE_QUOTE_FILE, quote).context(TEE_QUOTE_FILE)?; + tee_type.to_string() } Err(e) => { error!("Failed to get quote: {}", e); - std::fs::write(TEE_QUOTE_FILE, [])?; - "none" + std::fs::write(TEE_QUOTE_FILE, []).context(TEE_QUOTE_FILE)?; + "none".to_string() } }; @@ -80,6 +96,11 @@ fn main_with_error() -> Result<()> { }) } +#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))] +fn main_with_error() -> Result<()> { + anyhow::bail!("OS or architecture not supported"); +} + fn main() -> Result<()> { let ret = main_with_error(); if let Err(e) = &ret { diff --git a/bin/tee-ratls-preexec/src/main.rs b/bin/tee-ratls-preexec/src/main.rs index 1fdd12d..e6dfb1c 100644 --- a/bin/tee-ratls-preexec/src/main.rs +++ b/bin/tee-ratls-preexec/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2024 Matter Labs +// Copyright (c) 2024-2025 Matter Labs //! Pre-exec for binary running in a TEE needing attestation of a secret signing key @@ -17,7 +17,7 @@ use std::io::Write; use std::os::unix::process::CommandExt; use std::path::PathBuf; use std::process::Command; -use teepot::server::pki::make_signed_cert; +use teepot::pki::make_signed_cert; use tracing::error; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; diff --git a/bin/tee-self-attestation-test/README.md b/bin/tee-self-attestation-test/README.md index 55d273a..d7ea174 100644 --- a/bin/tee-self-attestation-test/README.md +++ b/bin/tee-self-attestation-test/README.md @@ -1,6 +1,6 @@ # self-attestation-test -Optionally build and load the containers (remove the `matterlabsrobot/` repo from the commands below then) +Optionally build and load the containers (remove the `ghcr.io/matter-labs/` repo from the commands below then) ```bash $ nix build -L .#container-verify-attestation-sgx && docker load -i result @@ -12,9 +12,9 @@ $ nix build -L .#container-self-attestation-test-sgx-azure && docker load -i res ```bash ❯ docker run -i --init --rm --privileged --device /dev/sgx_enclave \ - matterlabsrobot/teepot-self-attestation-test-sgx-azure:latest \ + ghcr.io/matter-labs/teepot-self-attestation-test-sgx-azure:latest \ | base64 -d --ignore-garbage \ - | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest - + | docker run -i --rm ghcr.io/matter-labs/verify-attestation-sgx:latest - aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. Gramine is starting. Parsing TOML manifest file, this may take some time... @@ -31,9 +31,9 @@ reportdata: 00000000000000000000000000000000000000000000000000000000000000000000 ```bash ❯ docker run -i --init --rm --privileged --device /dev/sgx_enclave \ - matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \ + ghcr.io/matter-labs/teepot-self-attestation-test-sgx-dcap:latest \ | base64 -d --ignore-garbage \ - | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest - + | docker run -i --rm ghcr.io/matter-labs/verify-attestation-sgx:latest - aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. Gramine is starting. Parsing TOML manifest file, this may take some time... @@ -48,9 +48,9 @@ On an outdated machine, this might look like this: ```bash ❯ docker run -i --init --rm --privileged --device /dev/sgx_enclave \ - matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \ + ghcr.io/matter-labs/teepot-self-attestation-test-sgx-dcap:latest \ | base64 -d --ignore-garbage \ - | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest - + | docker run -i --rm ghcr.io/matter-labs/verify-attestation-sgx:latest - aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. Gramine is starting. Parsing TOML manifest file, this may take some time... @@ -68,3 +68,14 @@ mrsigner: c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d mrenclave: 7ffe70789261a51769f50e129bfafb2aafe91a4e17c3f0d52839006777c652f6 reportdata: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ``` + +## podman + +```bash +❯ podman run -i --rm --group-add=keep-groups -v /var/run/aesmd:/var/run/aesmd -v /dev/sgx_enclave:/dev/sgx_enclave \ + ghcr.io/matter-labs/teepot-self-attestation-test-sgx-dcap:latest \ + | base64 -d --ignore-garbage \ + | podman run -i --rm --net host \ + -v /etc/sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf \ + ghcr.io/matter-labs/verify-attestation-sgx-dcap:latest +``` diff --git a/bin/tee-self-attestation-test/src/main.rs b/bin/tee-self-attestation-test/src/main.rs index a08353c..4c9437f 100644 --- a/bin/tee-self-attestation-test/src/main.rs +++ b/bin/tee-self-attestation-test/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Simple TEE self-attestation test @@ -8,7 +8,7 @@ use anyhow::{Context, Result}; use base64::{engine::general_purpose, Engine as _}; -use teepot::server::attestation::get_quote_and_collateral; +use teepot::quote::attestation::get_quote_and_collateral; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; @@ -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(()) } diff --git a/bin/verify-attestation/Cargo.toml b/bin/verify-attestation/Cargo.toml index ff473d9..8a4e129 100644 --- a/bin/verify-attestation/Cargo.toml +++ b/bin/verify-attestation/Cargo.toml @@ -10,7 +10,4 @@ repository.workspace = true [dependencies] anyhow.workspace = true clap.workspace = true -hex.workspace = true -secp256k1.workspace = true teepot.workspace = true -zksync_basic_types.workspace = true diff --git a/bin/verify-attestation/src/main.rs b/bin/verify-attestation/src/main.rs index 7f0c380..b33b7ad 100644 --- a/bin/verify-attestation/src/main.rs +++ b/bin/verify-attestation/src/main.rs @@ -1,17 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Tool for SGX attestation and batch signature verification -use anyhow::{Context, Result}; -use clap::{Args, Parser, Subcommand}; -use secp256k1::{ecdsa::Signature, Message, PublicKey}; +use anyhow::{bail, Context, Result}; +use clap::Parser; + use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH}; -use teepot::{ - client::TcbLevel, - sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult}, -}; -use zksync_basic_types::H256; +use teepot::quote::{get_collateral, verify_quote_with_collateral, QuoteVerificationResult}; #[derive(Parser, Debug)] #[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)] @@ -19,9 +15,6 @@ struct Arguments { /// Attestation quote proving the signature originated from a TEE enclave. #[clap(name = "attestation_file", value_parser)] attestation: ArgSource, - /// An optional subcommand, for instance, for optional signature verification. - #[clap(subcommand)] - command: Option, } #[derive(Debug, Clone)] @@ -41,22 +34,6 @@ impl FromStr for ArgSource { } } -#[derive(Args, Debug)] -struct SignatureArgs { - /// File containing a batch signature signed within a TEE enclave. - #[arg(long)] - signature_file: PathBuf, - /// Batch root hash for signature verification. - #[arg(long)] - root_hash: H256, -} - -#[derive(Subcommand, Debug)] -enum SubCommands { - /// Verify a batch signature signed within a TEE enclave. - SignVerify(SignatureArgs), -} - fn main() -> Result<()> { let args = Arguments::parse(); let attestation_quote_bytes = match args.attestation { @@ -71,40 +48,18 @@ fn main() -> Result<()> { }; let quote_verification_result = verify_attestation_quote(&attestation_quote_bytes)?; print_quote_verification_summary("e_verification_result); - match &args.command { - Some(SubCommands::SignVerify(signature_args)) => { - verify_signature("e_verification_result, signature_args)?; - } - None => {} - } - Ok(()) -} - -fn verify_signature( - quote_verification_result: &QuoteVerificationResult, - signature_args: &SignatureArgs, -) -> Result<()> { - let reportdata = "e_verification_result.quote.report_body.reportdata; - let public_key = PublicKey::from_slice(reportdata)?; - println!("Public key from attestation quote: {}", public_key); - let signature_bytes = fs::read(&signature_args.signature_file)?; - let signature = Signature::from_compact(&signature_bytes)?; - let root_hash_msg = Message::from_digest_slice(&signature_args.root_hash.0)?; - if signature.verify(&root_hash_msg, &public_key).is_ok() { - println!("Signature verified successfully"); - } else { - println!("Failed to verify signature"); - } Ok(()) } fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result { + if attestation_quote_bytes.is_empty() { + bail!("Empty quote provided!"); + } println!( "Verifying quote ({} bytes)...", attestation_quote_bytes.len() ); - let collateral = - tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral")?; + let collateral = get_collateral(attestation_quote_bytes)?; let unix_time: i64 = std::time::SystemTime::now() .duration_since(UNIX_EPOCH)? .as_secs() as _; @@ -115,7 +70,7 @@ fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result`: The batch number or range of batch numbers to verify the attestation and signature (e.g., " + 42" or "42-45"). Mutually exclusive with `--continuous`. +- `--continuous `: Continuous mode: keep verifying new batches starting from the specified batch number + until interrupted. Mutually exclusive with `--batch`. +- `--rpc `: URL of the RPC server to query for the batch attestation and signature. +- `--chain `: Chain ID of the network to query (default: L2ChainId::default()). +- `--rate-limit `: Rate limit between requests in milliseconds (default: 0). +- `--log-level `: Log level for the log output. Valid values are: `off`, `error`, `warn`, `info`, `debug`, + `trace` (default: `warn`). +- `--attestation-policy-file `: Path to a YAML file containing attestation policy configuration. This overrides + any attestation policy settings provided via command line options. + +Either `--batch` or `--continuous` mode must be specified. + +### YAML Configuration File + +The attestation policy is loaded from a YAML file using the `--attestation-policy-file` option. + +Example YAML configuration file: + +```yaml +sgx: + mrenclaves: + - a2caa7055e333f69c3e46ca7ba65b135a86c90adfde2afb356e05075b7818b3c + - 36eeb64cc816f80a1cf5818b26710f360714b987d3799e757cbefba7697b9589 + - 4a8b79e5123f4dbf23453d583cb8e5dcf4d19a6191a0be6dd85b7b3052c32faf + - 1498845b3f23667356cc49c38cae7b4ac234621a5b85fdd5c52b5f5d12703ec9 + - 1b2374631bb2572a0e05b3be8b5cdd23c42e9d7551e1ef200351cae67c515a65 + - 6fb19e47d72a381a9f3235c450f8c40f01428ce19a941f689389be3eac24f42a + - b610fd1d749775cc3de88beb84afe8bb79f55a19100db12d76f6a62ac576e35d + - a0b1b069b01bdcf3c1517ef8d4543794a27ed4103e464be7c4afdc6136b42d66 + - 71e2a11a74b705082a7286b2008f812f340c0e4de19f8b151baa347eda32d057 + - d5a0bf8932d9a3d7af6d9405d4c6de7dcb7b720bb5510666b4396fc58ee58bb2 + allowed_tcb_levels: + - Ok + - SwHardeningNeeded + allowed_advisory_ids: + - INTEL-SA-00615 +tdx: + mrs: + - - 2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525 + - 3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f + - c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146 + - 092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945 + - 971fb52f90ec98a234301ca9b8fc30b613c33e3dd9c0cc42dcb8003d4a95d8fb218b75baf028b70a3cabcb947e1ca453 + - - 2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525 + - 3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f + - c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146 + - 092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945 + - f57bb7ed82c6ae4a29e6c9879338c592c7d42a39135583e8ccbe3940f2344b0eb6eb8503db0ffd6a39ddd00cd07d8317 + allowed_tcb_levels: + - Ok +``` diff --git a/bin/verify-era-proof-attestation/examples/attestation_policy.yaml b/bin/verify-era-proof-attestation/examples/attestation_policy.yaml new file mode 100644 index 0000000..cfb6a34 --- /dev/null +++ b/bin/verify-era-proof-attestation/examples/attestation_policy.yaml @@ -0,0 +1,31 @@ +sgx: + mrenclaves: + - a2caa7055e333f69c3e46ca7ba65b135a86c90adfde2afb356e05075b7818b3c + - 36eeb64cc816f80a1cf5818b26710f360714b987d3799e757cbefba7697b9589 + - 4a8b79e5123f4dbf23453d583cb8e5dcf4d19a6191a0be6dd85b7b3052c32faf + - 1498845b3f23667356cc49c38cae7b4ac234621a5b85fdd5c52b5f5d12703ec9 + - 1b2374631bb2572a0e05b3be8b5cdd23c42e9d7551e1ef200351cae67c515a65 + - 6fb19e47d72a381a9f3235c450f8c40f01428ce19a941f689389be3eac24f42a + - b610fd1d749775cc3de88beb84afe8bb79f55a19100db12d76f6a62ac576e35d + - a0b1b069b01bdcf3c1517ef8d4543794a27ed4103e464be7c4afdc6136b42d66 + - 71e2a11a74b705082a7286b2008f812f340c0e4de19f8b151baa347eda32d057 + - d5a0bf8932d9a3d7af6d9405d4c6de7dcb7b720bb5510666b4396fc58ee58bb2 + allowed_tcb_levels: + - Ok + - SwHardeningNeeded + allowed_advisory_ids: + - INTEL-SA-00615 +tdx: + mrs: + - - 2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525 + - 3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f + - c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146 + - 092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945 + - 971fb52f90ec98a234301ca9b8fc30b613c33e3dd9c0cc42dcb8003d4a95d8fb218b75baf028b70a3cabcb947e1ca453 + - - 2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525 + - 3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f + - c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146 + - 092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945 + - f57bb7ed82c6ae4a29e6c9879338c592c7d42a39135583e8ccbe3940f2344b0eb6eb8503db0ffd6a39ddd00cd07d8317 + allowed_tcb_levels: + - Ok diff --git a/bin/verify-era-proof-attestation/src/args.rs b/bin/verify-era-proof-attestation/src/args.rs deleted file mode 100644 index 95555a5..0000000 --- a/bin/verify-era-proof-attestation/src/args.rs +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs - -use anyhow::{anyhow, Result}; -use clap::{ArgGroup, Args, Parser}; -use std::time::Duration; -use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; -use tracing_subscriber::filter::LevelFilter; -use url::Url; -use zksync_basic_types::L1BatchNumber; -use zksync_types::L2ChainId; - -#[derive(Parser, Debug, Clone)] -#[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)] -#[clap(group( - ArgGroup::new("mode") - .required(true) - .args(&["batch_range", "continuous"]), -))] -pub struct Arguments { - #[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)] - pub log_level: LevelFilter, - /// The batch number or range of batch numbers to verify the attestation and signature (e.g., - /// "42" or "42-45"). This option is mutually exclusive with the `--continuous` mode. - #[clap(short = 'n', long = "batch", value_parser = parse_batch_range)] - pub batch_range: Option<(L1BatchNumber, L1BatchNumber)>, - /// Continuous mode: keep verifying new batches until interrupted. This option is mutually - /// exclusive with the `--batch` option. - #[clap(long, value_name = "FIRST_BATCH")] - pub continuous: Option, - /// URL of the RPC server to query for the batch attestation and signature. - #[clap(long = "rpc")] - pub rpc_url: Url, - /// Chain ID of the network to query. - #[clap(long = "chain", default_value_t = L2ChainId::default().as_u64())] - pub chain_id: u64, - /// Rate limit between requests in milliseconds. - #[clap(long, default_value = "0", value_parser = parse_duration)] - pub rate_limit: Duration, - /// Criteria for valid attestation policy. Invalid proofs will be rejected. - #[clap(flatten)] - pub attestation_policy: AttestationPolicyArgs, -} - -/// Attestation policy implemented as a set of criteria that must be met by SGX attestation. -#[derive(Args, Debug, Clone)] -pub struct AttestationPolicyArgs { - /// Comma-separated list of allowed hex-encoded SGX mrsigners. Batch attestation must consist of - /// one of these mrsigners. If the list is empty, the mrsigner check is skipped. - #[arg(long = "mrsigners")] - pub sgx_mrsigners: Option, - /// Comma-separated list of allowed hex-encoded SGX mrenclaves. Batch attestation must consist - /// of one of these mrenclaves. If the list is empty, the mrenclave check is skipped. - #[arg(long = "mrenclaves")] - pub sgx_mrenclaves: Option, - /// Comma-separated list of allowed TCB levels. If the list is empty, the TCB level check is - /// skipped. Allowed values: Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, - /// OutOfDate, OutOfDateConfigNeeded. - #[arg(long, value_parser = parse_tcb_levels, default_value = "Ok")] - pub sgx_allowed_tcb_levels: EnumSet, -} - -fn parse_batch_range(s: &str) -> Result<(L1BatchNumber, L1BatchNumber)> { - let parse = |s: &str| { - s.parse::() - .map(L1BatchNumber::from) - .map_err(|e| anyhow!(e)) - }; - match s.split_once('-') { - Some((start, end)) => { - let (start, end) = (parse(start)?, parse(end)?); - if start > end { - Err(anyhow!( - "Start batch number ({}) must be less than or equal to end batch number ({})", - start, - end - )) - } else { - Ok((start, end)) - } - } - None => { - let batch_number = parse(s)?; - Ok((batch_number, batch_number)) - } - } -} - -fn parse_duration(s: &str) -> Result { - let millis = s.parse()?; - Ok(Duration::from_millis(millis)) -} - -#[derive(Clone)] -struct LogLevelParser; - -impl clap::builder::TypedValueParser for LogLevelParser { - type Value = LevelFilter; - - fn parse_ref( - &self, - cmd: &clap::Command, - arg: Option<&clap::Arg>, - value: &std::ffi::OsStr, - ) -> Result { - clap::builder::TypedValueParser::parse(self, cmd, arg, value.to_owned()) - } - - fn parse( - &self, - cmd: &clap::Command, - arg: Option<&clap::Arg>, - value: std::ffi::OsString, - ) -> std::result::Result { - use std::str::FromStr; - let p = clap::builder::PossibleValuesParser::new([ - "off", "error", "warn", "info", "debug", "trace", - ]); - let v = p.parse(cmd, arg, value)?; - - Ok(LevelFilter::from_str(&v).unwrap()) - } -} diff --git a/bin/verify-era-proof-attestation/src/client.rs b/bin/verify-era-proof-attestation/src/client.rs deleted file mode 100644 index 6746e38..0000000 --- a/bin/verify-era-proof-attestation/src/client.rs +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs - -use anyhow::{anyhow, Context, Result}; -use url::Url; -use zksync_basic_types::{L1BatchNumber, H256}; -use zksync_types::L2ChainId; -use zksync_web3_decl::{ - client::{Client as NodeClient, L2}, - error::ClientRpcContext, - namespaces::ZksNamespaceClient, -}; - -pub trait JsonRpcClient { - async fn get_root_hash(&self, batch_number: L1BatchNumber) -> Result; - // TODO implement get_tee_proofs(batch_number, tee_type) once https://crates.io/crates/zksync_web3_decl crate is updated -} - -pub struct MainNodeClient(NodeClient); - -impl MainNodeClient { - pub fn new(rpc_url: Url, chain_id: u64) -> Result { - let node_client = NodeClient::http(rpc_url.into()) - .context("failed creating JSON-RPC client for main node")? - .for_network( - L2ChainId::try_from(chain_id) - .map_err(anyhow::Error::msg)? - .into(), - ) - .build(); - - Ok(MainNodeClient(node_client)) - } -} - -impl JsonRpcClient for MainNodeClient { - async fn get_root_hash(&self, batch_number: L1BatchNumber) -> Result { - self.0 - .get_l1_batch_details(batch_number) - .rpc_context("get_l1_batch_details") - .await? - .and_then(|res| res.base.root_hash) - .ok_or_else(|| anyhow!("No root hash found for batch #{}", batch_number)) - } -} diff --git a/bin/verify-era-proof-attestation/src/client/http.rs b/bin/verify-era-proof-attestation/src/client/http.rs new file mode 100644 index 0000000..c23b9c7 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/client/http.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! HTTP client for making requests to external services + +use reqwest::Client; +use serde::{de::DeserializeOwned, Serialize}; +use std::time::Duration; +use url::Url; + +use crate::{ + core::DEFAULT_HTTP_REQUEST_TIMEOUT, + error::{Error, Result}, +}; + +/// Client for making HTTP requests +#[derive(Clone)] +pub struct HttpClient { + client: Client, +} + +impl HttpClient { + /// Create a new HTTP client with default configuration + pub fn new() -> Self { + let client = Client::builder() + .timeout(Duration::from_secs(DEFAULT_HTTP_REQUEST_TIMEOUT)) + .build() + .expect("Failed to create HTTP client"); + + Self { client } + } + + /// Make a POST request to the specified URL with the provided body + pub async fn post(&self, url: &Url, body: T) -> Result { + let response = self.client.post(url.clone()).json(&body).send().await?; + self.handle_response(response).await + } + + /// Send a JSON request and parse the response + pub async fn send_json( + &self, + url: &Url, + body: T, + ) -> Result { + let response_text = self.post(url, body).await?; + let response: R = serde_json::from_str(&response_text) + .map_err(|e| Error::JsonRpcInvalidResponse(e.to_string()))?; + + Ok(response) + } + + /// Handle the HTTP response + async fn handle_response(&self, response: reqwest::Response) -> Result { + let status = response.status(); + let body = response.text().await?; + + if status.is_success() { + Ok(body) + } else { + Err(Error::Http { + status_code: status.as_u16(), + message: body, + }) + } + } +} diff --git a/bin/verify-era-proof-attestation/src/client/json_rpc.rs b/bin/verify-era-proof-attestation/src/client/json_rpc.rs new file mode 100644 index 0000000..407b819 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/client/json_rpc.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use url::Url; +use zksync_basic_types::{L1BatchNumber, H256}; +use zksync_types::L2ChainId; +use zksync_web3_decl::{ + client::{Client as NodeClient, L2}, + error::ClientRpcContext, + namespaces::ZksNamespaceClient, +}; + +use crate::error; + +/// Trait for interacting with the JSON-RPC API +pub trait JsonRpcClient { + /// Get the root hash for a specific batch + async fn get_root_hash(&self, batch_number: L1BatchNumber) -> error::Result; + // TODO implement get_tee_proofs(batch_number, tee_type) once https://crates.io/crates/zksync_web3_decl crate is updated +} + +/// Client for interacting with the main node +pub struct MainNodeClient(NodeClient); + +impl MainNodeClient { + /// Create a new client for the main node + pub fn new(rpc_url: Url, chain_id: u64) -> error::Result { + let chain_id = L2ChainId::try_from(chain_id) + .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}")))? + .for_network(chain_id.into()) + .build(); + + Ok(MainNodeClient(node_client)) + } +} + +impl JsonRpcClient for MainNodeClient { + async fn get_root_hash(&self, batch_number: L1BatchNumber) -> error::Result { + let batch_details = self + .0 + .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}")))? + .ok_or_else(|| { + 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}")) + }) + } +} diff --git a/bin/verify-era-proof-attestation/src/client/mod.rs b/bin/verify-era-proof-attestation/src/client/mod.rs new file mode 100644 index 0000000..3c070e6 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/client/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Client modules for external API communication + +mod http; +mod json_rpc; +mod retry; + +pub use http::HttpClient; +pub use json_rpc::{JsonRpcClient, MainNodeClient}; +pub use retry::{RetryConfig, RetryHelper}; diff --git a/bin/verify-era-proof-attestation/src/client/retry.rs b/bin/verify-era-proof-attestation/src/client/retry.rs new file mode 100644 index 0000000..12c7855 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/client/retry.rs @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Retry mechanism for handling transient failures + +use std::time::Duration; +use tokio::time::sleep; + +use crate::{ + core::{DEFAULT_RETRY_DELAY_MS, MAX_PROOF_FETCH_RETRIES}, + error::{Error, Result}, +}; + +/// Configuration for retry behavior +#[derive(Debug, Clone)] +pub struct RetryConfig { + /// Maximum number of retry attempts + pub max_attempts: u32, + /// Delay between retry attempts + pub delay: Duration, + /// Whether to use exponential backoff + pub use_exponential_backoff: bool, +} + +impl Default for RetryConfig { + fn default() -> Self { + Self { + max_attempts: MAX_PROOF_FETCH_RETRIES, + delay: Duration::from_millis(DEFAULT_RETRY_DELAY_MS), + use_exponential_backoff: true, + } + } +} + +/// Helper for executing operations with retries +pub struct RetryHelper { + config: RetryConfig, +} + +impl RetryHelper { + /// Create a new retry helper with the given configuration + pub fn new(config: RetryConfig) -> Self { + Self { config } + } + + /// Execute an operation with retries + pub async fn execute(&self, operation_name: &str, operation: F) -> Result + where + F: Fn() -> Fut, + Fut: std::future::Future>, + { + let mut attempt = 0; + let mut last_error; + + loop { + attempt += 1; + tracing::debug!( + "Executing operation '{}' (attempt {}/{})", + operation_name, + attempt, + self.config.max_attempts + ); + + match operation().await { + Ok(result) => { + tracing::debug!( + "Operation '{}' succeeded on attempt {}", + operation_name, + attempt + ); + return Ok(result); + } + Err(Error::Interrupted) => return Err(Error::Interrupted), + Err(e) => { + last_error = e; + + if attempt >= self.config.max_attempts { + tracing::warn!( + "Operation '{}' failed after {} attempts. Giving up.", + operation_name, + attempt + ); + break; + } + + let delay = if self.config.use_exponential_backoff { + self.config.delay.mul_f32(2.0_f32.powi(attempt as i32 - 1)) + } else { + self.config.delay + }; + + tracing::warn!( + "Operation '{}' failed on attempt {}: {}. Retrying in {:?}...", + operation_name, + attempt, + last_error, + delay + ); + + sleep(delay).await; + } + } + } + + Err(last_error) + } +} diff --git a/bin/verify-era-proof-attestation/src/core/config.rs b/bin/verify-era-proof-attestation/src/core/config.rs new file mode 100644 index 0000000..c6f219b --- /dev/null +++ b/bin/verify-era-proof-attestation/src/core/config.rs @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Configuration settings for the verification process + +use crate::{ + core::{SGX_HASH_SIZE, TDX_HASH_SIZE}, + error, +}; +use bytes::{Bytes, BytesMut}; +use clap::{ArgGroup, Parser}; +use enumset::EnumSet; +use serde::{Deserialize, Serialize}; +use std::{collections::HashSet, fs, ops::Deref, path::PathBuf, str::FromStr, time::Duration}; +use teepot::{log::LogLevelParser, quote::tcblevel::TcbLevel}; +use tracing_subscriber::filter::LevelFilter; +use url::Url; +use zksync_basic_types::{tee_types::TeeType, L1BatchNumber}; +use zksync_types::L2ChainId; + +/// Primary configuration for the verification process +#[derive(Parser, Debug, Clone)] +#[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None +)] +#[clap(group( + ArgGroup::new("mode") + .required(true) + .args(&["batch_range", "continuous"]), +))] +pub struct VerifierConfigArgs { + /// Log level for the log output. + /// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace` + #[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)] + pub log_level: LevelFilter, + + /// The batch number or range of batch numbers to verify the attestation and signature (e.g., + /// "42" or "42-45"). This option is mutually exclusive with the `--continuous` mode. + #[clap(short = 'n', long = "batch", value_parser = parse_batch_range)] + pub batch_range: Option<(L1BatchNumber, L1BatchNumber)>, + + /// Continuous mode: keep verifying new batches until interrupted. This option is mutually + /// exclusive with the `--batch` option. + #[clap(long, value_name = "FIRST_BATCH")] + pub continuous: Option, + + /// URL of the RPC server to query for the batch attestation and signature. + #[clap(long = "rpc")] + pub rpc_url: Url, + + /// Chain ID of the network to query. + #[clap(long = "chain", default_value_t = L2ChainId::default().as_u64())] + pub chain_id: u64, + + /// Rate limit between requests in milliseconds. + #[clap(long, default_value = "0", value_parser = parse_duration)] + pub rate_limit: Duration, + + /// Path to a YAML file containing attestation policy configuration. + /// This overrides any attestation policy settings provided via command line options. + #[clap(long = "attestation-policy-file")] + pub attestation_policy_file: Option, + + /// Comma separated list of Tee types to process + #[clap(long)] + pub tee_types: TeeTypes, +} + +/// Attestation policy implemented as a set of criteria that must be met by SGX attestation. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct SgxAttestationPolicyConfig { + /// List of allowed hex-encoded SGX mrsigners. Batch attestation must consist of + /// one of these mrsigners. If the list is empty, the mrsigner check is skipped. + #[serde(default)] + pub mrsigners: Option>, + + /// List of allowed hex-encoded SGX mrenclaves. Batch attestation must consist + /// of one of these mrenclaves. If the list is empty, the mrenclave check is skipped. + #[serde(default)] + pub mrenclaves: Option>, + + /// List of allowed SGX TCB levels. If the list is empty, the TCB level check is + /// skipped. Allowed values: Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, + /// OutOfDate, OutOfDateConfigNeeded. + #[serde(default = "default_tcb_levels")] + pub allowed_tcb_levels: EnumSet, + + /// List of allowed SGX Advisories. If the list is empty, theAdvisories check is skipped. + #[serde(default)] + pub allowed_advisory_ids: Option>, +} + +/// Attestation policy implemented as a set of criteria that must be met by TDX attestation. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct TdxAttestationPolicyConfig { + /// List of allowed hex-encoded TDX mrs. Batch attestation must consist + /// of one of these mrs. If the list is empty, the mrs check is skipped. + #[serde(default)] + pub mrs: Option>, + + /// List of allowed SGX TCB levels. If the list is empty, the TCB level check is + /// skipped. Allowed values: Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, + /// OutOfDate, OutOfDateConfigNeeded. + #[serde(default = "default_tcb_levels")] + pub allowed_tcb_levels: EnumSet, + + /// List of allowed TDX Advisories. If the list is empty, theAdvisories check is skipped. + #[serde(default)] + pub allowed_advisory_ids: Option>, +} + +/// Attestation policy implemented as a set of criteria that must be met by SGX or TDX attestation. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct AttestationPolicyConfig { + /// SGX attestation policy + pub sgx: SgxAttestationPolicyConfig, + /// TDX attestation policy + pub tdx: TdxAttestationPolicyConfig, +} + +#[derive(Debug, Clone)] +pub struct AttestationPolicy { + pub sgx_mrsigners: Option>, + pub sgx_mrenclaves: Option>, + pub sgx_allowed_tcb_levels: EnumSet, + pub sgx_allowed_advisory_ids: Option>, + pub tdx_allowed_tcb_levels: EnumSet, + pub tdx_mrs: Option>, + pub tdx_allowed_advisory_ids: Option>, +} + +/// Default TCB levels used for Serde deserialization +fn default_tcb_levels() -> EnumSet { + let mut set = EnumSet::new(); + set.insert(TcbLevel::Ok); + set +} + +// TODO: +// When moving this binary to the `zksync-era` repo, we +// should be using `EnumSet` but this requires +// #[derive(EnumSetType, Debug, Serialize, Deserialize)] +// #[enumset(serialize_repr = "list")] +// for `TeeType` +#[derive(Clone, Debug)] +pub struct TeeTypes(HashSet); + +impl FromStr for TeeTypes { + type Err = error::Error; + + fn from_str(s: &str) -> Result { + let mut hs = HashSet::new(); + let tee_strs: Vec<&str> = s.split(',').collect(); + for tee_str in tee_strs { + match tee_str.to_ascii_lowercase().as_str() { + "sgx" => { + hs.insert(TeeType::Sgx); + } + "tdx" => { + hs.insert(TeeType::Tdx); + } + _ => { + return Err(error::Error::internal("Unknown TEE type")); + } + } + } + Ok(Self(hs)) + } +} +impl Default for TeeTypes { + fn default() -> Self { + Self(HashSet::from([TeeType::Sgx, TeeType::Tdx])) + } +} + +impl Deref for TeeTypes { + type Target = HashSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Clone)] +pub struct VerifierConfig { + pub args: VerifierConfigArgs, + pub policy: AttestationPolicy, +} + +impl VerifierConfig { + pub fn new(args: VerifierConfigArgs) -> error::Result { + 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}")) + })?; + + let policy_config: AttestationPolicyConfig = serde_yaml::from_str(&policy_content) + .map_err(|e| { + error::Error::internal(format!("Failed to parse attestation policy file: {e}")) + })?; + + tracing::info!("Loaded attestation policy from file: {:?}", path); + policy_config + } else { + AttestationPolicyConfig::default() + }; + + let policy = AttestationPolicy { + sgx_mrsigners: decode_hex_vec_option(policy.sgx.mrsigners, SGX_HASH_SIZE)?, + sgx_mrenclaves: decode_hex_vec_option(policy.sgx.mrenclaves, SGX_HASH_SIZE)?, + sgx_allowed_tcb_levels: policy.sgx.allowed_tcb_levels, + sgx_allowed_advisory_ids: policy.sgx.allowed_advisory_ids, + tdx_allowed_tcb_levels: policy.tdx.allowed_tcb_levels, + tdx_mrs: decode_tdx_mrs(policy.tdx.mrs, TDX_HASH_SIZE)?, + tdx_allowed_advisory_ids: policy.tdx.allowed_advisory_ids, + }; + + if policy.sgx_mrsigners.is_none() && policy.sgx_mrenclaves.is_none() { + tracing::error!( + "Neither `--sgx-mrenclaves` nor `--sgx-mrsigners` specified. Any code could have produced the SGX proof." + ); + } + + if policy.tdx_mrs.is_none() { + tracing::error!( + "`--tdxmrs` not specified. Any code could have produced the TDX proof." + ); + } + + Ok(Self { args, policy }) + } +} + +// Helper function to decode a vector of hex strings +fn decode_hex_vec_option( + hex_strings: Option>, + bytes_length: usize, +) -> Result>, hex::FromHexError> { + hex_strings + .map(|strings| { + strings + .into_iter() + .map(|s| { + if s.len() > (bytes_length * 2) { + return Err(hex::FromHexError::InvalidStringLength); + } + hex::decode(s).map(Bytes::from) + }) + .collect::, _>>() + }) + .transpose() +} + +// Improved decode_tdx_mrs function +fn decode_tdx_mrs( + tdx_mrs_opt: Option>, + bytes_length: usize, +) -> Result>, hex::FromHexError> { + match tdx_mrs_opt { + None => Ok(None), + Some(mrs_array) => { + let result = mrs_array + .into_iter() + .map(|strings| decode_and_combine_mrs(&strings, bytes_length)) + .collect::, _>>()?; + Ok(Some(result)) + } + } +} + +// Helper function to decode and combine MRs +fn decode_and_combine_mrs( + strings: &[String; 5], + bytes_length: usize, +) -> Result { + let mut buffer = BytesMut::with_capacity(bytes_length * 5); + + for s in strings { + if s.len() > (bytes_length * 2) { + return Err(hex::FromHexError::InvalidStringLength); + } + let decoded = hex::decode(s)?; + buffer.extend(decoded); + } + + Ok(buffer.freeze()) +} + +/// Parse a batch range from a string like "42" or "42-45" +fn parse_batch_range(s: &str) -> error::Result<(L1BatchNumber, L1BatchNumber)> { + let parse = |s: &str| { + s.parse::() + .map(L1BatchNumber::from) + .map_err(|e| error::Error::internal(format!("Can't convert batch {s} to number: {e}"))) + }; + 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)) + } +} + +/// Parse a duration from a millisecond string +fn parse_duration(s: &str) -> error::Result { + let millis = s + .parse() + .map_err(|e| error::Error::internal(format!("Can't convert {s} to duration: {e}")))?; + Ok(Duration::from_millis(millis)) +} + +#[cfg(test)] +mod test { + use super::*; + use std::{env, fs, path::PathBuf}; + use teepot::quote::tcblevel::TcbLevel; + + #[test] + fn test_load_attestation_policy_from_yaml() { + // Create a temporary directory for the test + let temp_dir = env::temp_dir().join("test_attestation_policy"); + fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); + + // Create a temporary YAML file + let yaml_path = temp_dir.join("policy.yaml"); + let yaml_content = r#" +sgx: + mrenclaves: + - a2caa7055e333f69c3e46ca7ba65b135a86c90adfde2afb356e05075b7818b3c + - 36eeb64cc816f80a1cf5818b26710f360714b987d3799e757cbefba7697b9589 + allowed_tcb_levels: + - Ok + - SwHardeningNeeded +tdx: + mrs: + - - 2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525 + - 3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f + - c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146 + - 092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945 + - 971fb52f90ec98a234301ca9b8fc30b613c33e3dd9c0cc42dcb8003d4a95d8fb218b75baf028b70a3cabcb947e1ca453 +"#; + fs::write(&yaml_path, yaml_content).expect("Failed to write YAML file"); + + // Create a minimal config + let config = VerifierConfig::new(VerifierConfigArgs { + log_level: LevelFilter::INFO, + batch_range: Some((L1BatchNumber(1), L1BatchNumber(10))), + continuous: None, + rpc_url: Url::parse("http://localhost:8545").unwrap(), + chain_id: 270, + rate_limit: Duration::from_millis(0), + attestation_policy_file: Some(yaml_path.clone()), + tee_types: Default::default(), + }) + .expect("Failed to load attestation policy"); + + // Verify that the attestation policy was loaded correctly + assert_eq!(config.policy.sgx_mrsigners, None); + assert_eq!( + config.policy.sgx_mrenclaves, + Some(vec![ + Bytes::from( + hex::decode("a2caa7055e333f69c3e46ca7ba65b135a86c90adfde2afb356e05075b7818b3c") + .unwrap(), + ), + Bytes::from( + hex::decode("36eeb64cc816f80a1cf5818b26710f360714b987d3799e757cbefba7697b9589") + .unwrap(), + ), + ]) + ); + assert!(config.policy.sgx_allowed_tcb_levels.contains(TcbLevel::Ok)); + assert!(config + .policy + .sgx_allowed_tcb_levels + .contains(TcbLevel::SwHardeningNeeded)); + assert_eq!( + config.policy.tdx_mrs, + Some(vec![Bytes::from( + hex::decode(concat!( + "2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525", + "3300980705adf09d28b707b79699d9874892164280832be2c386a715b6e204e0897fb564a064f810659207ba862b304f", + "c08ab64725566bcc8a6fb1c79e2e64744fcff1594b8f1f02d716fb66592ecd5de94933b2bc54ffbbc43a52aab7eb1146", + "092a4866a9e6a1672d7439a5d106fbc6eb57b738d5bfea5276d41afa2551824365fdd66700c1ce9c0b20542b9f9d5945", + "971fb52f90ec98a234301ca9b8fc30b613c33e3dd9c0cc42dcb8003d4a95d8fb218b75baf028b70a3cabcb947e1ca453" + )).unwrap()), + ]) + ); + + // Clean up + fs::remove_file(yaml_path).expect("Failed to remove temp YAML file"); + fs::remove_dir_all(temp_dir).expect("Failed to remove temp directory"); + } + + #[test] + fn test_invalid_yaml_file_path() { + // Create a minimal config with a non-existent YAML file path + let result = VerifierConfig::new(VerifierConfigArgs { + log_level: LevelFilter::INFO, + batch_range: Some((L1BatchNumber(1), L1BatchNumber(10))), + continuous: None, + rpc_url: Url::parse("http://localhost:8545").unwrap(), + chain_id: 270, + rate_limit: Duration::from_millis(0), + attestation_policy_file: Some(PathBuf::from("/non/existent/path.yaml")), + tee_types: Default::default(), + }); + assert!(result.is_err()); + } + + #[test] + fn test_invalid_yaml_content() { + // Create a temporary directory for the test + let temp_dir = env::temp_dir().join("test_invalid_yaml"); + fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); + + // Create a temporary YAML file with invalid content + let yaml_path = temp_dir.join("invalid_policy.yaml"); + let yaml_content = r#" +sgx_mrsigners: 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef +invalid_key: "some value" +allowed_tcb_levels: + - Invalid + - ConfigNeeded +"#; + fs::write(&yaml_path, yaml_content).expect("Failed to write YAML file"); + + // Create a minimal config + let result = VerifierConfig::new(VerifierConfigArgs { + log_level: LevelFilter::INFO, + batch_range: Some((L1BatchNumber(1), L1BatchNumber(10))), + continuous: None, + rpc_url: Url::parse("http://localhost:8545").unwrap(), + chain_id: 270, + rate_limit: Duration::from_millis(0), + attestation_policy_file: Some(yaml_path.clone()), + tee_types: Default::default(), + }); + assert!(result.is_err()); + + // Clean up + fs::remove_file(yaml_path).expect("Failed to remove temp YAML file"); + fs::remove_dir_all(temp_dir).expect("Failed to remove temp directory"); + } +} diff --git a/bin/verify-era-proof-attestation/src/core/constants.rs b/bin/verify-era-proof-attestation/src/core/constants.rs new file mode 100644 index 0000000..488f240 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/core/constants.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Constants used throughout the application + +/// Maximum number of retry attempts for fetching proofs +pub const MAX_PROOF_FETCH_RETRIES: u32 = 3; + +/// Default delay between retries (in milliseconds) +pub const DEFAULT_RETRY_DELAY_MS: u64 = 1000; + +/// Default timeout for HTTP requests (in seconds) +pub const DEFAULT_HTTP_REQUEST_TIMEOUT: u64 = 30; + +/// SGX hash size in bytes +pub const SGX_HASH_SIZE: usize = 32; + +/// TDX hash size in bytes +pub const TDX_HASH_SIZE: usize = 48; diff --git a/bin/verify-era-proof-attestation/src/core/mod.rs b/bin/verify-era-proof-attestation/src/core/mod.rs new file mode 100644 index 0000000..de76247 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/core/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Core components for Era proof attestation verification + +mod config; +mod constants; +mod types; + +pub use config::*; +pub use constants::*; +pub use types::*; diff --git a/bin/verify-era-proof-attestation/src/core/types.rs b/bin/verify-era-proof-attestation/src/core/types.rs new file mode 100644 index 0000000..e9d64b2 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/core/types.rs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Common type definitions used throughout the application + +use std::fmt; +use zksync_basic_types::L1BatchNumber; + +/// Represents the operating mode of the verifier +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VerifierMode { + /// Run on a single batch or range of batches and then exit + OneShot { + /// Starting batch number + start_batch: L1BatchNumber, + /// Ending batch number + end_batch: L1BatchNumber, + }, + /// Run continuously starting from a specific batch, until interrupted + Continuous { + /// Starting batch number + start_batch: L1BatchNumber, + }, +} + +impl fmt::Display for VerifierMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerifierMode::OneShot { + start_batch, + end_batch, + } => { + if start_batch == end_batch { + write!(f, "one-shot mode (batch {start_batch})") + } else { + write!(f, "one-shot mode (batches {start_batch}-{end_batch})") + } + } + VerifierMode::Continuous { start_batch } => { + write!(f, "continuous mode (starting from batch {start_batch})") + } + } + } +} + +/// Result of proof verification for a single batch +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VerificationResult { + /// All proofs for the batch were verified successfully + Success, + /// Some proofs for the batch failed verification + PartialSuccess { + /// Number of successfully verified proofs + verified_count: u32, + /// Number of proofs that failed verification + unverified_count: u32, + }, + /// No proofs for the batch were verified successfully + Failure, + /// Verification was interrupted before completion + Interrupted, + /// No proofs were found for the batch + NoProofsFound, +} + +impl VerificationResult { + /// Check if the majority of the proofs was verified successfully + pub fn is_successful(&self) -> bool { + match self { + VerificationResult::Success => true, + VerificationResult::PartialSuccess { + verified_count, + unverified_count, + } => verified_count > unverified_count, + VerificationResult::Failure + | VerificationResult::Interrupted + | VerificationResult::NoProofsFound => false, + } + } +} + +impl fmt::Display for VerificationResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerificationResult::Success => write!(f, "Success"), + VerificationResult::PartialSuccess { + verified_count, + unverified_count, + } => { + write!( + f, + "Partial Success ({verified_count} verified, {unverified_count} failed)" + ) + } + VerificationResult::Failure => write!(f, "Failure"), + VerificationResult::Interrupted => write!(f, "Interrupted"), + VerificationResult::NoProofsFound => write!(f, "No Proofs Found"), + } + } +} diff --git a/bin/verify-era-proof-attestation/src/error.rs b/bin/verify-era-proof-attestation/src/error.rs new file mode 100644 index 0000000..ec58c78 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/error.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Error types for the verification process + +use teepot::sgx::QuoteError; +use thiserror::Error; +use zksync_basic_types::L1BatchNumber; + +/// Result type used throughout the application +pub type Result = std::result::Result; + +/// Error types that can occur during verification +#[derive(Error, Debug)] +pub enum Error { + /// Error fetching proof + #[error("Failed to fetch proof for batch {batch_number}: {reason}")] + ProofFetch { + /// Batch number that caused the error + batch_number: L1BatchNumber, + /// Reason for the error + reason: String, + }, + + /// Error communicating with the HTTP server + #[error("HTTP request failed with status {status_code}: {message}")] + Http { + /// HTTP status code + status_code: u16, + /// Error message + message: String, + }, + + /// Error communicating with the JSON-RPC server + #[error("JSON-RPC error: {0}")] + JsonRpc(String), + + /// JSON-RPC response has an invalid format + #[error("JSON-RPC response has an invalid format")] + JsonRpcInvalidResponse(String), + + /// Invalid batch range + #[error("Invalid batch range: {0}")] + InvalidBatchRange(String), + + /// Error verifying attestation + #[error(transparent)] + AttestationVerification(#[from] QuoteError), + + /// Error verifying signature + #[error("Signature verification failed: {0}")] + SignatureVerification(String), + + /// Attestation policy violation + #[error("Attestation policy violation: {0}")] + PolicyViolation(String), + + /// Operation interrupted + #[error("Operation interrupted")] + Interrupted, + + #[error(transparent)] + FromHex(#[from] hex::FromHexError), + + /// Internal error + #[error("Internal error: {0}")] + Internal(String), +} + +/// Utility functions for working with errors +impl Error { + /// Create a new proof fetch error + pub fn proof_fetch(batch_number: L1BatchNumber, reason: impl Into) -> Self { + Self::ProofFetch { + batch_number, + reason: reason.into(), + } + } + + /// Create a new policy violation error + pub fn policy_violation(reason: impl Into) -> Self { + Self::PolicyViolation(reason.into()) + } + + /// Create a new signature verification error + pub fn signature_verification(reason: impl Into) -> Self { + Self::SignatureVerification(reason.into()) + } + + /// Create a new internal error + pub fn internal(reason: impl Into) -> Self { + Self::Internal(reason.into()) + } +} + +impl From for Error { + fn from(value: reqwest::Error) -> Self { + Self::Http { + status_code: value.status().map_or(0, |v| v.as_u16()), + message: value.to_string(), + } + } +} diff --git a/bin/verify-era-proof-attestation/src/main.rs b/bin/verify-era-proof-attestation/src/main.rs index cbb263a..9b45203 100644 --- a/bin/verify-era-proof-attestation/src/main.rs +++ b/bin/verify-era-proof-attestation/src/main.rs @@ -1,231 +1,94 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Tool for SGX attestation and batch signature verification, both continuous and one-shot -mod args; mod client; +mod core; +mod error; +mod processor; mod proof; mod verification; -use anyhow::{Context, Result}; -use args::{Arguments, AttestationPolicyArgs}; -use clap::Parser; -use client::MainNodeClient; -use proof::get_proofs; -use reqwest::Client; -use tokio::{signal, sync::watch}; -use tracing::{debug, error, info, trace, warn}; -use tracing_log::LogTracer; -use tracing_subscriber::{filter::LevelFilter, fmt, prelude::*, EnvFilter, Registry}; -use url::Url; -use zksync_basic_types::L1BatchNumber; - -use crate::verification::{ - log_quote_verification_summary, verify_attestation_quote, verify_batch_proof, +use crate::{ + core::{VerifierConfig, VerifierConfigArgs}, + error::Error, + processor::ProcessorFactory, }; +use clap::Parser; +use error::Result; +use tokio::signal; +use tokio_util::sync::CancellationToken; #[tokio::main] async fn main() -> Result<()> { - let args = Arguments::parse(); - setup_logging(&args.log_level)?; - validate_arguments(&args)?; - let (stop_sender, stop_receiver) = watch::channel(false); - let mut process_handle = tokio::spawn(verify_batches_proofs(stop_receiver, args)); - tokio::select! { - ret = &mut process_handle => { return ret?; }, - _ = signal::ctrl_c() => { - tracing::info!("Stop signal received, shutting down"); - stop_sender.send(true).ok(); - // Wait for process_batches to complete gracefully - process_handle.await??; - } - } + // Parse command-line arguments + let config = VerifierConfig::new(VerifierConfigArgs::parse())?; - Ok(()) -} + // Initialize logging + tracing::subscriber::set_global_default( + teepot::log::setup_logging(env!("CARGO_CRATE_NAME"), &config.args.log_level) + .map_err(|e| Error::internal(e.to_string()))?, + ) + .map_err(|e| Error::internal(e.to_string()))?; -fn setup_logging(log_level: &LevelFilter) -> Result<()> { - LogTracer::init().context("Failed to set logger")?; - let filter = EnvFilter::builder() - .try_from_env() - .unwrap_or(match *log_level { - LevelFilter::OFF => EnvFilter::new("off"), - _ => EnvFilter::new(format!( - "warn,{crate_name}={log_level},teepot={log_level}", - crate_name = env!("CARGO_CRATE_NAME"), - log_level = log_level - )), - }); - let subscriber = Registry::default() - .with(filter) - .with(fmt::layer().with_writer(std::io::stderr)); - tracing::subscriber::set_global_default(subscriber)?; + // Create processor based on config + let (processor, mode) = ProcessorFactory::create(config.clone())?; - Ok(()) -} + // Set up a cancellation Token + let token = CancellationToken::new(); -fn validate_arguments(args: &Arguments) -> Result<()> { - if args.attestation_policy.sgx_mrsigners.is_none() - && args.attestation_policy.sgx_mrenclaves.is_none() - { - error!("Neither `--sgx-mrenclaves` nor `--sgx-mrsigners` specified. Any code could have produced the proof."); - } + // Log startup information + tracing::info!("Starting verification in {}", mode); - Ok(()) -} - -/// Verify all TEE proofs for all batches starting from the given batch number up to the specified -/// batch number, if a range is provided. Otherwise, continue verifying batches until the stop -/// signal is received. -async fn verify_batches_proofs( - mut stop_receiver: watch::Receiver, - args: Arguments, -) -> Result<()> { - let node_client = MainNodeClient::new(args.rpc_url.clone(), args.chain_id)?; - let http_client = Client::new(); - let first_batch_number = match args.batch_range { - Some((first_batch_number, _)) => first_batch_number, - None => args - .continuous - .expect("clap::ArgGroup should guarantee batch range or continuous option is set"), + // Spawn processing task + let mut process_handle = { + let token = token.clone(); + tokio::spawn(async move { processor.run(token).await }) }; - let end_batch_number = args - .batch_range - .map_or(u32::MAX, |(_, end_batch_number)| end_batch_number.0); - let mut unverified_batches_count: u32 = 0; - let mut last_processed_batch_number = first_batch_number.0; - for current_batch_number in first_batch_number.0..=end_batch_number { - if *stop_receiver.borrow() { - tracing::warn!("Stop signal received, shutting down"); - break; + // Wait for processing to complete or for stop signal + tokio::select! { + result = &mut process_handle => { + match result { + Ok(Ok(verification_results)) => { + tracing::info!("Verification completed successfully"); + + let total_batches = verification_results.len(); + let successful_batches = verification_results.iter() + .filter(|(_, result)| result.is_successful()) + .count(); + + tracing::info!( + "Verified {} batches: {} succeeded, {} failed", + total_batches, + successful_batches, + total_batches - successful_batches + ); + + Ok(()) + }, + Ok(Err(e)) => { + tracing::error!("Verification failed: {}", e); + Err(e) + }, + Err(e) => { + tracing::error!("Task panicked: {}", e); + Err(Error::internal(format!("Task panicked: {e}"))) + } + } + }, + _ = signal::ctrl_c() => { + tracing::info!("Stop signal received, shutting down gracefully..."); + token.cancel(); + + // Wait for processor to complete gracefully + match process_handle.await { + Ok(_) => tracing::info!("Processor stopped gracefully"), + Err(e) => tracing::error!("Error stopping processor: {}", e), + } + + Ok(()) } - - trace!("Verifying TEE proofs for batch #{}", current_batch_number); - - let all_verified = verify_batch_proofs( - &mut stop_receiver, - current_batch_number.into(), - &args.rpc_url, - &http_client, - &node_client, - &args.attestation_policy, - ) - .await?; - - if !all_verified { - unverified_batches_count += 1; - } - - if current_batch_number < end_batch_number { - tokio::time::timeout(args.rate_limit, stop_receiver.changed()) - .await - .ok(); - } - - last_processed_batch_number = current_batch_number; } - - let verified_batches_count = - last_processed_batch_number + 1 - first_batch_number.0 - unverified_batches_count; - - if unverified_batches_count > 0 { - if verified_batches_count == 0 { - error!( - "All {} batches failed verification!", - unverified_batches_count - ); - } else { - error!( - "Some batches failed verification! Unverified batches: {}. Verified batches: {}.", - unverified_batches_count, verified_batches_count - ); - } - } else { - info!( - "All {} batches verified successfully!", - verified_batches_count - ); - } - - Ok(()) -} - -/// Verify all TEE proofs for the given batch number. Note that each batch number can potentially -/// have multiple proofs of the same TEE type. -async fn verify_batch_proofs( - stop_receiver: &mut watch::Receiver, - batch_number: L1BatchNumber, - rpc_url: &Url, - http_client: &Client, - node_client: &MainNodeClient, - attestation_policy: &AttestationPolicyArgs, -) -> Result { - let proofs = get_proofs(stop_receiver, batch_number, http_client, rpc_url).await?; - let batch_no = batch_number.0; - let mut total_proofs_count: u32 = 0; - let mut unverified_proofs_count: u32 = 0; - - for proof in proofs - .into_iter() - // only support SGX proofs for now - .filter(|proof| proof.tee_type.eq_ignore_ascii_case("sgx")) - { - let batch_no = proof.l1_batch_number; - - total_proofs_count += 1; - let tee_type = proof.tee_type.to_uppercase(); - - trace!(batch_no, tee_type, proof.proved_at, "Verifying proof."); - - debug!( - batch_no, - "Verifying quote ({} bytes)...", - proof.attestation.len() - ); - let quote_verification_result = verify_attestation_quote(&proof.attestation)?; - let verified_successfully = verify_batch_proof( - "e_verification_result, - attestation_policy, - node_client, - &proof.signature, - L1BatchNumber(proof.l1_batch_number), - ) - .await?; - - log_quote_verification_summary("e_verification_result); - - if verified_successfully { - info!( - batch_no, - proof.proved_at, tee_type, "Verification succeeded.", - ); - } else { - unverified_proofs_count += 1; - warn!(batch_no, proof.proved_at, tee_type, "Verification failed!",); - } - } - - let verified_proofs_count = total_proofs_count - unverified_proofs_count; - if unverified_proofs_count > 0 { - if verified_proofs_count == 0 { - error!( - batch_no, - "All {} proofs failed verification!", unverified_proofs_count - ); - } else { - warn!( - batch_no, - "Some proofs failed verification. Unverified proofs: {}. Verified proofs: {}.", - unverified_proofs_count, - verified_proofs_count - ); - } - } - - // if at least one proof is verified, consider the batch verified - let is_batch_verified = verified_proofs_count > 0; - - Ok(is_batch_verified) } diff --git a/bin/verify-era-proof-attestation/src/processor/batch_processor.rs b/bin/verify-era-proof-attestation/src/processor/batch_processor.rs new file mode 100644 index 0000000..ad0ae2c --- /dev/null +++ b/bin/verify-era-proof-attestation/src/processor/batch_processor.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Core functionality for processing individual batches + +use crate::error; +use tokio_util::sync::CancellationToken; +use zksync_basic_types::L1BatchNumber; + +use crate::{ + client::{HttpClient, MainNodeClient, RetryConfig}, + core::{VerificationResult, VerifierConfig}, + proof::ProofFetcher, + verification::{BatchVerifier, VerificationReporter}, +}; + +/// Responsible for processing individual batches +pub struct BatchProcessor { + config: VerifierConfig, + proof_fetcher: ProofFetcher, + batch_verifier: BatchVerifier, +} + +impl BatchProcessor { + /// Create a new batch processor with the given configuration + pub fn new(config: VerifierConfig) -> error::Result { + // Initialize clients and fetchers + let node_client = MainNodeClient::new(config.args.rpc_url.clone(), config.args.chain_id)?; + let http_client = HttpClient::new(); + let retry_config = RetryConfig::default(); + let proof_fetcher = + ProofFetcher::new(http_client, config.args.rpc_url.clone(), retry_config); + let batch_verifier = BatchVerifier::new(node_client, config.policy.clone()); + Ok(Self { + config, + proof_fetcher, + batch_verifier, + }) + } + + /// Process a single batch and return the verification result + pub async fn process_batch( + &self, + token: &CancellationToken, + batch_number: L1BatchNumber, + ) -> error::Result { + if token.is_cancelled() { + tracing::info!("Stop signal received, shutting down"); + return Ok(VerificationResult::Interrupted); + } + + tracing::trace!("Verifying TEE proofs for batch #{}", batch_number.0); + + // 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().copied() { + match self + .proof_fetcher + .get_proofs(token, batch_number, tee_type) + .await + { + Ok(batch_proofs) => proofs.extend(batch_proofs), + Err(error::Error::Interrupted) => return Err(error::Error::Interrupted), + Err(e) => { + tracing::error!( + "Failed to fetch proofs for TEE type {:?} at batch {}: {:#}", + tee_type, + batch_number.0, + e + ); + } + } + } + + if proofs.is_empty() { + tracing::warn!("No proofs found for batch #{}", batch_number.0); + return Ok(VerificationResult::NoProofsFound); + } + + // Verify proofs for the current batch + let verification_result = self + .batch_verifier + .verify_batch_proofs(token, batch_number, proofs) + .await?; + + let result = if verification_result.total_count == 0 { + VerificationResult::NoProofsFound + } else if verification_result.verified_count == verification_result.total_count { + VerificationResult::Success + } else if verification_result.verified_count > 0 { + VerificationResult::PartialSuccess { + verified_count: verification_result.verified_count, + unverified_count: verification_result.unverified_count, + } + } else { + VerificationResult::Failure + }; + + tracing::debug!("Batch #{} verification result: {}", batch_number.0, result); + + // Apply rate limiting between batches if needed + if !matches!(result, VerificationResult::Interrupted) + && self.config.args.rate_limit.as_millis() > 0 + { + tokio::time::timeout(self.config.args.rate_limit, token.cancelled()) + .await + .ok(); + } + + Ok(result) + } + + /// Log the overall verification results + pub fn log_overall_results(success_count: u32, failure_count: u32) { + VerificationReporter::log_overall_verification_results(success_count, failure_count); + } +} diff --git a/bin/verify-era-proof-attestation/src/processor/continuous_processor.rs b/bin/verify-era-proof-attestation/src/processor/continuous_processor.rs new file mode 100644 index 0000000..f9fb152 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/processor/continuous_processor.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Continuous batch processor for ongoing verification of new batches + +use tokio_util::sync::CancellationToken; +use zksync_basic_types::L1BatchNumber; + +use crate::{ + core::{VerificationResult, VerifierConfig}, + error, + processor::BatchProcessor, +}; + +/// Processes batches continuously until stopped +pub struct ContinuousProcessor { + batch_processor: BatchProcessor, + start_batch: L1BatchNumber, +} + +impl ContinuousProcessor { + /// Create a new continuous processor that starts from the given batch + pub fn new(config: VerifierConfig, start_batch: L1BatchNumber) -> error::Result { + let batch_processor = BatchProcessor::new(config)?; + + Ok(Self { + batch_processor, + start_batch, + }) + } + + /// Run the processor until stopped + pub async fn run( + &self, + token: &CancellationToken, + ) -> error::Result> { + tracing::info!( + "Starting continuous verification from batch {}", + self.start_batch.0 + ); + + let mut results = Vec::new(); + let mut success_count = 0; + let mut failure_count = 0; + let mut current_batch = self.start_batch.0; + + // Continue processing batches until stopped or reaching maximum batch number + while !token.is_cancelled() { + let batch = L1BatchNumber(current_batch); + match self.batch_processor.process_batch(token, batch).await { + Ok(result) => { + match result { + VerificationResult::Success | VerificationResult::PartialSuccess { .. } => { + success_count += 1; + } + VerificationResult::Failure => failure_count += 1, + VerificationResult::Interrupted => { + results.push((current_batch, result)); + break; + } + VerificationResult::NoProofsFound => { + // In continuous mode, we might hit batches that don't have proofs yet + // Wait a bit longer before retrying + if !token.is_cancelled() { + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + // Don't increment batch number, try again + continue; + } + } + } + + results.push((current_batch, result)); + } + Err(e) => { + tracing::error!("Error processing batch {}: {}", current_batch, e); + results.push((current_batch, VerificationResult::Failure)); + failure_count += 1; + } + } + + // Move to the next batch + current_batch = current_batch + .checked_add(1) + .ok_or(error::Error::internal("Maximum batch number reached"))?; + } + + // Log overall results + BatchProcessor::log_overall_results(success_count, failure_count); + + Ok(results) + } +} diff --git a/bin/verify-era-proof-attestation/src/processor/mod.rs b/bin/verify-era-proof-attestation/src/processor/mod.rs new file mode 100644 index 0000000..a2bac5b --- /dev/null +++ b/bin/verify-era-proof-attestation/src/processor/mod.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Processing logic for batch verification + +mod batch_processor; +mod continuous_processor; +mod one_shot_processor; + +pub use batch_processor::BatchProcessor; +pub use continuous_processor::ContinuousProcessor; +pub use one_shot_processor::OneShotProcessor; + +use crate::{ + core::{VerificationResult, VerifierConfig, VerifierMode}, + error::Result, +}; +use tokio_util::sync::CancellationToken; + +// Using an enum instead of a trait because async functions in traits can't be used in trait objects +/// Processor variants for different verification modes +pub enum ProcessorType { + /// One-shot processor for processing a specific range of batches + OneShot(OneShotProcessor), + /// Continuous processor for monitoring new batches + Continuous(ContinuousProcessor), +} + +impl ProcessorType { + /// Run the processor until completion or interruption + pub async fn run(&self, token: CancellationToken) -> Result> { + match self { + ProcessorType::OneShot(processor) => processor.run(&token).await, + ProcessorType::Continuous(processor) => processor.run(&token).await, + } + } +} + +/// Factory for creating the appropriate processor based on configuration +pub struct ProcessorFactory; + +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, 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, start)?; + let mode = VerifierMode::Continuous { start_batch: start }; + (ProcessorType::Continuous(processor), mode) + } else { + unreachable!("Clap ArgGroup should ensure either batch_range or continuous is set") + }; + + Ok(mode) + } +} diff --git a/bin/verify-era-proof-attestation/src/processor/one_shot_processor.rs b/bin/verify-era-proof-attestation/src/processor/one_shot_processor.rs new file mode 100644 index 0000000..994724b --- /dev/null +++ b/bin/verify-era-proof-attestation/src/processor/one_shot_processor.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! One-shot batch processor for verifying a single batch or a range of batches + +use crate::error; +use tokio_util::sync::CancellationToken; +use zksync_basic_types::L1BatchNumber; + +use crate::{ + core::{VerificationResult, VerifierConfig}, + processor::BatchProcessor, +}; + +/// Processes a specific range of batches and then exits +pub struct OneShotProcessor { + batch_processor: BatchProcessor, + start_batch: L1BatchNumber, + end_batch: L1BatchNumber, +} + +impl OneShotProcessor { + /// Create a new one-shot processor for the given batch range + pub fn new( + config: VerifierConfig, + start_batch: L1BatchNumber, + end_batch: L1BatchNumber, + ) -> error::Result { + let batch_processor = BatchProcessor::new(config)?; + + Ok(Self { + batch_processor, + start_batch, + end_batch, + }) + } + + /// Run the processor until completion or interruption + pub async fn run( + &self, + token: &CancellationToken, + ) -> error::Result> { + tracing::info!( + "Starting one-shot verification of batches {} to {}", + self.start_batch.0, + self.end_batch.0 + ); + + let mut results = Vec::new(); + let mut success_count = 0; + let mut failure_count = 0; + + for batch_number in self.start_batch.0..=self.end_batch.0 { + let batch = L1BatchNumber(batch_number); + let result = self.batch_processor.process_batch(token, batch).await?; + + match result { + VerificationResult::Success | VerificationResult::PartialSuccess { .. } => { + success_count += 1; + } + VerificationResult::Failure => failure_count += 1, + VerificationResult::Interrupted => { + results.push((batch_number, result)); + break; + } + VerificationResult::NoProofsFound => {} + } + + results.push((batch_number, result)); + } + + // Log overall results + BatchProcessor::log_overall_results(success_count, failure_count); + + Ok(results) + } +} diff --git a/bin/verify-era-proof-attestation/src/proof.rs b/bin/verify-era-proof-attestation/src/proof.rs deleted file mode 100644 index 6f1fdb7..0000000 --- a/bin/verify-era-proof-attestation/src/proof.rs +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs - -use anyhow::{bail, Result}; -use jsonrpsee_types::error::ErrorObject; -use reqwest::Client; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tokio::sync::watch; -use tracing::{error, warn}; -use url::Url; -use zksync_basic_types::L1BatchNumber; - -#[derive(Debug, Serialize, Deserialize)] -pub struct GetProofsRequest { - pub jsonrpc: String, - pub id: u32, - pub method: String, - pub params: (L1BatchNumber, String), -} - -pub async fn get_proofs( - stop_receiver: &mut watch::Receiver, - batch_number: L1BatchNumber, - http_client: &Client, - rpc_url: &Url, -) -> Result> { - let mut proofs_request = GetProofsRequest::new(batch_number); - let mut retries = 0; - let mut backoff = Duration::from_secs(1); - let max_backoff = Duration::from_secs(128); - let retry_backoff_multiplier: f32 = 2.0; - - while !*stop_receiver.borrow() { - let proofs = proofs_request - .send(stop_receiver, http_client, rpc_url) - .await?; - - if !proofs.is_empty() { - return Ok(proofs); - } - - retries += 1; - warn!( - batch_no = batch_number.0, retries, - "No TEE proofs found for batch #{}. They may not be ready yet. Retrying in {} milliseconds.", - batch_number, backoff.as_millis(), - ); - - tokio::time::timeout(backoff, stop_receiver.changed()) - .await - .ok(); - - backoff = std::cmp::min(backoff.mul_f32(retry_backoff_multiplier), max_backoff); - } - - Ok(vec![]) -} - -impl GetProofsRequest { - pub fn new(batch_number: L1BatchNumber) -> Self { - GetProofsRequest { - jsonrpc: "2.0".to_string(), - id: 1, - method: "unstable_getTeeProofs".to_string(), - params: (batch_number, "sgx".to_string()), - } - } - - pub async fn send( - &mut self, - stop_receiver: &mut watch::Receiver, - http_client: &Client, - rpc_url: &Url, - ) -> Result> { - let mut retries = 0; - let max_retries = 5; - let mut backoff = Duration::from_secs(1); - let max_backoff = Duration::from_secs(128); - let retry_backoff_multiplier: f32 = 2.0; - let mut response = None; - - while !*stop_receiver.borrow() { - let result = http_client - .post(rpc_url.clone()) - .json(self) - .send() - .await? - .error_for_status()? - .json::() - .await; - - match result { - Ok(res) => match res.error { - None => { - response = Some(res); - break; - } - Some(error) => { - // Handle corner case, where the old RPC interface expects 'Sgx' - if let Some(data) = error.data() { - if data.get().contains("unknown variant `sgx`, expected `Sgx`") { - self.params.1 = "Sgx".to_string(); - continue; - } - } - error!(?error, "received JSONRPC error {error:?}"); - bail!("JSONRPC error {error:?}"); - } - }, - Err(err) => { - retries += 1; - if retries >= max_retries { - return Err(anyhow::anyhow!( - "Failed to send request to {} after {} retries: {}. Request details: {:?}", - rpc_url, - max_retries, - err, - self - )); - } - warn!( - %err, - "Failed to send request to {rpc_url}. {retries}/{max_retries}, retrying in {} milliseconds. Request details: {:?}", - backoff.as_millis(), - self - ); - tokio::time::timeout(backoff, stop_receiver.changed()) - .await - .ok(); - backoff = std::cmp::min(backoff.mul_f32(retry_backoff_multiplier), max_backoff); - } - }; - } - - Ok(response.map_or_else(Vec::new, |res| res.result.unwrap_or_default())) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct GetProofsResponse { - pub jsonrpc: String, - pub result: Option>, - pub id: u32, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Proof { - pub l1_batch_number: u32, - pub tee_type: String, - pub pubkey: Vec, - pub signature: Vec, - pub proof: Vec, - pub proved_at: String, - pub attestation: Vec, -} diff --git a/bin/verify-era-proof-attestation/src/proof/fetcher.rs b/bin/verify-era-proof-attestation/src/proof/fetcher.rs new file mode 100644 index 0000000..5da8643 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/proof/fetcher.rs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use crate::{ + client::{HttpClient, RetryConfig, RetryHelper}, + error::{Error, Result}, + proof::{ + parsing::ProofResponseParser, + types::{GetProofsRequest, GetProofsResponse, Proof}, + }, +}; +use std::time::Duration; +use tokio_util::sync::CancellationToken; +use url::Url; +use zksync_basic_types::{tee_types::TeeType, L1BatchNumber}; + +/// Handles fetching proofs from the server with retry logic +pub struct ProofFetcher { + http_client: HttpClient, + rpc_url: Url, + retry_config: RetryConfig, +} + +impl ProofFetcher { + /// Create a new proof fetcher + pub fn new(http_client: HttpClient, rpc_url: Url, retry_config: RetryConfig) -> Self { + Self { + http_client, + rpc_url, + retry_config, + } + } + + /// Get proofs for a batch number with retry logic + pub async fn get_proofs( + &self, + token: &CancellationToken, + batch_number: L1BatchNumber, + tee_type: TeeType, + ) -> Result> { + let mut proofs_request = GetProofsRequest::new(batch_number, tee_type); + let mut backoff = Duration::from_secs(1); + let max_backoff = Duration::from_secs(128); + let retry_backoff_multiplier: f32 = 2.0; + + while !token.is_cancelled() { + match self.send_request(&proofs_request, token).await { + Ok(response) => { + // Parse the response using the ProofResponseParser + match ProofResponseParser::parse_response(response) { + Ok(proofs) => { + // Filter valid proofs + let valid_proofs = ProofResponseParser::filter_valid_proofs(&proofs); + + if !valid_proofs.is_empty() { + return Ok(valid_proofs); + } + + // No valid proofs found, retry + let error_msg = format!( + "No valid TEE proofs found for batch #{}. They may not be ready yet. Retrying in {} milliseconds.", + batch_number.0, + backoff.as_millis() + ); + tracing::warn!(batch_no = batch_number.0, "{}", error_msg); + // Here we could use the ProofFetching error if we needed to return immediately + // return Err(Error::ProofFetching(error_msg)); + } + Err(e) => { + // Handle specific error for Sgx variant + if let Error::JsonRpc(msg) = &e { + if msg.contains("RPC requires 'Sgx' variant") { + tracing::debug!("Switching to 'Sgx' variant for RPC"); + proofs_request.params.1 = "Sgx".to_string(); + continue; + } + } + return Err(e); + } + } + } + Err(e) => { + return Err(e); + } + } + + tokio::time::timeout(backoff, token.cancelled()).await.ok(); + + backoff = std::cmp::min( + Duration::from_millis( + (backoff.as_millis() as f32 * retry_backoff_multiplier) as u64, + ), + max_backoff, + ); + + if token.is_cancelled() { + break; + } + } + + // If we've reached this point, we've either been stopped or exhausted retries + if token.is_cancelled() { + // Return empty vector if stopped + Ok(vec![]) + } else { + // Use the ProofFetching error variant if we've exhausted retries + Err(Error::proof_fetch(batch_number, "exhausted retries")) + } + } + + /// Send a request to the server with retry logic + async fn send_request( + &self, + request: &GetProofsRequest, + token: &CancellationToken, + ) -> Result { + let retry_helper = RetryHelper::new(self.retry_config.clone()); + let request_clone = request.clone(); + let http_client = self.http_client.clone(); + let rpc_url = self.rpc_url.clone(); + + retry_helper + .execute(&format!("get_proofs_{}", request.params.0), || async { + let result = http_client + .send_json::<_, GetProofsResponse>(&rpc_url, &request_clone) + .await; + + // Check if we need to abort due to stop signal + if token.is_cancelled() { + return Err(Error::Interrupted); + } + + result + }) + .await + } +} diff --git a/bin/verify-era-proof-attestation/src/proof/mod.rs b/bin/verify-era-proof-attestation/src/proof/mod.rs new file mode 100644 index 0000000..46c125a --- /dev/null +++ b/bin/verify-era-proof-attestation/src/proof/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +mod fetcher; +mod parsing; +mod types; + +pub use fetcher::ProofFetcher; +pub use types::Proof; diff --git a/bin/verify-era-proof-attestation/src/proof/parsing.rs b/bin/verify-era-proof-attestation/src/proof/parsing.rs new file mode 100644 index 0000000..8611741 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/proof/parsing.rs @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use super::types::{GetProofsResponse, Proof}; +use crate::error; + +/// Handles parsing of proof responses and error handling +pub struct ProofResponseParser; + +impl ProofResponseParser { + /// Parse a response and extract the proofs + pub fn parse_response(response: GetProofsResponse) -> error::Result> { + // Handle JSON-RPC errors + if let Some(error) = response.error { + // Special case for handling the old RPC interface + if let Some(data) = error.data() { + if data.get().contains("unknown variant `sgx`, expected `Sgx`") { + return Err(error::Error::JsonRpc( + "RPC requires 'Sgx' variant instead of 'sgx'".to_string(), + )); + } + } + + return Err(error::Error::JsonRpc(format!("JSONRPC error: {error:?}"))); + } + + // Extract proofs from the result + Ok(response.result.unwrap_or_default()) + } + + /// Filter proofs to find valid ones + pub fn filter_valid_proofs(proofs: &[Proof]) -> Vec { + proofs + .iter() + .filter(|proof| !proof.is_failed_or_picked()) + .cloned() + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use jsonrpsee_types::error::ErrorObject; + + #[test] + fn test_proof_is_permanently_ignored() { + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("permanently_ignored".to_string()), + attestation: None, + }; + + assert!(proof.is_permanently_ignored()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("PERMANENTLY_IGNORED".to_string()), + attestation: None, + }; + + assert!(proof.is_permanently_ignored()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("other".to_string()), + attestation: None, + }; + + assert!(!proof.is_permanently_ignored()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: None, + attestation: None, + }; + + assert!(!proof.is_permanently_ignored()); + } + + #[test] + fn test_proof_is_failed_or_picked() { + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("failed".to_string()), + attestation: None, + }; + + assert!(proof.is_failed_or_picked()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("picked_by_prover".to_string()), + attestation: None, + }; + + assert!(proof.is_failed_or_picked()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("FAILED".to_string()), + attestation: None, + }; + + assert!(proof.is_failed_or_picked()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("other".to_string()), + attestation: None, + }; + + assert!(!proof.is_failed_or_picked()); + + let proof = Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: None, + attestation: None, + }; + + assert!(!proof.is_failed_or_picked()); + } + + #[test] + fn test_parse_response_success() { + let response = GetProofsResponse { + jsonrpc: "2.0".to_string(), + result: Some(vec![Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: None, + attestation: None, + }]), + id: 1, + error: None, + }; + + let proofs = ProofResponseParser::parse_response(response).unwrap(); + assert_eq!(proofs.len(), 1); + assert_eq!(proofs[0].l1_batch_number, 123); + } + + #[test] + fn test_parse_response_error() { + let response = GetProofsResponse { + jsonrpc: "2.0".to_string(), + result: None, + id: 1, + error: Some(ErrorObject::owned(1, "Error", None::<()>)), + }; + + let error = ProofResponseParser::parse_response(response).unwrap_err(); + match error { + error::Error::JsonRpc(msg) => { + assert!(msg.contains("JSONRPC error")); + } + _ => panic!("Expected JsonRpc error"), + } + } + + #[test] + fn test_parse_response_sgx_variant_error() { + let error_obj = ErrorObject::owned( + 1, + "Error", + Some( + serde_json::to_value("unknown variant `sgx`, expected `Sgx`") + .unwrap() + .to_string(), + ), + ); + + let response = GetProofsResponse { + jsonrpc: "2.0".to_string(), + result: None, + id: 1, + error: Some(error_obj), + }; + + let error = ProofResponseParser::parse_response(response).unwrap_err(); + match error { + error::Error::JsonRpc(msg) => { + assert!(msg.contains("RPC requires 'Sgx' variant")); + } + _ => panic!("Expected JsonRpc error about Sgx variant"), + } + } + + #[test] + fn test_filter_valid_proofs() { + let proofs = vec![ + Proof { + l1_batch_number: 123, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: None, + attestation: None, + }, + Proof { + l1_batch_number: 124, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("failed".to_string()), + attestation: None, + }, + Proof { + l1_batch_number: 125, + tee_type: "TDX".to_string(), + pubkey: None, + signature: None, + proof: None, + proved_at: "2023-01-01T00:00:00Z".to_string(), + status: Some("picked_by_prover".to_string()), + attestation: None, + }, + ]; + + let valid_proofs = ProofResponseParser::filter_valid_proofs(&proofs); + assert_eq!(valid_proofs.len(), 1); + assert_eq!(valid_proofs[0].l1_batch_number, 123); + } +} diff --git a/bin/verify-era-proof-attestation/src/proof/types.rs b/bin/verify-era-proof-attestation/src/proof/types.rs new file mode 100644 index 0000000..2957083 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/proof/types.rs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use jsonrpsee_types::error::ErrorObject; +use serde::{Deserialize, Serialize}; +use serde_with::{hex::Hex, serde_as}; +use zksync_basic_types::{tee_types::TeeType, L1BatchNumber}; + +/// Request structure for fetching proofs +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct GetProofsRequest { + pub jsonrpc: String, + pub id: u32, + pub method: String, + pub params: (L1BatchNumber, String), +} + +impl GetProofsRequest { + /// Create a new request for the given batch number + pub fn new(batch_number: L1BatchNumber, tee_type: TeeType) -> Self { + GetProofsRequest { + jsonrpc: "2.0".to_string(), + id: 1, + method: "unstable_getTeeProofs".to_string(), + params: (batch_number, tee_type.to_string()), + } + } +} + +/// Response structure for proof requests +#[derive(Debug, Serialize, Deserialize)] +pub struct GetProofsResponse { + pub jsonrpc: String, + pub result: Option>, + pub id: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option>, +} + +/// Proof structure containing attestation and signature data +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Proof { + pub l1_batch_number: u32, + pub tee_type: String, + #[serde_as(as = "Option")] + pub pubkey: Option>, + #[serde_as(as = "Option")] + pub signature: Option>, + #[serde_as(as = "Option")] + pub proof: Option>, + pub proved_at: String, + pub status: Option, + #[serde_as(as = "Option")] + pub attestation: Option>, +} + +impl Proof { + /// Check if the proof is marked as permanently ignored + pub fn is_permanently_ignored(&self) -> bool { + self.status + .as_ref() + .is_some_and(|s| s.eq_ignore_ascii_case("permanently_ignored")) + } + + /// Check if the proof is failed or picked by a prover + pub fn is_failed_or_picked(&self) -> bool { + self.status.as_ref().is_some_and(|s| { + s.eq_ignore_ascii_case("failed") || s.eq_ignore_ascii_case("picked_by_prover") + }) + } + + /// Get the attestation bytes or an empty vector if not present + pub fn attestation_bytes(&self) -> Vec { + self.attestation.clone().unwrap_or_default() + } + + /// Get the signature bytes or an empty vector if not present + pub fn signature_bytes(&self) -> Vec { + self.signature.clone().unwrap_or_default() + } +} diff --git a/bin/verify-era-proof-attestation/src/verification.rs b/bin/verify-era-proof-attestation/src/verification.rs deleted file mode 100644 index 18d381b..0000000 --- a/bin/verify-era-proof-attestation/src/verification.rs +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs - -use anyhow::{Context, Result}; -use hex::encode; -use secp256k1::{constants::PUBLIC_KEY_SIZE, ecdsa::Signature, Message, PublicKey}; -use teepot::{ - client::TcbLevel, - sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult}, -}; -use tracing::{debug, info, warn}; -use zksync_basic_types::{L1BatchNumber, H256}; - -use crate::args::AttestationPolicyArgs; -use crate::client::JsonRpcClient; - -pub async fn verify_batch_proof( - quote_verification_result: &QuoteVerificationResult<'_>, - attestation_policy: &AttestationPolicyArgs, - node_client: &impl JsonRpcClient, - signature: &[u8], - batch_number: L1BatchNumber, -) -> Result { - if !is_quote_matching_policy(attestation_policy, quote_verification_result) { - return Ok(false); - } - - let batch_no = batch_number.0; - - let public_key = PublicKey::from_slice( - "e_verification_result.quote.report_body.reportdata[..PUBLIC_KEY_SIZE], - )?; - debug!(batch_no, "public key: {}", public_key); - - let root_hash = node_client.get_root_hash(batch_number).await?; - debug!(batch_no, "root hash: {}", root_hash); - - let is_verified = verify_signature(signature, public_key, root_hash)?; - if is_verified { - info!(batch_no, signature = %encode(signature), "Signature verified successfully."); - } else { - warn!(batch_no, signature = %encode(signature), "Failed to verify signature!"); - } - Ok(is_verified) -} - -pub fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result { - let collateral = - tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral!")?; - let unix_time: i64 = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH)? - .as_secs() as _; - verify_quote_with_collateral(attestation_quote_bytes, Some(&collateral), unix_time) - .context("Failed to verify quote with collateral!") -} - -pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) { - let QuoteVerificationResult { - collateral_expired, - result, - quote, - advisories, - .. - } = quote_verification_result; - if *collateral_expired { - warn!("Freshly fetched collateral expired!"); - } - let tcblevel = TcbLevel::from(*result); - info!( - "Quote verification result: {}. mrsigner: {}, mrenclave: {}, reportdata: {}. Advisory IDs: {}.", - tcblevel, - hex::encode(quote.report_body.mrsigner), - hex::encode(quote.report_body.mrenclave), - hex::encode(quote.report_body.reportdata), - if advisories.is_empty() { - "None".to_string() - } else { - advisories.iter().map(ToString::to_string).collect::>().join(", ") - } - ); -} - -fn verify_signature(signature: &[u8], public_key: PublicKey, root_hash: H256) -> Result { - let signature = Signature::from_compact(signature)?; - let root_hash_msg = Message::from_digest_slice(&root_hash.0)?; - Ok(signature.verify(&root_hash_msg, &public_key).is_ok()) -} - -fn is_quote_matching_policy( - attestation_policy: &AttestationPolicyArgs, - quote_verification_result: &QuoteVerificationResult<'_>, -) -> bool { - let quote = "e_verification_result.quote; - let tcblevel = TcbLevel::from(quote_verification_result.result); - - if !attestation_policy.sgx_allowed_tcb_levels.contains(tcblevel) { - warn!( - "Quote verification failed: TCB level mismatch (expected one of: {:?}, actual: {})", - attestation_policy.sgx_allowed_tcb_levels, tcblevel - ); - return false; - } - - check_policy( - attestation_policy.sgx_mrsigners.as_deref(), - "e.report_body.mrsigner, - "mrsigner", - ) && check_policy( - attestation_policy.sgx_mrenclaves.as_deref(), - "e.report_body.mrenclave, - "mrenclave", - ) -} - -fn check_policy(policy: Option<&str>, actual_value: &[u8], field_name: &str) -> bool { - if let Some(valid_values) = policy { - let valid_values: Vec<&str> = valid_values.split(',').collect(); - let actual_value = hex::encode(actual_value); - if !valid_values.contains(&actual_value.as_str()) { - warn!( - "Quote verification failed: {} mismatch (expected one of: {:?}, actual: {})", - field_name, valid_values, actual_value - ); - return false; - } - debug!(field_name, actual_value, "Attestation policy check passed"); - } - true -} diff --git a/bin/verify-era-proof-attestation/src/verification/attestation.rs b/bin/verify-era-proof-attestation/src/verification/attestation.rs new file mode 100644 index 0000000..e37527f --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/attestation.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use teepot::quote::{get_collateral, verify_quote_with_collateral, QuoteVerificationResult}; + +use crate::error; + +/// Handles verification of attestation quotes +pub struct AttestationVerifier; + +impl AttestationVerifier { + /// Verify an attestation quote + pub fn verify_quote(attestation_quote_bytes: &[u8]) -> error::Result { + // Get collateral for the quote + let collateral = get_collateral(attestation_quote_bytes)?; + + // 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}")))? + .as_secs() as _; + + // Verify the quote with the collateral + let res = + verify_quote_with_collateral(attestation_quote_bytes, Some(&collateral), unix_time)?; + + Ok(res) + } +} diff --git a/bin/verify-era-proof-attestation/src/verification/batch.rs b/bin/verify-era-proof-attestation/src/verification/batch.rs new file mode 100644 index 0000000..db26815 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/batch.rs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use crate::{ + client::JsonRpcClient, + core::AttestationPolicy, + error, + proof::Proof, + verification::{AttestationVerifier, PolicyEnforcer, SignatureVerifier, VerificationReporter}, +}; +use tokio_util::sync::CancellationToken; +use zksync_basic_types::L1BatchNumber; + +/// Result of a batch verification +#[derive(Debug, Clone, Copy)] +pub struct BatchVerificationResult { + /// Total number of proofs processed + pub total_count: u32, + /// Number of proofs that were verified successfully + pub verified_count: u32, + /// Number of proofs that failed verification + pub unverified_count: u32, +} + +/// Handles the batch verification process +pub struct BatchVerifier { + node_client: C, + attestation_policy: AttestationPolicy, +} + +impl BatchVerifier { + /// Create a new batch verifier + pub fn new(node_client: C, attestation_policy: AttestationPolicy) -> Self { + Self { + node_client, + attestation_policy, + } + } + + /// Verify proofs for a batch + pub async fn verify_batch_proofs( + &self, + token: &CancellationToken, + batch_number: L1BatchNumber, + proofs: Vec, + ) -> error::Result { + let batch_no = batch_number.0; + let mut total_proofs_count: u32 = 0; + let mut verified_proofs_count: u32 = 0; + + for proof in proofs { + if token.is_cancelled() { + tracing::warn!("Stop signal received during batch verification"); + return Ok(BatchVerificationResult { + total_count: total_proofs_count, + verified_count: verified_proofs_count, + unverified_count: total_proofs_count - verified_proofs_count, + }); + } + + total_proofs_count += 1; + let tee_type = proof.tee_type.to_uppercase(); + + if proof.is_permanently_ignored() { + tracing::debug!( + batch_no, + tee_type, + "Proof is marked as permanently ignored. Skipping." + ); + continue; + } + + tracing::debug!(batch_no, tee_type, proof.proved_at, "Verifying proof."); + + let attestation_bytes = proof.attestation_bytes(); + let signature_bytes = proof.signature_bytes(); + + tracing::debug!( + batch_no, + "Verifying quote ({} bytes)...", + attestation_bytes.len() + ); + + // Verify attestation + let quote_verification_result = AttestationVerifier::verify_quote(&attestation_bytes)?; + + // Log verification results + VerificationReporter::log_quote_verification_summary("e_verification_result); + + // Check if attestation matches policy + let policy_matches = PolicyEnforcer::validate_policy( + &self.attestation_policy, + "e_verification_result, + ); + + if let Err(e) = policy_matches { + tracing::error!(batch_no, tee_type, "Attestation policy check failed: {e}"); + continue; + } + + // Verify signature + let root_hash = self + .node_client + .get_root_hash(L1BatchNumber(proof.l1_batch_number)) + .await?; + + let signature_verified = SignatureVerifier::verify_batch_proof( + "e_verification_result, + root_hash, + &signature_bytes, + )?; + + if signature_verified { + tracing::info!( + batch_no, + proof.proved_at, + tee_type, + "Verification succeeded.", + ); + verified_proofs_count += 1; + } else { + tracing::warn!(batch_no, proof.proved_at, tee_type, "Verification failed!"); + } + } + + let unverified_proofs_count = total_proofs_count.saturating_sub(verified_proofs_count); + + // Log batch verification results + VerificationReporter::log_batch_verification_results( + batch_no, + verified_proofs_count, + unverified_proofs_count, + ); + + Ok(BatchVerificationResult { + total_count: total_proofs_count, + verified_count: verified_proofs_count, + unverified_count: unverified_proofs_count, + }) + } +} diff --git a/bin/verify-era-proof-attestation/src/verification/mod.rs b/bin/verify-era-proof-attestation/src/verification/mod.rs new file mode 100644 index 0000000..1a8f19b --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/mod.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +mod attestation; +mod batch; +mod policy; +mod reporting; +mod signature; + +pub use attestation::AttestationVerifier; +pub use batch::BatchVerifier; +pub use policy::PolicyEnforcer; +pub use reporting::VerificationReporter; +pub use signature::SignatureVerifier; diff --git a/bin/verify-era-proof-attestation/src/verification/policy.rs b/bin/verify-era-proof-attestation/src/verification/policy.rs new file mode 100644 index 0000000..b283e81 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/policy.rs @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use crate::{ + core::AttestationPolicy, + error::{Error, Result}, +}; +use bytes::Bytes; +use enumset::EnumSet; +use teepot::quote::{tcblevel::TcbLevel, QuoteVerificationResult, Report}; + +/// Enforces policy requirements on attestation quotes +pub struct PolicyEnforcer; + +impl PolicyEnforcer { + /// Check if a quote matches the attestation policy + pub fn validate_policy( + attestation_policy: &AttestationPolicy, + quote_verification_result: &QuoteVerificationResult, + ) -> Result<()> { + let quote = "e_verification_result.quote; + let tcblevel = quote_verification_result.result; + + match "e.report { + Report::SgxEnclave(report_body) => { + // Validate TCB level + Self::validate_tcb_level(attestation_policy.sgx_allowed_tcb_levels, tcblevel)?; + + // Validate SGX Advisories + for advisory in "e_verification_result.advisories { + Self::check_policy( + attestation_policy.sgx_allowed_advisory_ids.as_deref(), + advisory, + "advisories", + )?; + } + + // Validate SGX policies + Self::check_policy_hash( + attestation_policy.sgx_mrsigners.as_deref(), + &report_body.mr_signer, + "mrsigner", + )?; + + Self::check_policy_hash( + attestation_policy.sgx_mrenclaves.as_deref(), + &report_body.mr_enclave, + "mrenclave", + ) + } + Report::TD10(report_body) => { + // Validate TCB level + Self::validate_tcb_level(attestation_policy.tdx_allowed_tcb_levels, tcblevel)?; + + // Validate TDX Advisories + for advisory in "e_verification_result.advisories { + Self::check_policy( + attestation_policy.tdx_allowed_advisory_ids.as_deref(), + advisory, + "mrsigner", + )?; + } + + // Build combined TDX MR and validate + let tdx_mr = Self::build_tdx_mr([ + &report_body.mr_td, + &report_body.rt_mr0, + &report_body.rt_mr1, + &report_body.rt_mr2, + &report_body.rt_mr3, + ]); + + Self::check_policy_hash(attestation_policy.tdx_mrs.as_deref(), &tdx_mr, "tdxmr") + } + Report::TD15(report_body) => { + // Validate TCB level + Self::validate_tcb_level(attestation_policy.tdx_allowed_tcb_levels, tcblevel)?; + + // Validate TDX Advisories + for advisory in "e_verification_result.advisories { + Self::check_policy( + attestation_policy.tdx_allowed_advisory_ids.as_deref(), + advisory, + "advisories", + )?; + } + + // Build combined TDX MR and validate + let tdx_mr = Self::build_tdx_mr([ + &report_body.base.mr_td, + &report_body.base.rt_mr0, + &report_body.base.rt_mr1, + &report_body.base.rt_mr2, + &report_body.base.rt_mr3, + ]); + + Self::check_policy_hash(attestation_policy.tdx_mrs.as_deref(), &tdx_mr, "tdxmr") + } + _ => Err(Error::policy_violation("Unknown quote report format")), + } + } + + /// Helper method to validate TCB levels + fn validate_tcb_level(allowed_levels: EnumSet, actual_level: TcbLevel) -> Result<()> { + if !allowed_levels.contains(actual_level) { + let error_msg = format!( + "Quote verification failed: TCB level mismatch (expected one of: {allowed_levels:?}, actual: {actual_level})", + ); + return Err(Error::policy_violation(error_msg)); + } + Ok(()) + } + + /// Helper method to build combined TDX measurement register + fn build_tdx_mr(parts: [&[u8]; N]) -> Vec { + parts.into_iter().flatten().copied().collect() + } + + /// Check if a policy value matches the actual value + fn check_policy(policy: Option<&[String]>, actual_value: &str, field_name: &str) -> Result<()> { + if let Some(valid_values) = policy { + if !valid_values.iter().any(|value| value == actual_value) { + let error_msg = + format!( + "Quote verification failed: {} mismatch (expected one of: [ {} ], actual: {})", + field_name, valid_values.join(", "), actual_value + ); + return Err(Error::policy_violation(error_msg)); + } + + tracing::debug!(field_name, actual_value, "Attestation policy check passed"); + } + + Ok(()) + } + + fn check_policy_hash( + policy: Option<&[Bytes]>, + actual_value: &[u8], + field_name: &str, + ) -> Result<()> { + if let Some(valid_values) = policy { + let actual_value = Bytes::copy_from_slice(actual_value); + if !valid_values.contains(&actual_value) { + let valid_values = valid_values + .iter() + .map(hex::encode) + .collect::>() + .join(", "); + let error_msg = format!( + "Quote verification failed: {field_name} mismatch (expected one of: [ {valid_values} ], actual: {actual_value:x})" + ); + return Err(Error::policy_violation(error_msg)); + } + + tracing::debug!( + field_name, + actual_value = format!("{actual_value:x}"), + "Attestation policy check passed" + ); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_check_policy() { + // Test with no policy (should pass) + PolicyEnforcer::check_policy_hash(None, &[1, 2, 3], "test").unwrap(); + + // Test with matching policy + let actual_value: Bytes = hex::decode("01020304").unwrap().into(); + PolicyEnforcer::check_policy_hash( + Some(vec![actual_value.clone()]).as_deref(), + &actual_value, + "test", + ) + .unwrap(); + + //.clone() Test with matching policy (multiple values) + PolicyEnforcer::check_policy_hash( + Some(vec![ + "aabbcc".into(), + "01020304".into(), + "ddeeff".into(), + actual_value.clone(), + ]) + .as_deref(), + &actual_value, + "test", + ) + .unwrap(); + + // Test with non-matching policy + PolicyEnforcer::check_policy_hash( + Some(vec!["aabbcc".into(), "ddeeff".into()]).as_deref(), + &actual_value, + "test", + ) + .unwrap_err(); + } +} diff --git a/bin/verify-era-proof-attestation/src/verification/reporting.rs b/bin/verify-era-proof-attestation/src/verification/reporting.rs new file mode 100644 index 0000000..638512d --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/reporting.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use teepot::quote::QuoteVerificationResult; + +/// Handles reporting and logging of verification results +pub struct VerificationReporter; + +impl VerificationReporter { + /// Log summary of a quote verification + pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) { + let QuoteVerificationResult { + collateral_expired, + result: tcblevel, + quote, + advisories, + .. + } = quote_verification_result; + + if *collateral_expired { + tracing::warn!("Freshly fetched collateral expired!"); + } + + let advisories = if advisories.is_empty() { + "None".to_string() + } else { + advisories + .iter() + .map(ToString::to_string) + .collect::>() + .join(", ") + }; + + tracing::debug!( + "Quote verification result: {tcblevel}. {report}. Advisory IDs: {advisories}.", + report = "e.report + ); + } + + /// Log the results of batch verification + pub fn log_batch_verification_results( + batch_no: u32, + verified_proofs_count: u32, + unverified_proofs_count: u32, + ) { + if unverified_proofs_count > 0 { + if verified_proofs_count == 0 { + tracing::error!( + batch_no, + "All {} proofs failed verification!", + unverified_proofs_count + ); + } else { + tracing::warn!( + batch_no, + "Some proofs failed verification. Unverified proofs: {}. Verified proofs: {}.", + unverified_proofs_count, + verified_proofs_count + ); + } + } else if verified_proofs_count > 0 { + tracing::info!( + batch_no, + "All {} proofs verified successfully!", + verified_proofs_count + ); + } + } + + /// Log overall verification results for multiple batches + pub fn log_overall_verification_results( + verified_batches_count: u32, + unverified_batches_count: u32, + ) { + if unverified_batches_count > 0 { + if verified_batches_count == 0 { + tracing::error!( + "All {} batches failed verification!", + unverified_batches_count + ); + } else { + tracing::error!( + "Some batches failed verification! Unverified batches: {}. Verified batches: {}.", + unverified_batches_count, + verified_batches_count + ); + } + } else { + tracing::info!("{} batches verified successfully!", verified_batches_count); + } + } +} diff --git a/bin/verify-era-proof-attestation/src/verification/signature.rs b/bin/verify-era-proof-attestation/src/verification/signature.rs new file mode 100644 index 0000000..ab34835 --- /dev/null +++ b/bin/verify-era-proof-attestation/src/verification/signature.rs @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +use secp256k1::{ + ecdsa::{RecoverableSignature, RecoveryId, Signature}, + Message, SECP256K1, +}; +use teepot::{ + ethereum::{public_key_to_ethereum_address, recover_signer}, + prover::reportdata::ReportData, + quote::QuoteVerificationResult, +}; +use zksync_basic_types::H256; + +use crate::error; + +const SIGNATURE_LENGTH_WITH_RECOVERY_ID: usize = 65; +const SIGNATURE_LENGTH_WITHOUT_RECOVERY_ID: usize = 64; + +/// Handles verification of signatures in proofs +pub struct SignatureVerifier; + +impl SignatureVerifier { + /// Verify a batch proof signature + pub fn verify_batch_proof( + quote_verification_result: &QuoteVerificationResult, + root_hash: H256, + signature: &[u8], + ) -> error::Result { + 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}")))?; + + Self::verify(&report_data, &root_hash, signature) + } + + /// Verify signature against report data and root hash + pub fn verify( + report_data: &ReportData, + root_hash: &H256, + signature: &[u8], + ) -> error::Result { + match report_data { + ReportData::V0(report) => Self::verify_v0(report, root_hash, signature), + ReportData::V1(report) => Self::verify_v1(report, root_hash, signature), + ReportData::Unknown(_) => Ok(false), + } + } + + /// Verify a V0 report + fn verify_v0( + report: &teepot::prover::reportdata::ReportDataV0, + root_hash: &H256, + signature: &[u8], + ) -> error::Result { + tracing::debug!("ReportData::V0"); + 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()) + } + + /// Verify a V1 report + fn verify_v1( + report: &teepot::prover::reportdata::ReportDataV1, + root_hash: &H256, + signature: &[u8], + ) -> error::Result { + tracing::debug!("ReportData::V1"); + let ethereum_address_from_report = report.ethereum_address; + + let root_hash_msg = Message::from_digest( + root_hash + .as_bytes() + .try_into() + .map_err(|_| error::Error::signature_verification("root hash not 32 bytes"))?, + ); + + tracing::trace!("sig len = {}", signature.len()); + + // Try to recover Ethereum address from signature + let ethereum_address_from_signature = match signature.len() { + // Handle 64-byte signature case (missing recovery ID) + SIGNATURE_LENGTH_WITHOUT_RECOVERY_ID => { + SignatureVerifier::recover_address_with_missing_recovery_id( + signature, + &root_hash_msg, + )? + } + // Standard 65-byte signature case + SIGNATURE_LENGTH_WITH_RECOVERY_ID => { + let signature_bytes: [u8; SIGNATURE_LENGTH_WITH_RECOVERY_ID] = + signature.try_into().map_err(|_| { + error::Error::signature_verification( + "Expected 65-byte signature but got a different length", + ) + })?; + + recover_signer(&signature_bytes, &root_hash_msg).map_err(|e| { + error::Error::signature_verification(format!("Failed to recover signer: {e}")) + })? + } + // Any other length is invalid + len => { + return Err(error::Error::signature_verification(format!( + "Invalid signature length: {len} bytes" + ))) + } + }; + + // Log verification details + tracing::debug!( + "Root hash: {}. Ethereum address from the attestation quote: {}. Ethereum address from the signature: {}.", + root_hash, + hex::encode(ethereum_address_from_report), + hex::encode(ethereum_address_from_signature), + ); + + Ok(ethereum_address_from_signature == ethereum_address_from_report) + } + + /// Helper function to recover Ethereum address when recovery ID is missing + fn recover_address_with_missing_recovery_id( + signature: &[u8], + message: &Message, + ) -> error::Result<[u8; 20]> { + tracing::info!("Signature is missing RecoveryId!"); + + // Try all possible recovery IDs + for rec_id in [ + RecoveryId::Zero, + RecoveryId::One, + RecoveryId::Two, + RecoveryId::Three, + ] { + let Ok(rec_sig) = RecoverableSignature::from_compact(signature, rec_id) else { + continue; + }; + + let Ok(public) = SECP256K1.recover_ecdsa(*message, &rec_sig) else { + continue; + }; + + let ethereum_address = public_key_to_ethereum_address(&public); + tracing::info!("Had to use RecoveryId::{rec_id:?}"); + return Ok(ethereum_address); + } + + // No valid recovery ID found + Err(error::Error::signature_verification( + "Could not find valid recovery ID", + )) + } +} diff --git a/checks/cargoClippy/default.nix b/checks/cargoClippy/default.nix new file mode 100644 index 0000000..2b16ef7 --- /dev/null +++ b/checks/cargoClippy/default.nix @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Matter Labs +{ teepot }: teepot.teepot.passthru.craneLib.cargoClippy ( + teepot.teepot.passthru.commonArgs // { + pname = "teepot"; + inherit (teepot.teepot.passthru) cargoArtifacts; + } +) diff --git a/packages/cargoFmt/default.nix b/checks/cargoDeny/default.nix similarity index 50% rename from packages/cargoFmt/default.nix rename to checks/cargoDeny/default.nix index cc8e631..7f3cb73 100644 --- a/packages/cargoFmt/default.nix +++ b/checks/cargoDeny/default.nix @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2024 Matter Labs -{ teepotCrate }: teepotCrate.craneLib.cargoFmt ( - teepotCrate.commonArgs // { +{ teepot }: teepot.teepot.passthru.craneLib.cargoDeny ( + teepot.teepot.passthru.commonArgs // { pname = "teepot"; } ) diff --git a/packages/cargoDeny/default.nix b/checks/cargoFmt/default.nix similarity index 50% rename from packages/cargoDeny/default.nix rename to checks/cargoFmt/default.nix index e4c400a..4f60a7f 100644 --- a/packages/cargoDeny/default.nix +++ b/checks/cargoFmt/default.nix @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2024 Matter Labs -{ teepotCrate }: teepotCrate.craneLib.cargoDeny ( - teepotCrate.commonArgs // { +{ teepot }: teepot.teepot.passthru.craneLib.cargoFmt ( + teepot.teepot.passthru.commonArgs // { pname = "teepot"; } ) diff --git a/crates/intel-dcap-api/CLAUDE.md b/crates/intel-dcap-api/CLAUDE.md new file mode 100644 index 0000000..851b6ae --- /dev/null +++ b/crates/intel-dcap-api/CLAUDE.md @@ -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 diff --git a/crates/intel-dcap-api/Cargo.toml b/crates/intel-dcap-api/Cargo.toml new file mode 100644 index 0000000..03c93b5 --- /dev/null +++ b/crates/intel-dcap-api/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "intel-dcap-api" +description = "Intel DCAP API Client" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +keywords = ["sgx", "tdx", "intel", "attestation", "confidential"] +categories = ["api-bindings", "cryptography", "authentication"] + +[dependencies] +base64.workspace = true +percent-encoding.workspace = true +reqwest = { workspace = true, features = ["json"] } +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +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"] diff --git a/crates/intel-dcap-api/examples/common_usage.rs b/crates/intel-dcap-api/examples/common_usage.rs new file mode 100644 index 0000000..e276787 --- /dev/null +++ b/crates/intel-dcap-api/examples/common_usage.rs @@ -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> { + // 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(()) +} diff --git a/crates/intel-dcap-api/examples/example.rs b/crates/intel-dcap-api/examples/example.rs new file mode 100644 index 0000000..16c3523 --- /dev/null +++ b/crates/intel-dcap-api/examples/example.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use intel_dcap_api::{ + ApiClient, ApiVersion, CaType, CrlEncoding, EnclaveIdentityResponse, IntelApiError, + PckCrlResponse, PlatformFilter, TcbInfoResponse, +}; + +#[tokio::main] +async fn main() -> Result<(), IntelApiError> { + for api_version in [ApiVersion::V3, ApiVersion::V4] { + println!("Using API version: {}", api_version); + + let client = ApiClient::new_with_version(api_version)?; + + // Example: Get SGX TCB Info + let fmspc_example = "00606A000000"; // Example FMSPC from docs + match client.get_sgx_tcb_info(fmspc_example, None, None).await { + Ok(TcbInfoResponse { + tcb_info_json, + issuer_chain, + }) => println!( + "SGX TCB Info for {}:\n{}\nIssuer Chain: {}", + fmspc_example, tcb_info_json, issuer_chain + ), + Err(e) => eprintln!("Error getting SGX TCB info: {}", e), + } + + // Example: Get FMSPCs + match client.get_fmspcs(Some(PlatformFilter::E3)).await { + // Filter for E3 platform type [cite: 230] + Ok(fmspc_list) => println!("\nE3 FMSPCs:\n{}", fmspc_list), + Err(e) => eprintln!("Error getting FMSPCs: {}", e), + } + + // Example: Get SGX QE Identity + match client.get_sgx_qe_identity(None, None).await { + Ok(EnclaveIdentityResponse { + enclave_identity_json, + issuer_chain, + }) => { + println!( + "\nSGX QE Identity:\n{}\nIssuer Chain: {}", + enclave_identity_json, issuer_chain + ) + } + Err(e) => eprintln!("Error getting SGX QE Identity: {}", e), + } + + // Example: Get PCK CRL (Platform CA, PEM encoding) + match client + .get_pck_crl(CaType::Platform, Some(CrlEncoding::Pem)) + .await + { + // [cite: 118, 119] + Ok(PckCrlResponse { + crl_data, + issuer_chain, + }) => { + // Attempt to decode PEM for display, otherwise show byte count + match String::from_utf8(crl_data.clone()) { + Ok(pem_string) => println!( + "\nPlatform PCK CRL (PEM):\n{}\nIssuer Chain: {}", + pem_string, issuer_chain + ), + Err(_) => println!( + "\nPlatform PCK CRL ({} bytes, likely DER):\nIssuer Chain: {}", + crl_data.len(), + issuer_chain + ), + } + } + Err(e) => eprintln!("Error getting PCK CRL: {}", e), + } + } + + Ok(()) +} diff --git a/crates/intel-dcap-api/examples/fetch_test_data.rs b/crates/intel-dcap-api/examples/fetch_test_data.rs new file mode 100644 index 0000000..be32fdf --- /dev/null +++ b/crates/intel-dcap-api/examples/fetch_test_data.rs @@ -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> { + // 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 = Vec::new(); + let mut failures: Vec = 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(()) +} diff --git a/crates/intel-dcap-api/examples/get_pck_crl.rs b/crates/intel-dcap-api/examples/get_pck_crl.rs new file mode 100644 index 0000000..72d0820 --- /dev/null +++ b/crates/intel-dcap-api/examples/get_pck_crl.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use intel_dcap_api::{ApiClient, CaType, CrlEncoding, IntelApiError, PckCrlResponse}; +use x509_cert::{ + der::{oid::AssociatedOid, Decode, SliceReader}, + ext::pkix::{ + crl::dp::DistributionPoint, + name::{DistributionPointName, GeneralName}, + CrlDistributionPoints, + }, +}; + +#[tokio::main] +async fn main() -> Result<(), IntelApiError> { + let client = ApiClient::new()?; + + let PckCrlResponse { + crl_data, + issuer_chain, + } = client + .get_pck_crl(CaType::Platform, Some(CrlEncoding::Der)) + .await?; + + let certs = x509_cert::certificate::CertificateInner::< + x509_cert::certificate::Rfc5280 + >::load_pem_chain(issuer_chain.as_bytes()).map_err( + |_| IntelApiError::InvalidParameter("Could not load a PEM chain") + )?; + + for cert in certs { + println!("Issuer: {}", cert.tbs_certificate.issuer); + println!("Subject: {}", cert.tbs_certificate.subject); + println!("Serial Number: {}", cert.tbs_certificate.serial_number); + println!("Not Before: {}", cert.tbs_certificate.validity.not_before); + println!("Not After: {}", cert.tbs_certificate.validity.not_after); + + // Extract and print CRL distribution points + if let Some(extensions) = &cert.tbs_certificate.extensions { + for ext in extensions.iter() { + if ext.extn_id == CrlDistributionPoints::OID { + // Create a SliceReader from the byte slice + let mut reader = SliceReader::new(ext.extn_value.as_bytes()).map_err(|_| { + IntelApiError::InvalidParameter( + "Could not create reader from extension value", + ) + })?; + + // Now pass the reader to decode_value + if let Ok(dist_points) = Vec::::decode(&mut reader) { + for point in dist_points { + if let Some(DistributionPointName::FullName(names)) = + point.distribution_point + { + for name in names { + if let GeneralName::UniformResourceIdentifier(uri) = name { + let uri = uri.as_str(); + let crl_bytes = reqwest::get(uri).await?.bytes().await?; + println!("CRL bytes (hex): {}", hex::encode(&crl_bytes)); + } + } + } + } + } else { + println!("Could not decode CRL distribution points"); + } + } + } + } + } + + println!("CRL bytes (hex): {}", hex::encode(&crl_data)); + + Ok(()) +} diff --git a/crates/intel-dcap-api/examples/handle_rate_limit.rs b/crates/intel-dcap-api/examples/handle_rate_limit.rs new file mode 100644 index 0000000..def5af6 --- /dev/null +++ b/crates/intel-dcap-api/examples/handle_rate_limit.rs @@ -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> { + // 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(()) +} diff --git a/crates/intel-dcap-api/examples/integration_test.rs b/crates/intel-dcap-api/examples/integration_test.rs new file mode 100644 index 0000000..b88088f --- /dev/null +++ b/crates/intel-dcap-api/examples/integration_test.rs @@ -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> { + 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(()) +} diff --git a/crates/intel-dcap-api/specs/API spec V3.md b/crates/intel-dcap-api/specs/API spec V3.md new file mode 100644 index 0000000..59667cd --- /dev/null +++ b/crates/intel-dcap-api/specs/API spec V3.md @@ -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]. \ 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]. \ Request-ID: Random identifier[cite: 17]. \ 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]. \ 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]. \ Request-ID[cite: 36]. \ SGX-PCK-Certificate-Issuer-Chain: URL-encoded issuer chain[cite: 36]. \ SGX-TCBm: Hex-encoded CPUSVN and PCESVN[cite: 37]. \ SGX-FMSPC: Hex-encoded FMSPC[cite: 37]. \ SGX-PCK-Certificate-CA-Type: 'processor' or 'platform'[cite: 39]. \ Warning: Optional message[cite: 39]. | Operation successful[cite: 36]. | +| 400 | | Request-ID[cite: 39]. \ Warning[cite: 39]. | Invalid request parameters[cite: 39]. | +| 401 | | Request-ID[cite: 40]. \ Warning[cite: 40]. | Failed to authenticate or authorize the request[cite: 40]. | +| 404 | | Request-ID[cite: 40]. \ 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]. \ Warning[cite: 41]. | Internal server error occurred[cite: 41]. | +| 503 | | Request-ID[cite: 42]. \ 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]. \ Request-ID[cite: 65]. \ SGX-PCK-Certificate-Issuer-Chain: Issuer chain[cite: 66]. \ SGX-FMSPC[cite: 66]. \ SGX-PCK-Certificate-CA-Type[cite: 66]. \ Warning[cite: 66]. | Operation successful[cite: 65]. | +| 400 | | Request-ID[cite: 67]. \ Warning[cite: 67]. | Invalid request parameters[cite: 67]. | +| 401 | | Request-ID[cite: 68]. \ Warning[cite: 68]. | Failed to authenticate or authorize the request[cite: 68]. | +| 404 | | Request-ID[cite: 69]. \ 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]. \ Warning[cite: 70]. | Internal server error occurred[cite: 70]. | +| 503 | | Request-ID[cite: 70]. \ 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]. \ Request-ID[cite: 72]. \ SGX-PCK-CRL-Issuer-Chain: Issuer chain[cite: 72]. \ Warning[cite: 72]. | Operation successful[cite: 72]. | +| 400 | | Request-ID[cite: 72]. \ Warning[cite: 73]. | Invalid request parameters[cite: 72]. | +| 401 | | Request-ID[cite: 73]. \ Warning[cite: 73]. | Failed to authenticate or authorize[cite: 73]. | +| 500 | | Request-ID[cite: 73]. \ Warning[cite: 73]. | Internal server error occurred[cite: 73]. | +| 503 | | Request-ID[cite: 73]. \ 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]. \ Request-ID[cite: 96]. \ SGX-TCB-Info-Issuer-Chain: Issuer chain[cite: 96]. \ Warning[cite: 96]. | Operation successful[cite: 96]. | +| 400 | | Request-ID[cite: 96]. \ Warning[cite: 96]. | Invalid request (bad FMSPC or conflicting `update`/`tcbEvaluationDataNumber`)[cite: 96]. | +| 401 | | Request-ID[cite: 96]. \ Warning[cite: 96]. | Failed to authenticate or authorize[cite: 96]. | +| 404 | | Request-ID[cite: 96]. \ Warning[cite: 96]. | TCB info not found for FMSPC or `tcbEvaluationDataNumber`[cite: 96]. | +| 410 | | Request-ID[cite: 98]. \ Warning[cite: 98]. | TCB Information for `tcbEvaluationDataNumber` no longer available[cite: 98]. | +| 500 | | Request-ID[cite: 98]. \ Warning[cite: 98]. | Internal server error[cite: 98]. | +| 503 | | Request-ID[cite: 98]. \ 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]. \ Request-ID[cite: 122]. \ SGX-Enclave-Identity-Issuer-Chain: Issuer chain[cite: 122]. \ Warning[cite: 122]. | Operation successful[cite: 122]. | +| 400 | | Request-ID[cite: 122]. \ Warning[cite: 123]. | Invalid request (bad params or conflicting `update`/`tcbEvaluationDataNumber`)[cite: 122, 124]. | +| 401 | | Request-ID[cite: 123]. \ Warning[cite: 123]. | Failed to authenticate or authorize[cite: 123]. | +| 404 | | Request-ID[cite: 123]. \ Warning[cite: 123]. | QE identity not found for `tcbEvaluationDataNumber`[cite: 124]. | +| 410 | | Request-ID[cite: 124]. \ Warning[cite: 124]. | QEIdentity for `tcbEvaluationDataNumber` no longer available[cite: 124]. | +| 500 | | Request-ID[cite: 125]. \ Warning[cite: 125]. | Internal server error[cite: 125]. | +| 503 | | Request-ID[cite: 125]. \ 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]. + diff --git a/crates/intel-dcap-api/specs/API spec V4.md b/crates/intel-dcap-api/specs/API spec V4.md new file mode 100644 index 0000000..0112b31 --- /dev/null +++ b/crates/intel-dcap-api/specs/API spec V4.md @@ -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]. diff --git a/crates/intel-dcap-api/src/client/enclave_identity.rs b/crates/intel-dcap-api/src/client/enclave_identity.rs new file mode 100644 index 0000000..64e760b --- /dev/null +++ b/crates/intel-dcap-api/src/client/enclave_identity.rs @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Enclave Identity + +use super::ApiClient; // Import from parent module +use crate::{ + error::IntelApiError, + responses::EnclaveIdentityResponse, + types::{ApiVersion, UpdateType}, +}; + +impl ApiClient { + /// Retrieves the SGX QE Identity from the Intel API. + /// + /// Returns Enclave Identity JSON string (Appendix B) and Issuer Chain header. + /// Supports both v3 and v4. The `update` and `tcb_evaluation_data_number` + /// parameters are only valid in API v4. Returns the enclave identity JSON + /// and an issuer chain header. + /// + /// # Arguments + /// + /// * `update` - Optional [`UpdateType`] (v4 only). + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// An [`EnclaveIdentityResponse`] containing the JSON identity and issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails, if conflicting v4 parameters are used, + /// or if the desired identity resource is not found. + pub async fn get_sgx_qe_identity( + &self, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + self.get_sgx_enclave_identity("qe", update, tcb_evaluation_data_number) + .await + } + + /// Retrieves the TDX QE Identity from the Intel API (API v4 only). + /// + /// # Arguments + /// + /// * `update` - Optional [`UpdateType`] (v4 only). + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// An [`EnclaveIdentityResponse`] containing the JSON identity and issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used, + /// if conflicting parameters are provided, or if the identity resource is not found. + /// GET /tdx/certification/v4/qe/identity - V4 ONLY + pub async fn get_tdx_qe_identity( + &self, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + // Ensure V4 API + self.ensure_v4_api("get_tdx_qe_identity")?; + // Check conflicting parameters (only relevant for V4, checked inside helper) + self.check_conflicting_update_params(update, tcb_evaluation_data_number)?; + + let path = self.build_api_path("tdx", "qe", "identity")?; + let mut url = self.base_url.join(&path)?; + + if let Some(upd) = update { + url.query_pairs_mut() + .append_pair("update", &upd.to_string()); + } + if let Some(tedn) = tcb_evaluation_data_number { + url.query_pairs_mut() + .append_pair("tcbEvaluationDataNumber", &tedn.to_string()); + } + + let request_builder = self.client.get(url); + + // Special handling for 404/410 when tcbEvaluationDataNumber is specified + if let Some(tedn_val) = tcb_evaluation_data_number { + // Use the helper function to check status before proceeding + self.check_tcb_evaluation_status(&request_builder, tedn_val, "TDX QE Identity") + .await?; + // If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain + } + + // Fetch JSON and header (TDX only exists in V4) + let (enclave_identity_json, issuer_chain) = self + .fetch_json_with_issuer_chain( + request_builder, + "SGX-Enclave-Identity-Issuer-Chain", + None, + ) + .await?; + + Ok(EnclaveIdentityResponse { + enclave_identity_json, + issuer_chain, + }) + } + + /// Retrieves the SGX QVE Identity from the Intel API. + /// + /// Supports API v3 and v4. The `update` and `tcb_evaluation_data_number` parameters + /// are v4 only. Returns the QVE identity JSON and issuer chain. + /// + /// # Arguments + /// + /// * `update` - Optional [`UpdateType`] (v4 only). + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// An [`EnclaveIdentityResponse`] containing the QVE identity JSON and issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails, if conflicting parameters are used, + /// or if the identity resource is not found. + /// GET /sgx/certification/{v3,v4}/qve/identity + pub async fn get_sgx_qve_identity( + &self, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + self.get_sgx_enclave_identity("qve", update, tcb_evaluation_data_number) + .await + } + + /// Retrieves the SGX QAE Identity from the Intel API (API v4 only). + /// + /// # Arguments + /// + /// * `update` - Optional [`UpdateType`] (v4 only). + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// An [`EnclaveIdentityResponse`] containing the QAE identity JSON and issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used, + /// if conflicting parameters are provided, or if the QAE identity is not found. + /// GET /sgx/certification/v4/qae/identity - V4 ONLY + pub async fn get_sgx_qae_identity( + &self, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + // QAE endpoint requires V4 + if self.api_version != ApiVersion::V4 { + return Err(IntelApiError::UnsupportedApiVersion( + "QAE Identity endpoint requires API v4".to_string(), + )); + } + // Call the generic helper, it will handle V4 params and 404/410 checks + self.get_sgx_enclave_identity("qae", update, tcb_evaluation_data_number) + .await + } + + /// Retrieves generic SGX enclave identity (QE, QVE, QAE) data. + /// + /// # Arguments + /// + /// * `identity_path_segment` - String slice representing the identity path segment (e.g., "qe", "qve", "qae"). + /// * `update` - Optional [`UpdateType`] for API v4. + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number for API v4. + /// + /// # Returns + /// + /// An [`EnclaveIdentityResponse`] containing the JSON identity data and issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or the specified resource + /// is unavailable. + async fn get_sgx_enclave_identity( + &self, + identity_path_segment: &str, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + self.check_v4_only_param(update, "update")?; + self.check_v4_only_param(tcb_evaluation_data_number, "tcbEvaluationDataNumber")?; + self.check_conflicting_update_params(update, tcb_evaluation_data_number)?; + + let path = self.build_api_path("sgx", identity_path_segment, "identity")?; + let mut url = self.base_url.join(&path)?; + + if self.api_version == ApiVersion::V4 { + if let Some(upd) = update { + url.query_pairs_mut() + .append_pair("update", &upd.to_string()); + } + if let Some(tedn) = tcb_evaluation_data_number { + url.query_pairs_mut() + .append_pair("tcbEvaluationDataNumber", &tedn.to_string()); + } + } + + let request_builder = self.client.get(url); + + if self.api_version == ApiVersion::V4 { + if let Some(tedn_val) = tcb_evaluation_data_number { + let description = format!("SGX {} Identity", identity_path_segment.to_uppercase()); + self.check_tcb_evaluation_status(&request_builder, tedn_val, &description) + .await?; + } + } + + let (enclave_identity_json, issuer_chain) = self + .fetch_json_with_issuer_chain( + request_builder, + "SGX-Enclave-Identity-Issuer-Chain", + Some("SGX-Enclave-Identity-Issuer-Chain"), + ) + .await?; + + Ok(EnclaveIdentityResponse { + enclave_identity_json, + issuer_chain, + }) + } +} diff --git a/crates/intel-dcap-api/src/client/fmspc.rs b/crates/intel-dcap-api/src/client/fmspc.rs new file mode 100644 index 0000000..c515ac9 --- /dev/null +++ b/crates/intel-dcap-api/src/client/fmspc.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! FMSPCs & TCB Evaluation Data Numbers + +use super::ApiClient; // Import from parent module +use crate::{ + error::{check_status, IntelApiError}, + responses::TcbEvaluationDataNumbersResponse, + types::{ApiVersion, PlatformFilter}, + FmspcJsonResponse, +}; +use reqwest::StatusCode; + +impl ApiClient { + /// GET /sgx/certification/{v3,v4}/fmspcs + /// Retrieves a list of FMSPC values for SGX and TDX platforms (API v4 only). + /// + /// # Arguments + /// + /// * `platform_filter` - An optional filter specifying SGX or TDX platforms. + /// + /// # Returns + /// + /// Optional 'platform' filter. + /// A `String` containing the JSON array of objects, each containing `fmspc` and `platform`. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used or if the request fails. + pub async fn get_fmspcs( + &self, + platform_filter: Option, + ) -> Result { + if self.api_version == ApiVersion::V3 { + return Err(IntelApiError::UnsupportedApiVersion( + "API v4 only function".to_string(), + )); + } + let path = self.build_api_path("sgx", "", "fmspcs")?; + let mut url = self.base_url.join(&path)?; + + if let Some(pf) = platform_filter { + url.query_pairs_mut() + .append_pair("platform", &pf.to_string()); + } + + let request_builder = self.client.get(url); + let response = self.execute_with_retry(request_builder).await?; + let response = check_status(response, &[StatusCode::OK]).await?; + + let fmspcs_json = response.text().await?; + + Ok(fmspcs_json) + } + + /// GET /sgx/certification/v4/tcbevaluationdatanumbers - V4 ONLY + /// Retrieves the currently supported SGX TCB Evaluation Data Numbers (API v4 only). + /// + /// # Returns + /// + /// A [`TcbEvaluationDataNumbersResponse`] containing the JSON structure of TCB Evaluation + /// Data Numbers and an issuer chain header. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used or if the request fails. + pub async fn get_sgx_tcb_evaluation_data_numbers( + &self, + ) -> Result { + // Endpoint requires V4 + if self.api_version != ApiVersion::V4 { + return Err(IntelApiError::UnsupportedApiVersion( + "SGX TCB Evaluation Data Numbers endpoint requires API v4".to_string(), + )); + } + + let path = self.build_api_path("sgx", "", "tcbevaluationdatanumbers")?; + let url = self.base_url.join(&path)?; + let request_builder = self.client.get(url); + + let (tcb_evaluation_data_numbers_json, issuer_chain) = self + .fetch_json_with_issuer_chain( + request_builder, + "TCB-Evaluation-Data-Numbers-Issuer-Chain", + None, + ) + .await?; + + Ok(TcbEvaluationDataNumbersResponse { + tcb_evaluation_data_numbers_json, + issuer_chain, + }) + } + + /// GET /tdx/certification/v4/tcbevaluationdatanumbers - V4 ONLY + /// Retrieves the currently supported TDX TCB Evaluation Data Numbers (API v4 only). + /// + /// # Returns + /// + /// A [`TcbEvaluationDataNumbersResponse`] containing the JSON structure of TCB Evaluation + /// Data Numbers and an issuer chain header. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used or if the request fails. + pub async fn get_tdx_tcb_evaluation_data_numbers( + &self, + ) -> Result { + // Endpoint requires V4 + if self.api_version != ApiVersion::V4 { + return Err(IntelApiError::UnsupportedApiVersion( + "TDX TCB Evaluation Data Numbers endpoint requires API v4".to_string(), + )); + } + + let path = self.build_api_path("tdx", "", "tcbevaluationdatanumbers")?; + let url = self.base_url.join(&path)?; + let request_builder = self.client.get(url); + + let (tcb_evaluation_data_numbers_json, issuer_chain) = self + .fetch_json_with_issuer_chain( + request_builder, + "TCB-Evaluation-Data-Numbers-Issuer-Chain", + None, + ) + .await?; + + Ok(TcbEvaluationDataNumbersResponse { + tcb_evaluation_data_numbers_json, + issuer_chain, + }) + } +} diff --git a/crates/intel-dcap-api/src/client/helpers.rs b/crates/intel-dcap-api/src/client/helpers.rs new file mode 100644 index 0000000..d699a94 --- /dev/null +++ b/crates/intel-dcap-api/src/client/helpers.rs @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Internal helper methods + +use super::ApiClient; // Import from parent module +use crate::{ + error::{check_status, extract_api_error_details, IntelApiError}, + responses::{PckCertificateResponse, PckCertificatesResponse}, + types::{ApiVersion, UpdateType}, +}; +use percent_encoding::percent_decode_str; +use reqwest::{RequestBuilder, Response, StatusCode}; +use std::{io, time::Duration}; +use tokio::time::sleep; + +impl ApiClient { + /// Helper to construct API paths dynamically based on version and technology (SGX/TDX). + pub(super) fn build_api_path( + &self, + technology: &str, + service: &str, + endpoint: &str, + ) -> Result { + let api_segment = self.api_version.path_segment(); + + if technology == "tdx" && self.api_version == ApiVersion::V3 { + return Err(IntelApiError::UnsupportedApiVersion(format!( + "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("//", "/")); + } + + Ok( + format!("/{technology}/certification/{api_segment}/{service}/{endpoint}") + .replace("//", "/"), + ) + } + + /// Helper to add an optional header if the string is non-empty. + pub(super) fn maybe_add_header( + builder: RequestBuilder, + header_name: &'static str, + header_value: Option<&str>, + ) -> RequestBuilder { + match header_value { + Some(value) if !value.is_empty() => builder.header(header_name, value), + _ => builder, + } + } + + /// Helper to extract a required header string value, handling potential v3/v4 differences. + pub(super) fn get_required_header( + &self, + response: &Response, + v4_header_name: &'static str, + v3_header_name: Option<&'static str>, + ) -> Result { + let header_name = match self.api_version { + ApiVersion::V4 => v4_header_name, + ApiVersion::V3 => v3_header_name.unwrap_or(v4_header_name), + }; + let value = response + .headers() + .get(header_name) + .ok_or(IntelApiError::MissingOrInvalidHeader(header_name))? + .to_str() + .map_err(|e| IntelApiError::HeaderValueParse(header_name, e.to_string()))?; + + if value.contains('%') { + percent_decode_str(value) + .decode_utf8() + .map_err(|e| IntelApiError::HeaderValueParse(header_name, e.to_string())) + .map(|s| s.to_string()) + } else { + Ok(value.to_string()) + } + } + + /// Helper to execute a request that returns a single PCK certificate and associated headers. + pub(super) async fn fetch_pck_certificate( + &self, + request_builder: RequestBuilder, + ) -> Result { + let response = self.execute_with_retry(request_builder).await?; + let response = check_status(response, &[StatusCode::OK]).await?; + + let issuer_chain = self.get_required_header( + &response, + "SGX-PCK-Certificate-Issuer-Chain", + Some("SGX-PCK-Certificate-Issuer-Chain"), + )?; + let tcbm = self.get_required_header(&response, "SGX-TCBm", Some("SGX-TCBm"))?; + let fmspc = self.get_required_header(&response, "SGX-FMSPC", Some("SGX-FMSPC"))?; + let pck_cert_pem = response.text().await?; + + Ok(PckCertificateResponse { + pck_cert_pem, + issuer_chain, + tcbm, + fmspc, + }) + } + + /// Helper to execute a request that returns a PCK certificates JSON array and associated headers. + pub(super) async fn fetch_pck_certificates( + &self, + request_builder: RequestBuilder, + ) -> Result { + let response = self.execute_with_retry(request_builder).await?; + let response = check_status(response, &[StatusCode::OK]).await?; + + let issuer_chain = self.get_required_header( + &response, + "SGX-PCK-Certificate-Issuer-Chain", + Some("SGX-PCK-Certificate-Issuer-Chain"), + )?; + let fmspc = self.get_required_header(&response, "SGX-FMSPC", Some("SGX-FMSPC"))?; + let pck_certs_json = response.text().await?; + + Ok(PckCertificatesResponse { + pck_certs_json, + issuer_chain, + fmspc, + }) + } + + /// Helper to execute a request expected to return JSON plus an Issuer-Chain header. + pub(super) async fn fetch_json_with_issuer_chain( + &self, + request_builder: RequestBuilder, + v4_issuer_chain_header: &'static str, + v3_issuer_chain_header: Option<&'static str>, + ) -> Result<(String, String), IntelApiError> { + let response = self.execute_with_retry(request_builder).await?; + let response = check_status(response, &[StatusCode::OK]).await?; + + let issuer_chain = + self.get_required_header(&response, v4_issuer_chain_header, v3_issuer_chain_header)?; + let json_body = response.text().await?; + + Ok((json_body, issuer_chain)) + } + + /// Checks for HTTP 404 or 410 status when querying TCB Evaluation Data Number based resources. + pub(super) async fn check_tcb_evaluation_status( + &self, + request_builder: &RequestBuilder, + tcb_evaluation_data_number_val: u64, + resource_description: &str, + ) -> Result<(), IntelApiError> { + let builder_clone = request_builder.try_clone().ok_or_else(|| { + IntelApiError::Io(io::Error::other( + "Failed to clone request builder for status check", + )) + })?; + + let response = self.execute_with_retry(builder_clone).await?; + let status = response.status(); + + if status == StatusCode::NOT_FOUND || status == StatusCode::GONE { + let (request_id, _, _) = extract_api_error_details(&response); + return Err(IntelApiError::ApiError { + status, + request_id, + error_code: None, + error_message: Some(format!( + "{} for TCB Evaluation Data Number {} {}", + resource_description, + tcb_evaluation_data_number_val, + if status == StatusCode::NOT_FOUND { + "not found" + } else { + "is no longer available" + } + )), + }); + } + Ok(()) + } + + /// Ensures the client is configured for API v4, otherwise returns an error. + pub(super) fn ensure_v4_api(&self, function_name: &str) -> Result<(), IntelApiError> { + if self.api_version != ApiVersion::V4 { + 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( + &self, + param_value: Option, + param_name: &str, + ) -> Result<(), IntelApiError> { + if self.api_version == ApiVersion::V3 && param_value.is_some() { + Err(IntelApiError::UnsupportedApiVersion(format!( + "'{param_name}' parameter requires API v4", + ))) + } else { + Ok(()) + } + } + + /// Checks for conflicting `update` and `tcb_evaluation_data_number` parameters when using V4. + pub(super) fn check_conflicting_update_params( + &self, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result<(), IntelApiError> { + if self.api_version == ApiVersion::V4 + && update.is_some() + && tcb_evaluation_data_number.is_some() + { + Err(IntelApiError::ConflictingParameters( + "'update' and 'tcbEvaluationDataNumber'", + )) + } else { + Ok(()) + } + } + + /// 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 { + 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::().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::().ok()) + .unwrap_or(60); // Default to 60 seconds + + // Wait before retrying + sleep(Duration::from_secs(retry_after_secs)).await; + retries += 1; + } + } +} diff --git a/crates/intel-dcap-api/src/client/mod.rs b/crates/intel-dcap-api/src/client/mod.rs new file mode 100644 index 0000000..3e71d5d --- /dev/null +++ b/crates/intel-dcap-api/src/client/mod.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +mod enclave_identity; +mod fmspc; +mod helpers; +mod pck_cert; +mod pck_crl; +mod registration; +mod tcb_info; + +use crate::{error::IntelApiError, types::ApiVersion}; +use reqwest::Client; +use url::Url; + +// Base URL for the Intel Trusted Services API +const BASE_URL: &str = "https://api.trustedservices.intel.com"; + +/// Client for interacting with Intel Trusted Services API. +/// +/// Provides methods to access both SGX and TDX certification services, +/// supporting API versions V3 and V4. This client offers functionality +/// to register platforms, retrieve PCK certificates and CRLs, fetch TCB +/// information, enclave identities, as well as TCB evaluation data numbers. +/// +/// # Examples +/// +/// ```rust,no_run +/// use intel_dcap_api::ApiClient; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// // Create a client with default settings (V4 API) +/// let client = ApiClient::new()?; +/// +/// // Retrieve TCB info for a specific FMSPC +/// let tcb_info = client.get_sgx_tcb_info("00606A000000", None, None).await?; +/// println!("TCB Info: {}", tcb_info.tcb_info_json); +/// +/// Ok(()) +/// } +/// ``` +#[derive(Clone)] +pub struct ApiClient { + client: Client, + base_url: Url, + api_version: ApiVersion, + /// Maximum number of automatic retries for rate-limited requests (429 responses) + max_retries: u32, +} + +impl ApiClient { + /// Creates a new client targeting the latest supported API version (V4). + /// + /// # Returns + /// + /// A result containing the newly created `ApiClient` or an `IntelApiError` if there + /// was an issue building the underlying HTTP client. + /// + /// # Errors + /// + /// This function may fail if the provided TLS version or base URL + /// cannot be used to build a `reqwest` client. + pub fn new() -> Result { + // Default to V4 + Self::new_with_options(BASE_URL, ApiVersion::V4) + } + + /// Creates a new client targeting a specific API version. + /// + /// # Arguments + /// + /// * `api_version` - The desired API version to use (V3 or V4). + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the `reqwest` client cannot be built + /// with the specified options. + pub fn new_with_version(api_version: ApiVersion) -> Result { + Self::new_with_options(BASE_URL, api_version) + } + + /// Creates a new client with a custom base URL, targeting the latest supported API version (V4). + /// + /// # Arguments + /// + /// * `base_url` - The custom base URL for the Intel Trusted Services API. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the `reqwest` client cannot be built + /// or if the provided base URL is invalid. + pub fn new_with_base_url(base_url: impl reqwest::IntoUrl) -> Result { + // Default to V4 + Self::new_with_options(base_url, ApiVersion::V4) + } + + /// Creates a new client with a custom base URL and specific API version. + /// + /// # Arguments + /// + /// * `base_url` - The custom base URL for the Intel Trusted Services API. + /// * `api_version` - The desired API version (V3 or V4). + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the `reqwest` client cannot be built + /// or if the provided base URL is invalid. + pub fn new_with_options( + base_url: impl reqwest::IntoUrl, + api_version: ApiVersion, + ) -> Result { + Ok(ApiClient { + client: Client::builder() + .min_tls_version(reqwest::tls::Version::TLS_1_2) + .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; + } +} diff --git a/crates/intel-dcap-api/src/client/pck_cert.rs b/crates/intel-dcap-api/src/client/pck_cert.rs new file mode 100644 index 0000000..fdc4842 --- /dev/null +++ b/crates/intel-dcap-api/src/client/pck_cert.rs @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Provisioning Certification Service + +use super::ApiClient; // Import from parent module +use crate::{ + error::IntelApiError, + requests::{PckCertRequest, PckCertsConfigRequest, PckCertsRequest}, + responses::{PckCertificateResponse, PckCertificatesResponse}, + types::ApiVersion, +}; +use reqwest::header; + +impl ApiClient { + /// GET /sgx/certification/{v3,v4}/pckcert + /// Retrieves a single SGX PCK certificate using encrypted PPID and SVNs. + /// + /// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter + /// is only valid for API v4 and allows specifying the PPID encryption key type (e.g. "RSA-3072"). + /// + /// # Arguments + /// + /// * `encrypted_ppid` - Hex-encoded encrypted PPID. + /// * `cpusvn` - Hex-encoded CPUSVN value. + /// * `pcesvn` - Hex-encoded PCESVN value. + /// * `pceid` - Hex-encoded PCEID value. + /// * `subscription_key` - Optional subscription key if the Intel API requires it. + /// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only). + /// + /// # Returns + /// + /// A [`PckCertificateResponse`] containing the PEM-encoded certificate, issuer chain, + /// TCBm, and FMSPC. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the API call fails or the response contains an invalid status. + /// Returns PEM Cert, Issuer Chain, TCBm, FMSPC. + pub async fn get_pck_certificate_by_ppid( + &self, + encrypted_ppid: &str, + cpusvn: &str, + pcesvn: &str, + pceid: &str, + subscription_key: Option<&str>, + ppid_encryption_key_type: Option<&str>, + ) -> Result { + // Check V4-only parameter + self.check_v4_only_param(ppid_encryption_key_type, "PPID-Encryption-Key")?; + + let path = self.build_api_path("sgx", "", "pckcert")?; // service is empty + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut() + .append_pair("encrypted_ppid", encrypted_ppid) + .append_pair("cpusvn", cpusvn) + .append_pair("pcesvn", pcesvn) + .append_pair("pceid", pceid); + + let mut request_builder = self.client.get(url); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + // Only add for V4 + if self.api_version == ApiVersion::V4 { + request_builder = Self::maybe_add_header( + request_builder, + "PPID-Encryption-Key", + ppid_encryption_key_type, + ); + } + + self.fetch_pck_certificate(request_builder).await + } + + /// POST /sgx/certification/{v3,v4}/pckcert + /// Retrieves a single SGX PCK certificate using a platform manifest and SVNs. + /// + /// Optionally requires a subscription key. + /// + /// # Arguments + /// + /// * `platform_manifest` - Hex-encoded platform manifest. + /// * `cpusvn` - Hex-encoded CPUSVN value. + /// * `pcesvn` - Hex-encoded PCESVN value. + /// * `pceid` - Hex-encoded PCEID value. + /// * `subscription_key` - Optional subscription key if the Intel API requires it. + /// + /// # Returns + /// + /// A [`PckCertificateResponse`] containing the PEM-encoded certificate, issuer chain, + /// TCBm, and FMSPC. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or if the response is invalid. + /// Returns PEM Cert, Issuer Chain, TCBm, FMSPC. + pub async fn get_pck_certificate_by_manifest( + &self, + platform_manifest: &str, + cpusvn: &str, + pcesvn: &str, + pceid: &str, + subscription_key: Option<&str>, + ) -> Result { + let path = self.build_api_path("sgx", "", "pckcert")?; + let url = self.base_url.join(&path)?; + let request_body = PckCertRequest { + platform_manifest, + cpusvn, + pcesvn, + pceid, + }; + + let mut request_builder = self + .client + .post(url) + .header(header::CONTENT_TYPE, "application/json") + .json(&request_body); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + self.fetch_pck_certificate(request_builder).await + } + + /// GET /sgx/certification/{v3,v4}/pckcerts + /// Retrieves all SGX PCK certificates for a platform using encrypted PPID. + /// + /// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter + /// is only valid for API v4. + /// + /// # Arguments + /// + /// * `encrypted_ppid` - Hex-encoded encrypted PPID. + /// * `pceid` - Hex-encoded PCEID value. + /// * `subscription_key` - Optional subscription key if the Intel API requires it. + /// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only). + /// + /// # Returns + /// + /// A [`PckCertificatesResponse`] containing JSON with `{tcb, tcbm, cert}` entries, + /// as well as the issuer chain and FMSPC headers. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the API call fails or the response status is invalid. + pub async fn get_pck_certificates_by_ppid( + &self, + encrypted_ppid: &str, + pceid: &str, + subscription_key: Option<&str>, + ppid_encryption_key_type: Option<&str>, + ) -> Result { + // Check V4-only parameter + self.check_v4_only_param(ppid_encryption_key_type, "PPID-Encryption-Key")?; + + let path = self.build_api_path("sgx", "", "pckcerts")?; + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut() + .append_pair("encrypted_ppid", encrypted_ppid) + .append_pair("pceid", pceid); + + let mut request_builder = self.client.get(url); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + // Only add for V4 + if self.api_version == ApiVersion::V4 { + request_builder = Self::maybe_add_header( + request_builder, + "PPID-Encryption-Key", + ppid_encryption_key_type, + ); + } + + self.fetch_pck_certificates(request_builder).await + } + + /// POST /sgx/certification/{v3,v4}/pckcerts + /// Retrieves all SGX PCK certificates for a platform using a platform manifest. + /// + /// Optionally requires a subscription key. + /// + /// # Arguments + /// + /// * `platform_manifest` - Hex-encoded platform manifest. + /// * `pceid` - Hex-encoded PCEID value. + /// * `subscription_key` - Optional subscription key if the Intel API requires it. + /// + /// # Returns + /// + /// A [`PckCertificatesResponse`] containing JSON with `{tcb, tcbm, cert}` entries, + /// as well as the issuer chain and FMSPC headers. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the API call fails or the response status is invalid. + pub async fn get_pck_certificates_by_manifest( + &self, + platform_manifest: &str, + pceid: &str, + subscription_key: Option<&str>, + ) -> Result { + let path = self.build_api_path("sgx", "", "pckcerts")?; + let url = self.base_url.join(&path)?; + let request_body = PckCertsRequest { + platform_manifest, + pceid, + }; + + let mut request_builder = self + .client + .post(url) + .header(header::CONTENT_TYPE, "application/json") + .json(&request_body); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + self.fetch_pck_certificates(request_builder).await + } + + /// GET /sgx/certification/{v3,v4}/pckcerts/config (using PPID) + /// Retrieves SGX PCK certificates for a specific configuration (CPUSVN) using encrypted PPID. + /// + /// Optionally requires a subscription key. The `ppid_encryption_key_type` parameter + /// is only valid for API v4. Returns JSON with `{tcb, tcbm, cert}` entries, + /// as well as the issuer chain and FMSPC headers. + /// + /// # Arguments + /// + /// * `encrypted_ppid` - Hex-encoded encrypted PPID. + /// * `pceid` - Hex-encoded PCEID value. + /// * `cpusvn` - Hex-encoded CPUSVN value for the requested configuration. + /// * `subscription_key` - Optional subscription key if the Intel API requires it. + /// * `ppid_encryption_key_type` - Optional PPID encryption key type (V4 only). + /// + /// # Returns + /// + /// A [`PckCertificatesResponse`] with the requested config's certificate data. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or if the response status + /// is not `200 OK`. + pub async fn get_pck_certificates_config_by_ppid( + &self, + encrypted_ppid: &str, + pceid: &str, + cpusvn: &str, + subscription_key: Option<&str>, + ppid_encryption_key_type: Option<&str>, + ) -> Result { + // V3 does not support PPID-Encryption-Key header/type + if self.api_version == ApiVersion::V3 && ppid_encryption_key_type.is_some() { + return Err(IntelApiError::UnsupportedApiVersion( + "PPID-Encryption-Key header is only supported in API v4".to_string(), + )); + } + + let path = self.build_api_path("sgx", "", "pckcerts/config")?; + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut() + .append_pair("encrypted_ppid", encrypted_ppid) + .append_pair("pceid", pceid) + .append_pair("cpusvn", cpusvn); + + let mut request_builder = self.client.get(url); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + // Only add for V4 + if self.api_version == ApiVersion::V4 { + request_builder = Self::maybe_add_header( + request_builder, + "PPID-Encryption-Key", + ppid_encryption_key_type, + ); + } + + self.fetch_pck_certificates(request_builder).await + } + + /// POST /sgx/certification/{v3,v4}/pckcerts/config (using Manifest) + /// Retrieves SGX PCK certificates for a specific configuration (CPUSVN) using a platform manifest. + /// + /// Optionally requires a subscription key. Returns JSON with `{tcb, tcbm, cert}` entries, + /// as well as the issuer chain and FMSPC headers. + /// + /// # Arguments + /// + /// * `platform_manifest` - Hex-encoded platform manifest. + /// * `pceid` - Hex-encoded PCEID value. + /// * `cpusvn` - Hex-encoded CPUSVN value for the requested configuration. + /// * `subscription_key` - Optional subscription key if needed by the Intel API. + /// + /// # Returns + /// + /// A [`PckCertificatesResponse`] with the requested config's certificate data. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or if the response status + /// is not `200 OK`. + pub async fn get_pck_certificates_config_by_manifest( + &self, + platform_manifest: &str, + pceid: &str, + cpusvn: &str, + subscription_key: Option<&str>, + ) -> Result { + let path = self.build_api_path("sgx", "", "pckcerts/config")?; + let url = self.base_url.join(&path)?; + let request_body = PckCertsConfigRequest { + platform_manifest, + pceid, + cpusvn, + }; + + let mut request_builder = self + .client + .post(url) + .header(header::CONTENT_TYPE, "application/json") + .json(&request_body); + + request_builder = Self::maybe_add_header( + request_builder, + "Ocp-Apim-Subscription-Key", + subscription_key, + ); + + self.fetch_pck_certificates(request_builder).await + } +} diff --git a/crates/intel-dcap-api/src/client/pck_crl.rs b/crates/intel-dcap-api/src/client/pck_crl.rs new file mode 100644 index 0000000..d5d0c6e --- /dev/null +++ b/crates/intel-dcap-api/src/client/pck_crl.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! PCK Certificate Revocation List + +use super::ApiClient; // Import from parent module +use crate::{ + error::{check_status, IntelApiError}, + responses::PckCrlResponse, + types::{CaType, CrlEncoding}, +}; +use reqwest::StatusCode; + +impl ApiClient { + /// GET /sgx/certification/{v3,v4}/pckcrl + /// Retrieves the PCK Certificate Revocation List (CRL) for a specified CA type. + /// + /// Optionally takes an `encoding` parameter indicating whether the CRL should be + /// returned as PEM or DER. Defaults to PEM if not specified. + /// + /// # Arguments + /// + /// * `ca_type` - The type of CA to retrieve the CRL for (e.g., "processor" or "platform"). + /// * `encoding` - An optional [`CrlEncoding`] (PEM or DER). + /// + /// # Returns + /// + /// A [`PckCrlResponse`] containing the CRL data and the issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or if the response status + /// is not `200 OK`. + /// Optional 'encoding' parameter ("pem" or "der"). + /// Returns CRL data (PEM or DER) and Issuer Chain header. + pub async fn get_pck_crl( + &self, + ca_type: CaType, + encoding: Option, + ) -> Result { + let path = self.build_api_path("sgx", "", "pckcrl")?; + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut() + .append_pair("ca", &ca_type.to_string()); + + if let Some(enc) = encoding { + url.query_pairs_mut() + .append_pair("encoding", &enc.to_string()); + } + + let request_builder = self.client.get(url); + let response = self.execute_with_retry(request_builder).await?; + let response = check_status(response, &[StatusCode::OK]).await?; + + let issuer_chain = self.get_required_header( + &response, + "SGX-PCK-CRL-Issuer-Chain", + Some("SGX-PCK-CRL-Issuer-Chain"), + )?; + + // Response body is PEM or DER CRL + let crl_data = response.bytes().await?.to_vec(); + + Ok(PckCrlResponse { + crl_data, + issuer_chain, + }) + } +} diff --git a/crates/intel-dcap-api/src/client/registration.rs b/crates/intel-dcap-api/src/client/registration.rs new file mode 100644 index 0000000..d510649 --- /dev/null +++ b/crates/intel-dcap-api/src/client/registration.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Registration + +use super::ApiClient; // Import from parent module +use crate::{ + error::{check_status, IntelApiError}, + responses::AddPackageResponse, +}; +use reqwest::{header, StatusCode}; +use std::num::ParseIntError; + +impl ApiClient { + /// POST /sgx/registration/v1/platform + /// Registers a multi-package SGX platform with the Intel Trusted Services API. + /// + /// # Arguments + /// + /// * `platform_manifest` - Binary data representing the platform manifest. + /// + /// # Returns + /// + /// Request body is binary Platform Manifest + /// Returns the hex-encoded PPID as a `String` upon success. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails or if the response status + /// is not HTTP `201 CREATED`. + pub async fn register_platform( + &self, + platform_manifest: Vec, + ) -> Result { + // Registration paths are fixed, use the helper with "registration" service + let path = self.build_api_path("sgx", "registration", "platform")?; + let url = self.base_url.join(&path)?; + + let request_builder = self + .client + .post(url) + .header(header::CONTENT_TYPE, "application/octet-stream") + .body(platform_manifest); + + let response = self.execute_with_retry(request_builder).await?; + + let response = check_status(response, &[StatusCode::CREATED]).await?; + + // Response body is hex-encoded PPID + let ppid_hex = response.text().await?; + Ok(ppid_hex) + } + + /// POST /sgx/registration/v1/package + /// Adds new package(s) to an already registered SGX platform instance. + /// + /// # Arguments + /// + /// * `add_package_request` - Binary data for the "Add Package" request body. + /// * `subscription_key` - The subscription key required by the Intel API. + /// + /// # Returns + /// + /// A [`AddPackageResponse`] containing the Platform Membership Certificates and + /// the count of them extracted from the response header. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the request fails, if the subscription key is invalid, + /// or if the response status is not HTTP `200 OK`. + pub async fn add_package( + &self, + add_package_request: Vec, + subscription_key: &str, + ) -> Result { + if subscription_key.is_empty() { + return Err(IntelApiError::InvalidSubscriptionKey); + } + + // Registration paths are fixed + let path = self.build_api_path("sgx", "registration", "package")?; + let url = self.base_url.join(&path)?; + + let request_builder = self + .client + .post(url) + .header("Ocp-Apim-Subscription-Key", subscription_key) + .header(header::CONTENT_TYPE, "application/octet-stream") + .body(add_package_request); + + let response = self.execute_with_retry(request_builder).await?; + + let response = check_status(response, &[StatusCode::OK]).await?; + + // Use the generic header helper, assuming header name is stable across reg versions + let cert_count_str = self.get_required_header(&response, "Certificate-Count", None)?; + let pck_cert_count: usize = cert_count_str.parse().map_err(|e: ParseIntError| { + IntelApiError::HeaderValueParse("Certificate-Count", e.to_string()) + })?; + + // Response body is a binary array of certificates + let pck_certs = response.bytes().await?.to_vec(); + Ok(AddPackageResponse { + pck_certs, + pck_cert_count, + }) + } +} diff --git a/crates/intel-dcap-api/src/client/tcb_info.rs b/crates/intel-dcap-api/src/client/tcb_info.rs new file mode 100644 index 0000000..9c001ca --- /dev/null +++ b/crates/intel-dcap-api/src/client/tcb_info.rs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! TCB Info + +use super::ApiClient; // Import from parent module +use crate::{ + error::IntelApiError, + responses::TcbInfoResponse, + types::{ApiVersion, UpdateType}, +}; + +impl ApiClient { + /// GET /sgx/certification/{v3,v4}/tcb + /// Retrieves SGX TCB information for a given FMSPC. + /// + /// Returns TCB Info JSON string (Appendix A) and Issuer Chain header. + /// This function supports both API v3 and v4. The `update` and `tcbEvaluationDataNumber` + /// parameters are only supported by API v4. If both are provided at the same time (for v4), + /// a conflict error is returned. + /// + /// # Arguments + /// + /// * `fmspc` - Hex-encoded FMSPC value. + /// * `update` - Optional [`UpdateType`] for API v4. + /// * `tcb_evaluation_data_number` - Optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// A [`TcbInfoResponse`] containing the TCB info JSON and the issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if the API request fails, if conflicting parameters are used, + /// or if the requested TCB data is not found. + pub async fn get_sgx_tcb_info( + &self, + fmspc: &str, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + // V3 does not support 'update' or 'tcbEvaluationDataNumber' + if self.api_version == ApiVersion::V3 && update.is_some() { + return Err(IntelApiError::UnsupportedApiVersion( + "'update' parameter requires API v4".to_string(), + )); + } + if self.api_version == ApiVersion::V3 && tcb_evaluation_data_number.is_some() { + return Err(IntelApiError::UnsupportedApiVersion( + "'tcbEvaluationDataNumber' parameter requires API v4".to_string(), + )); + } + if self.api_version == ApiVersion::V4 + && update.is_some() + && tcb_evaluation_data_number.is_some() + { + return Err(IntelApiError::ConflictingParameters( + "'update' and 'tcbEvaluationDataNumber'", + )); + } + + let path = self.build_api_path("sgx", "", "tcb")?; + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut().append_pair("fmspc", fmspc); + + // Add V4-specific parameters + if self.api_version == ApiVersion::V4 { + if let Some(upd) = update { + url.query_pairs_mut() + .append_pair("update", &upd.to_string()); + } + if let Some(tedn) = tcb_evaluation_data_number { + url.query_pairs_mut() + .append_pair("tcbEvaluationDataNumber", &tedn.to_string()); + } + } + + let request_builder = self.client.get(url); + + // Special handling for 404/410 when tcbEvaluationDataNumber is specified (V4 only) + if self.api_version == ApiVersion::V4 { + if let Some(tedn_val) = tcb_evaluation_data_number { + // Use the helper function to check status before proceeding + self.check_tcb_evaluation_status(&request_builder, tedn_val, "SGX TCB Info") + .await?; + // If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain + } + } + + // Fetch JSON and header (header name seems same for v3/v4) + let (tcb_info_json, issuer_chain) = self + .fetch_json_with_issuer_chain( + request_builder, + "TCB-Info-Issuer-Chain", + Some("SGX-TCB-Info-Issuer-Chain"), + ) + .await?; + + Ok(TcbInfoResponse { + tcb_info_json, + issuer_chain, + }) + } + + /// GET /tdx/certification/v4/tcb + /// Retrieves TDX TCB information for a given FMSPC (API v4 only). + /// + /// # Arguments + /// + /// * `fmspc` - Hex-encoded FMSPC value. + /// * `update` - An optional [`UpdateType`] (v4 only). + /// * `tcb_evaluation_data_number` - An optional TCB Evaluation Data Number (v4 only). + /// + /// # Returns + /// + /// A [`TcbInfoResponse`] containing TDX TCB info JSON and the issuer chain. + /// + /// # Errors + /// + /// Returns an `IntelApiError` if an unsupported API version is used, + /// if there are conflicting parameters, or if the TDX TCB data is not found. + /// Returns TCB Info JSON string (Appendix A) and Issuer Chain header. + pub async fn get_tdx_tcb_info( + &self, + fmspc: &str, + update: Option, + tcb_evaluation_data_number: Option, + ) -> Result { + // Ensure V4 API + self.ensure_v4_api("get_tdx_tcb_info")?; + // Check conflicting parameters (only relevant for V4, checked inside helper) + self.check_conflicting_update_params(update, tcb_evaluation_data_number)?; + + let path = self.build_api_path("tdx", "", "tcb")?; + let mut url = self.base_url.join(&path)?; + url.query_pairs_mut().append_pair("fmspc", fmspc); + + if let Some(upd) = update { + url.query_pairs_mut() + .append_pair("update", &upd.to_string()); + } + if let Some(tedn) = tcb_evaluation_data_number { + url.query_pairs_mut() + .append_pair("tcbEvaluationDataNumber", &tedn.to_string()); + } + + let request_builder = self.client.get(url); + + // Special handling for 404/410 when tcbEvaluationDataNumber is specified + if let Some(tedn_val) = tcb_evaluation_data_number { + // Use the helper function to check status before proceeding + self.check_tcb_evaluation_status(&request_builder, tedn_val, "TDX TCB Info") + .await?; + // If the check passes (doesn't return Err), continue to fetch_json_with_issuer_chain + } + + // Fetch JSON and header (TDX only exists in V4) + let (tcb_info_json, issuer_chain) = self + .fetch_json_with_issuer_chain(request_builder, "TCB-Info-Issuer-Chain", None) + .await?; + + Ok(TcbInfoResponse { + tcb_info_json, + issuer_chain, + }) + } +} diff --git a/crates/intel-dcap-api/src/error.rs b/crates/intel-dcap-api/src/error.rs new file mode 100644 index 0000000..4e07216 --- /dev/null +++ b/crates/intel-dcap-api/src/error.rs @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use reqwest::{Response, StatusCode}; +use thiserror::Error; + +/// Represents all possible errors that can occur when interacting with Intel's DCAP API. +#[derive(Error, Debug)] +pub enum IntelApiError { + /// Indicates that the requested API version or feature is unsupported. + #[error("Unsupported API version or feature: {0}")] + UnsupportedApiVersion(String), + + /// Wraps an underlying reqwest error. + #[error("Reqwest error: {0}")] + Reqwest(#[from] reqwest::Error), + + /// Wraps a URL parsing error. + #[error("URL parsing error: {0}")] + UrlParse(#[from] url::ParseError), + + /// Wraps a Serde JSON error. + #[error("Serde JSON error: {0}")] + JsonError(#[from] serde_json::Error), + + /// Represents a general API error, capturing the HTTP status and optional error details. + #[error("API Error: Status={status}, Request-ID={request_id}, Code={error_code:?}, Message={error_message:?}")] + ApiError { + /// HTTP status code returned by the API. + status: StatusCode, + /// The unique request identifier for tracing errors. + request_id: String, + /// An optional server-provided error code. + error_code: Option, + /// An optional server-provided error message. + error_message: Option, + }, + + /// Indicates that a header is missing or invalid. + #[error("Header missing or invalid: {0}")] + MissingOrInvalidHeader(&'static str), + + /// Represents an invalid subscription key. + #[error("Invalid Subscription Key format")] + InvalidSubscriptionKey, + + /// Indicates that conflicting parameters were supplied. + #[error("Cannot provide conflicting parameters: {0}")] + ConflictingParameters(&'static str), + + /// Wraps a standard I/O error. + #[error("I/O Error: {0}")] + Io(#[from] std::io::Error), + + /// Represents an error while parsing a header's value. + #[error("Header value parse error for '{0}': {1}")] + HeaderValueParse(&'static str, String), + + /// Indicates an invalid parameter was provided. + #[error("Invalid parameter value: {0}")] + InvalidParameter(&'static str), + + /// 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> { + /// 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. +pub(crate) fn extract_api_error_details( + response: &Response, +) -> (String, Option, Option) { + let request_id = response + .headers() + .get("Request-ID") + .and_then(|v| v.to_str().ok()) + .unwrap_or("Unknown") + .to_string(); + let error_code = response + .headers() + .get("Error-Code") + .and_then(|v| v.to_str().ok()) + .map(String::from); + let error_message = response + .headers() + .get("Error-Message") + .and_then(|v| v.to_str().ok()) + .map(String::from); + (request_id, error_code, error_message) +} + +/// Checks the response status and returns an ApiError if it's not one of the expected statuses. +pub(crate) async fn check_status( + response: Response, + expected_statuses: &[StatusCode], +) -> Result { + 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::().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 { + status, + request_id, + error_code, + error_message, + }) + } +} diff --git a/crates/intel-dcap-api/src/lib.rs b/crates/intel-dcap-api/src/lib.rs new file mode 100644 index 0000000..f6f4ea4 --- /dev/null +++ b/crates/intel-dcap-api/src/lib.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Intel API Client +//! +//! This module provides an API client for interacting with the Intel API for Trusted Services. +//! The API follows the documentation found at [Intel API Documentation](https://api.portal.trustedservices.intel.com/content/documentation.html). +//! +//! Create an [`ApiClient`] to interface with the Intel API. +//! +//! # 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}; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), IntelApiError> { +//! let client = ApiClient::new()?; +//! +//! // Example: Get SGX TCB Info +//! let fmspc_example = "00606A000000"; // Example FMSPC from docs +//! match client.get_sgx_tcb_info(fmspc_example, None, None).await { +//! Ok(TcbInfoResponse { +//! tcb_info_json, +//! issuer_chain, +//! }) => println!( +//! "SGX TCB Info for {}:\n{}\nIssuer Chain: {}", +//! fmspc_example, tcb_info_json, issuer_chain +//! ), +//! Err(e) => eprintln!("Error getting SGX TCB info: {}", e), +//! } +//! +//! Ok(()) +//! } +//! ``` + +#![deny(missing_docs)] +#![deny(clippy::all)] + +mod client; +mod error; +mod requests; +mod responses; +mod types; + +// Re-export public items +pub use client::ApiClient; +pub use error::IntelApiError; +pub use responses::{ + AddPackageResponse, EnclaveIdentityJson, EnclaveIdentityResponse, FmspcJsonResponse, + PckCertificateResponse, PckCertificatesResponse, PckCrlResponse, TcbEvaluationDataNumbersJson, + TcbEvaluationDataNumbersResponse, TcbInfoJson, TcbInfoResponse, +}; +pub use types::{ApiVersion, CaType, CrlEncoding, PlatformFilter, UpdateType}; diff --git a/crates/intel-dcap-api/src/requests.rs b/crates/intel-dcap-api/src/requests.rs new file mode 100644 index 0000000..3be562c --- /dev/null +++ b/crates/intel-dcap-api/src/requests.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use serde::Serialize; + +#[derive(Serialize)] +pub(crate) struct PckCertRequest<'a> { + #[serde(rename = "platformManifest")] + pub(crate) platform_manifest: &'a str, + pub(crate) cpusvn: &'a str, + pub(crate) pcesvn: &'a str, + pub(crate) pceid: &'a str, +} + +#[derive(Serialize)] +pub(crate) struct PckCertsRequest<'a> { + #[serde(rename = "platformManifest")] + pub(crate) platform_manifest: &'a str, + pub(crate) pceid: &'a str, +} + +#[derive(Serialize)] +pub(crate) struct PckCertsConfigRequest<'a> { + #[serde(rename = "platformManifest")] + pub(crate) platform_manifest: &'a str, + pub(crate) cpusvn: &'a str, + pub(crate) pceid: &'a str, +} diff --git a/crates/intel-dcap-api/src/responses.rs b/crates/intel-dcap-api/src/responses.rs new file mode 100644 index 0000000..29e0a37 --- /dev/null +++ b/crates/intel-dcap-api/src/responses.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +/// JSON structure as defined in Appendix A of the API spec. +/// Content may vary slightly between API v3 and v4. +pub type TcbInfoJson = String; + +/// JSON structure as defined in Appendix B of the API spec. +/// Content may vary slightly between API v3 and v4. +pub type EnclaveIdentityJson = String; + +/// JSON Array of {tcb, tcbm, cert}. +/// Content structure expected to be consistent between v3 and v4. +pub type PckCertsJsonResponse = String; + +/// JSON Array of {fmspc, platform}. +/// Content structure expected to be consistent between v3 and v4. +pub type FmspcJsonResponse = String; + +/// JSON structure as defined in Appendix C of the API spec (V4 ONLY). +pub type TcbEvaluationDataNumbersJson = String; + +/// Response structure for a PCK (Platform Configuration Key) Certificate. +/// +/// Contains the PCK certificate, its issuer chain, TCB measurement, and FMSPC value. +#[derive(Debug, Clone)] +pub struct PckCertificateResponse { + /// PEM-encoded PCK certificate. + pub pck_cert_pem: String, + /// PEM-encoded certificate chain for the PCK certificate issuer. + /// Header name differs between v3 ("PCS-Certificate-Issuer-Chain") and v4 ("SGX-PCK-Certificate-Issuer-Chain"). + pub issuer_chain: String, + /// TCBm value associated with the certificate (Hex-encoded). + pub tcbm: String, + /// FMSPC value associated with the certificate (Hex-encoded). + pub fmspc: String, +} + +/// Response structure for multiple PCK (Platform Configuration Key) Certificates. +/// +/// Contains a JSON array of PCK certificates, their issuer chain, and the associated FMSPC value. +/// This struct represents the response for retrieving multiple PCK certificates from the Intel SGX API. +#[derive(Debug, Clone)] +pub struct PckCertificatesResponse { + /// JSON array containing PCK certificates and their associated TCB levels. + pub pck_certs_json: PckCertsJsonResponse, // String alias for now + /// PEM-encoded certificate chain for the PCK certificate issuer. + /// Header name differs between v3 ("PCS-Certificate-Issuer-Chain") and v4 ("SGX-PCK-Certificate-Issuer-Chain"). + pub issuer_chain: String, + /// FMSPC value associated with the certificates (Hex-encoded). + pub fmspc: String, +} + +/// Response structure for TCB (Trusted Computing Base) Information. +/// +/// Contains the JSON representation of TCB information for a specific platform, +/// along with the certificate chain of the TCB Info signer. +#[derive(Debug, Clone)] +pub struct TcbInfoResponse { + /// JSON containing TCB information for a specific platform (FMSPC). + pub tcb_info_json: TcbInfoJson, // String alias for now + /// PEM-encoded certificate chain for the TCB Info signer. + /// Header name differs slightly between v3 ("SGX-TCB-Info-Issuer-Chain") and v4 ("TCB-Info-Issuer-Chain" - check spec). + pub issuer_chain: String, +} + +/// Response structure for Enclave Identity Information. +/// +/// Contains the JSON representation of enclave identity details for QE, QvE, or QAE, +/// along with its issuer chain. +#[derive(Debug, Clone)] +pub struct EnclaveIdentityResponse { + /// JSON containing information about the QE, QvE, or QAE. + pub enclave_identity_json: EnclaveIdentityJson, // String alias for now + /// PEM-encoded certificate chain for the Enclave Identity signer. + /// Header name seems consistent ("SGX-Enclave-Identity-Issuer-Chain"). + pub issuer_chain: String, +} + +/// Response structure for TCB Evaluation Data Numbers (V4 ONLY). +/// +/// Contains the JSON representation of supported TCB Evaluation Data Numbers +/// and its corresponding issuer chain. +#[derive(Debug, Clone)] +pub struct TcbEvaluationDataNumbersResponse { + /// JSON containing the list of supported TCB Evaluation Data Numbers (V4 ONLY). + pub tcb_evaluation_data_numbers_json: TcbEvaluationDataNumbersJson, // String alias for now + /// PEM-encoded certificate chain for the TCB Evaluation Data Numbers signer (V4 ONLY). + /// Header: "TCB-Evaluation-Data-Numbers-Issuer-Chain". + pub issuer_chain: String, +} + +/// Response structure for Platform Configuration Key Certificate Revocation List (PCK CRL). +/// +/// Contains the CRL data and its issuer chain for validating platform configuration keys. +#[derive(Debug, Clone)] +pub struct PckCrlResponse { + /// CRL data (PEM or DER encoded). + pub crl_data: Vec, + /// PEM-encoded certificate chain for the CRL issuer. + /// Header name differs between v3 ("PCS-CRL-Issuer-Chain") and v4 ("SGX-PCK-CRL-Issuer-Chain"). + pub issuer_chain: String, +} + +/// Response structure for the request to add a package. +pub struct AddPackageResponse { + /// Platform Membership Certificates + pub pck_certs: Vec, + /// The certificate count extracted from the response header. + pub pck_cert_count: usize, +} diff --git a/crates/intel-dcap-api/src/types.rs b/crates/intel-dcap-api/src/types.rs new file mode 100644 index 0000000..07b73f1 --- /dev/null +++ b/crates/intel-dcap-api/src/types.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +use std::fmt; + +/// Represents the type of Certificate Authority (CA) for Intel Trusted Services. +/// +/// This enum defines the different types of Certificate Authorities used in the Intel DCAP API, +/// specifically distinguishing between processor and platform CAs. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CaType { + /// Represents a processor-specific Certificate Authority. + Processor, + /// Represents a platform-wide Certificate Authority. + Platform, +} + +impl fmt::Display for CaType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CaType::Processor => write!(f, "processor"), + CaType::Platform => write!(f, "platform"), + } + } +} + +/// Represents the encoding format for Certificate Revocation Lists (CRLs). +/// +/// This enum defines the supported encoding formats for CRLs in the Intel DCAP API, +/// distinguishing between PEM (Privacy Enhanced Mail) and DER (Distinguished Encoding Rules) formats. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CrlEncoding { + /// Represents the PEM (Privacy Enhanced Mail) encoding format. + Pem, + /// Represents the DER (Distinguished Encoding Rules) encoding format. + Der, +} + +impl fmt::Display for CrlEncoding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CrlEncoding::Pem => write!(f, "pem"), + CrlEncoding::Der => write!(f, "der"), + } + } +} + +/// Represents the type of update for Intel Trusted Services. +/// +/// This enum defines different update types, distinguishing between early and standard updates +/// in the Intel DCAP (Data Center Attestation Primitives) API. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UpdateType { + /// Represents early updates, typically used for preview or beta releases. + Early, + /// Represents standard updates, which are the regular release cycle. + Standard, +} + +impl fmt::Display for UpdateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + UpdateType::Early => write!(f, "early"), + UpdateType::Standard => write!(f, "standard"), + } + } +} + +/// Represents the platform filter options for Intel DCAP (Data Center Attestation Primitives) API. +/// +/// This enum allows filtering platforms based on different criteria, +/// such as selecting all platforms, client-specific platforms, or specific Intel processor generations. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PlatformFilter { + /// Represents a selection of all available platforms. + All, + /// Represents a selection of client-specific platforms. + Client, + /// Represents platforms with Intel E3 processors. + E3, + /// Represents platforms with Intel E5 processors. + E5, +} + +impl fmt::Display for PlatformFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PlatformFilter::All => write!(f, "all"), + PlatformFilter::Client => write!(f, "client"), + PlatformFilter::E3 => write!(f, "E3"), + PlatformFilter::E5 => write!(f, "E5"), + } + } +} + +/// Represents the version of the Intel Trusted Services API to target. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ApiVersion { + /// Represents version 3 of the Intel Trusted Services API. + V3, + /// Represents version 4 of the Intel Trusted Services API. + V4, +} + +impl ApiVersion { + /// Returns the string representation of the version for URL paths. + pub fn path_segment(&self) -> &'static str { + match self { + ApiVersion::V3 => "v3", + ApiVersion::V4 => "v4", + } + } +} + +impl fmt::Display for ApiVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ApiVersion::V3 => write!(f, "v3"), + ApiVersion::V4 => write!(f, "v4"), + } + } +} diff --git a/crates/intel-dcap-api/tests/mock_api_tests.rs b/crates/intel-dcap-api/tests/mock_api_tests.rs new file mode 100644 index 0000000..34a1f39 --- /dev/null +++ b/crates/intel-dcap-api/tests/mock_api_tests.rs @@ -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"); +} diff --git a/crates/intel-dcap-api/tests/real_data_mock_tests.rs b/crates/intel-dcap-api/tests/real_data_mock_tests.rs new file mode 100644 index 0000000..026335d --- /dev/null +++ b/crates/intel-dcap-api/tests/real_data_mock_tests.rs @@ -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"), + } +} diff --git a/crates/intel-dcap-api/tests/test_data/fmspcs.json b/crates/intel-dcap-api/tests/test_data/fmspcs.json new file mode 100644 index 0000000..93e4cc8 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/fmspcs.json @@ -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\"}]" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/fmspcs_all_platforms.json b/crates/intel-dcap-api/tests/test_data/fmspcs_all_platforms.json new file mode 100644 index 0000000..93e4cc8 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/fmspcs_all_platforms.json @@ -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\"}]" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/fmspcs_no_filter.json b/crates/intel-dcap-api/tests/test_data/fmspcs_no_filter.json new file mode 100644 index 0000000..93e4cc8 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/fmspcs_no_filter.json @@ -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\"}]" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/pck_crl_platform.json b/crates/intel-dcap-api/tests/test_data/pck_crl_platform.json new file mode 100644 index 0000000..bdad22c --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/pck_crl_platform.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/pck_crl_processor.json b/crates/intel-dcap-api/tests/test_data/pck_crl_processor.json new file mode 100644 index 0000000..ec77f5b --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/pck_crl_processor.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/pck_crl_processor_der.json b/crates/intel-dcap-api/tests/test_data/pck_crl_processor_der.json new file mode 100644 index 0000000..3421b50 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/pck_crl_processor_der.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_qae_identity.json b/crates/intel-dcap-api/tests/test_data/sgx_qae_identity.json new file mode 100644 index 0000000..4c4c329 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_qae_identity.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_qe_identity.json b/crates/intel-dcap-api/tests/test_data/sgx_qe_identity.json new file mode 100644 index 0000000..3c4c157 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_qe_identity.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_qe_identity_v3.json b/crates/intel-dcap-api/tests/test_data/sgx_qe_identity_v3.json new file mode 100644 index 0000000..f545dbb --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_qe_identity_v3.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_qve_identity.json b/crates/intel-dcap-api/tests/test_data/sgx_qve_identity.json new file mode 100644 index 0000000..2a29b18 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_qve_identity.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_tcb_eval_nums.json b/crates/intel-dcap-api/tests/test_data/sgx_tcb_eval_nums.json new file mode 100644 index 0000000..9076051 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_tcb_eval_nums.json @@ -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\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_00906ED50000.json b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_00906ED50000.json new file mode 100644 index 0000000..8e17eee --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_00906ED50000.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"SGX\",\"version\":3,\"issueDate\":\"2025-05-27T19:31:07Z\",\"nextUpdate\":\"2025-06-26T19:31:07Z\",\"fmspc\":\"00906ED50000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00828\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00161\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":2},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":2},{\"svn\":2},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-01-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":6},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00203\",\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]}]},\"signature\":\"6110be36e7515cd536981fedf215ef6b937de0f4c758b672557395a2dc987d94028eb8c4ee3c065bb5585fb2953233160253064128157a48cac7a76822f6aacb\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_alt.json b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_alt.json new file mode 100644 index 0000000..8e17eee --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_alt.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"SGX\",\"version\":3,\"issueDate\":\"2025-05-27T19:31:07Z\",\"nextUpdate\":\"2025-06-26T19:31:07Z\",\"fmspc\":\"00906ED50000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00828\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00161\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":2},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":2},{\"svn\":2},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-01-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":6},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00203\",\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]}]},\"signature\":\"6110be36e7515cd536981fedf215ef6b937de0f4c758b672557395a2dc987d94028eb8c4ee3c065bb5585fb2953233160253064128157a48cac7a76822f6aacb\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_early.json b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_early.json new file mode 100644 index 0000000..b57d7eb --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_early.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"SGX\",\"version\":3,\"issueDate\":\"2025-05-27T19:31:08Z\",\"nextUpdate\":\"2025-06-26T19:31:08Z\",\"fmspc\":\"00906ED50000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":19,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":22},{\"svn\":22},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2025-05-14T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":22},{\"svn\":22},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2025-05-14T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-01153\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":21},{\"svn\":21},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2024-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":14},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00828\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":20},{\"svn\":20},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":19},{\"svn\":19},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00161\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":17},{\"svn\":17},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00161\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":15},{\"svn\":15},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":6},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00161\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":14},{\"svn\":14},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":2},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00219\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":13},{\"svn\":13},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":2},{\"svn\":2},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":7},\"tcbDate\":\"2019-01-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":1},{\"svn\":1},{\"svn\":2},{\"svn\":4},{\"svn\":1},{\"svn\":128},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":6},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00203\",\"INTEL-SA-00233\",\"INTEL-SA-00220\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00219\",\"INTEL-SA-00161\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00614\",\"INTEL-SA-00617\",\"INTEL-SA-00289\",\"INTEL-SA-00828\",\"INTEL-SA-01153\",\"INTEL-SA-00334\",\"INTEL-SA-00615\"]}]},\"signature\":\"f923da7909b959d047642857106e14d3198da1222fc9bb0d6eb4aab571d55c3c583488eec8b06445666d3c19f02d2ec72cde4c2247886684d93fb70ed1f67efc\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_v3.json b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_v3.json new file mode 100644 index 0000000..16c1925 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/sgx_tcb_info_v3.json @@ -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_info_json": "{\"tcbInfo\":{\"version\":2,\"issueDate\":\"2025-05-27T19:31:07Z\",\"nextUpdate\":\"2025-06-26T19:31:07Z\",\"fmspc\":\"00906ED50000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomp01svn\":21,\"sgxtcbcomp02svn\":21,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":14,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":21,\"sgxtcbcomp02svn\":21,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":20,\"sgxtcbcomp02svn\":20,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":14,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":20,\"sgxtcbcomp02svn\":20,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":19,\"sgxtcbcomp02svn\":19,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":6,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":19,\"sgxtcbcomp02svn\":19,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":13},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":17,\"sgxtcbcomp02svn\":17,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":6,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":17,\"sgxtcbcomp02svn\":17,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":6,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":17,\"sgxtcbcomp02svn\":17,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":17,\"sgxtcbcomp02svn\":17,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":15,\"sgxtcbcomp02svn\":15,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":6,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":15,\"sgxtcbcomp02svn\":15,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2020-06-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":14,\"sgxtcbcomp02svn\":14,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":6,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":14,\"sgxtcbcomp02svn\":14,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":10},\"tcbDate\":\"2019-12-11T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":13,\"sgxtcbcomp02svn\":13,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":2,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":13,\"sgxtcbcomp02svn\":13,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":9},\"tcbDate\":\"2019-11-13T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\"},{\"tcb\":{\"sgxtcbcomp01svn\":2,\"sgxtcbcomp02svn\":2,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":7},\"tcbDate\":\"2019-05-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":1,\"sgxtcbcomp02svn\":1,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":7},\"tcbDate\":\"2019-01-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"},{\"tcb\":{\"sgxtcbcomp01svn\":1,\"sgxtcbcomp02svn\":1,\"sgxtcbcomp03svn\":2,\"sgxtcbcomp04svn\":4,\"sgxtcbcomp05svn\":1,\"sgxtcbcomp06svn\":128,\"sgxtcbcomp07svn\":0,\"sgxtcbcomp08svn\":0,\"sgxtcbcomp09svn\":0,\"sgxtcbcomp10svn\":0,\"sgxtcbcomp11svn\":0,\"sgxtcbcomp12svn\":0,\"sgxtcbcomp13svn\":0,\"sgxtcbcomp14svn\":0,\"sgxtcbcomp15svn\":0,\"sgxtcbcomp16svn\":0,\"pcesvn\":6},\"tcbDate\":\"2018-08-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]},\"signature\":\"fd7fe639c792132bce84469fb9a95648657c4f72c45b71f753b06c2597d09fd01fd8f2619f15f1faf1b1136d9fa5c7f2bb92252730a441090515dbefd60cc4e4\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/tdx_qe_identity.json b/crates/intel-dcap-api/tests/test_data/tdx_qe_identity.json new file mode 100644 index 0000000..7a77ea3 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/tdx_qe_identity.json @@ -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" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/tdx_tcb_eval_nums.json b/crates/intel-dcap-api/tests/test_data/tdx_tcb_eval_nums.json new file mode 100644 index 0000000..545318e --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/tdx_tcb_eval_nums.json @@ -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\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/tdx_tcb_info.json b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info.json new file mode 100644 index 0000000..c2e4b43 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"TDX\",\"version\":3,\"issueDate\":\"2025-05-27T19:31:43Z\",\"nextUpdate\":\"2025-06-26T19:31:43Z\",\"fmspc\":\"00806F050000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tdxModule\":{\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\"},\"tdxModuleIdentities\":[{\"id\":\"TDX_03\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},{\"id\":\"TDX_01\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]}],\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":6,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]}]},\"signature\":\"4eb9de091235241a82cdede1eea6ba1e7df3e158e58591ae2b743ff9fc8ab3774bbb68758da9d588486f84df74b00c10081fb49b7aafee980c70a8f54e28b4f2\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_00806F050000.json b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_00806F050000.json new file mode 100644 index 0000000..c313dc4 --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_00806F050000.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"TDX\",\"version\":3,\"issueDate\":\"2025-05-27T19:25:02Z\",\"nextUpdate\":\"2025-06-26T19:25:02Z\",\"fmspc\":\"00806F050000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tdxModule\":{\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\"},\"tdxModuleIdentities\":[{\"id\":\"TDX_03\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},{\"id\":\"TDX_01\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]}],\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":6,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]}]},\"signature\":\"05d350ab0840b40362419623d36b11e5fcfaebcf0926deb6058232a66a2bbd12e347e8dc5e843d13351714144b3aefd23425f396e15b1a1b7fb673415122b82d\"}" +} \ No newline at end of file diff --git a/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_eval17.json b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_eval17.json new file mode 100644 index 0000000..927484c --- /dev/null +++ b/crates/intel-dcap-api/tests/test_data/tdx_tcb_info_eval17.json @@ -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_info_json": "{\"tcbInfo\":{\"id\":\"TDX\",\"version\":3,\"issueDate\":\"2025-05-27T19:31:44Z\",\"nextUpdate\":\"2025-06-26T19:31:44Z\",\"fmspc\":\"00806F050000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":17,\"tdxModule\":{\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\"},\"tdxModuleIdentities\":[{\"id\":\"TDX_03\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":3},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"}]},{\"id\":\"TDX_01\",\"mrsigner\":\"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"attributes\":\"0000000000000000\",\"attributesMask\":\"FFFFFFFFFFFFFFFF\",\"tcbLevels\":[{\"tcb\":{\"isvsvn\":4},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"isvsvn\":2},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\"}]}],\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":7,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2024-03-13T00:00:00Z\",\"tcbStatus\":\"UpToDate\"},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":6,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":6,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-08-09T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":5,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":2,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":2,\"category\":\"BIOS\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":1,\"category\":\"BIOS\"},{\"svn\":0},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"SEAMLDR ACM\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5,\"tdxtcbcomponents\":[{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":0,\"category\":\"OS/VMM\",\"type\":\"TDX Module\"},{\"svn\":5,\"category\":\"OS/VMM\",\"type\":\"TDX Late Microcode Update\"},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}]},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00837\",\"INTEL-SA-00960\",\"INTEL-SA-00982\",\"INTEL-SA-00986\"]}]},\"signature\":\"1c00c4b83ac0c9abe692b11a59df58604c6d638472822c11d7cb7753a5826be5cb3f8201da78bbaca85429c0e15a51255fc56dcbdd5e913f92e658b0cac377b1\"}" +} \ No newline at end of file diff --git a/crates/teepot-tdx-attest-rs/Cargo.lock b/crates/teepot-tdx-attest-rs/Cargo.lock new file mode 100644 index 0000000..f8b313e --- /dev/null +++ b/crates/teepot-tdx-attest-rs/Cargo.lock @@ -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" diff --git a/crates/teepot-tdx-attest-rs/Cargo.toml b/crates/teepot-tdx-attest-rs/Cargo.toml new file mode 100644 index 0000000..0e88ec7 --- /dev/null +++ b/crates/teepot-tdx-attest-rs/Cargo.toml @@ -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" } diff --git a/crates/teepot-tdx-attest-rs/License.txt b/crates/teepot-tdx-attest-rs/License.txt new file mode 100644 index 0000000..c49d62f --- /dev/null +++ b/crates/teepot-tdx-attest-rs/License.txt @@ -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. + diff --git a/crates/teepot-tdx-attest-rs/README.md b/crates/teepot-tdx-attest-rs/README.md new file mode 100644 index 0000000..ac2dbbf --- /dev/null +++ b/crates/teepot-tdx-attest-rs/README.md @@ -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). \ No newline at end of file diff --git a/crates/teepot-tdx-attest-rs/src/lib.rs b/crates/teepot-tdx-attest-rs/src/lib.rs new file mode 100644 index 0000000..cbf0e84 --- /dev/null +++ b/crates/teepot-tdx-attest-rs/src/lib.rs @@ -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>) { + 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::() { + 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::() != 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>) { + 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::()]; + 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); + } +} diff --git a/crates/teepot-tdx-attest-sys/Cargo.lock b/crates/teepot-tdx-attest-sys/Cargo.lock new file mode 100644 index 0000000..79d6f01 --- /dev/null +++ b/crates/teepot-tdx-attest-sys/Cargo.lock @@ -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" diff --git a/crates/teepot-tdx-attest-sys/Cargo.toml b/crates/teepot-tdx-attest-sys/Cargo.toml new file mode 100644 index 0000000..7e9db7a --- /dev/null +++ b/crates/teepot-tdx-attest-sys/Cargo.toml @@ -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" diff --git a/crates/teepot-tdx-attest-sys/License.txt b/crates/teepot-tdx-attest-sys/License.txt new file mode 100644 index 0000000..c49d62f --- /dev/null +++ b/crates/teepot-tdx-attest-sys/License.txt @@ -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. + diff --git a/crates/teepot-tdx-attest-sys/README.md b/crates/teepot-tdx-attest-sys/README.md new file mode 100644 index 0000000..abd4cb9 --- /dev/null +++ b/crates/teepot-tdx-attest-sys/README.md @@ -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. \ No newline at end of file diff --git a/crates/teepot-tdx-attest-sys/bindings.h b/crates/teepot-tdx-attest-sys/bindings.h new file mode 100644 index 0000000..01a400d --- /dev/null +++ b/crates/teepot-tdx-attest-sys/bindings.h @@ -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" diff --git a/crates/teepot-tdx-attest-sys/build.rs b/crates/teepot-tdx-attest-sys/build.rs new file mode 100644 index 0000000..7737907 --- /dev/null +++ b/crates/teepot-tdx-attest-sys/build.rs @@ -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!"); +} diff --git a/crates/teepot-tdx-attest-sys/src/lib.rs b/crates/teepot-tdx-attest-sys/src/lib.rs new file mode 100644 index 0000000..c4194d4 --- /dev/null +++ b/crates/teepot-tdx-attest-sys/src/lib.rs @@ -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")); diff --git a/crates/teepot-tee-quote-verification-rs/Cargo.lock b/crates/teepot-tee-quote-verification-rs/Cargo.lock new file mode 100644 index 0000000..a906ad0 --- /dev/null +++ b/crates/teepot-tee-quote-verification-rs/Cargo.lock @@ -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" diff --git a/crates/teepot-tee-quote-verification-rs/Cargo.toml b/crates/teepot-tee-quote-verification-rs/Cargo.toml index 1959c34..cac4734 100644 --- a/crates/teepot-tee-quote-verification-rs/Cargo.toml +++ b/crates/teepot-tee-quote-verification-rs/Cargo.toml @@ -2,13 +2,16 @@ [package] name = "teepot-tee-quote-verification-rs" -version = "0.3.0" +version = "0.6.0" edition = "2021" license = "BSD-3-Clause" -repository.workspace = true -homepage.workspace = true +repository = "https://github.com/matter-labs/teepot" +homepage = "https://github.com/matter-labs/teepot" description = "Fork of intel-tee-quote-verification-rs" [dependencies] -intel-tee-quote-verification-sys.workspace = true -serde.workspace = true +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" } diff --git a/crates/teepot-tee-quote-verification-rs/README.md b/crates/teepot-tee-quote-verification-rs/README.md new file mode 100644 index 0000000..bc3480a --- /dev/null +++ b/crates/teepot-tee-quote-verification-rs/README.md @@ -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 \ No newline at end of file diff --git a/crates/teepot-tee-quote-verification-rs/src/empty.rs b/crates/teepot-tee-quote-verification-rs/src/empty.rs new file mode 100644 index 0000000..756b0ba --- /dev/null +++ b/crates/teepot-tee-quote-verification-rs/src/empty.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: BSD-3-Clause + +pub const NOTHING_TO_SEE_HERE: u8 = 0; diff --git a/crates/teepot-tee-quote-verification-rs/src/intel.rs b/crates/teepot-tee-quote-verification-rs/src/intel.rs new file mode 100644 index 0000000..6e897c2 --- /dev/null +++ b/crates/teepot-tee-quote-verification-rs/src/intel.rs @@ -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::() as u32); +/// ``` +pub fn sgx_qv_get_quote_supplemental_data_size() -> Result { + 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::() as u32); +/// ``` +pub fn tdx_qv_get_quote_supplemental_data_size() -> Result { + 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 { + fn try_into_collateral( + buf: *const sgx_ql_qve_collateral_t, + buf_len: u32, + ) -> Result { + fn try_into_boxed_slice( + p: *mut ::std::os::raw::c_char, + size: u32, + ) -> Result, 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::() + || !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), + } + } +} diff --git a/crates/teepot-tee-quote-verification-rs/src/lib.rs b/crates/teepot-tee-quote-verification-rs/src/lib.rs index 22c8055..9b51eeb 100644 --- a/crates/teepot-tee-quote-verification-rs/src/lib.rs +++ b/crates/teepot-tee-quote-verification-rs/src/lib.rs @@ -1,556 +1,10 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2024 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; -use std::mem; -use std::ops::Deref; -use std::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; -pub use qvl_sys::sgx_ql_qe_report_info_t; -pub use qvl_sys::sgx_ql_qv_result_t; -pub use qvl_sys::sgx_ql_qv_supplemental_t; -pub use qvl_sys::sgx_ql_qve_collateral_t; -pub use qvl_sys::sgx_ql_request_policy_t; -pub use qvl_sys::sgx_qv_path_type_t; -pub use qvl_sys::tdx_ql_qve_collateral_t; -pub use qvl_sys::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::() as u32); -/// ``` -pub fn sgx_qv_get_quote_supplemental_data_size() -> Result { - 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::() as u32); -/// ``` -pub fn tdx_qv_get_quote_supplemental_data_size() -> Result { - 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]>, -} - -impl TryFrom<&sgx_ql_qve_collateral_t> for Collateral { - type Error = (); - - fn try_from(value: &sgx_ql_qve_collateral_t) -> Result { - fn to_boxed_slice(p: *mut ::std::os::raw::c_char, size: u32) -> Result, ()> { - if p.is_null() { - return Err(()); - } - Ok(Box::from(unsafe { - slice::from_raw_parts(p as _, size as _) - })) - } - - Ok(Collateral { - major_version: unsafe { value.__bindgen_anon_1.__bindgen_anon_1.major_version }, - minor_version: unsafe { value.__bindgen_anon_1.__bindgen_anon_1.minor_version }, - tee_type: value.tee_type, - pck_crl_issuer_chain: to_boxed_slice( - value.pck_crl_issuer_chain, - value.pck_crl_issuer_chain_size, - )?, - root_ca_crl: to_boxed_slice(value.root_ca_crl, value.root_ca_crl_size)?, - pck_crl: to_boxed_slice(value.pck_crl, value.pck_crl_size)?, - tcb_info_issuer_chain: to_boxed_slice( - value.tcb_info_issuer_chain, - value.tcb_info_issuer_chain_size, - )?, - tcb_info: to_boxed_slice(value.tcb_info, value.tcb_info_size)?, - qe_identity_issuer_chain: to_boxed_slice( - value.qe_identity_issuer_chain, - value.qe_identity_issuer_chain_size, - )?, - qe_identity: to_boxed_slice(value.qe_identity, value.qe_identity_size)?, - }) - } -} - -// 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<'a> Deref for SgxQlQveCollateralT<'a> { - 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 { - 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 => { - assert!(!buf.is_null()); - assert!(buf_len > 0); - assert_eq!( - (buf as usize) % mem::align_of::(), - 0 - ); - // SAFETY: buf is not null, buf_len is not zero, and buf is aligned. - let orig_collateral = &unsafe { *(buf as *const sgx_ql_qve_collateral_t) }; - Collateral::try_from(orig_collateral).map_err(|_| quote3_error_t::SGX_QL_ERROR_MAX) - } - 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::*; diff --git a/crates/teepot-vault/Cargo.toml b/crates/teepot-vault/Cargo.toml new file mode 100644 index 0000000..0c0d4c6 --- /dev/null +++ b/crates/teepot-vault/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "teepot-vault" +description = "TEE secret manager" +license.workspace = true +version.workspace = true +edition.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +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 = { workspace = true, features = ["alloc"] } +hex.workspace = true +pgp.workspace = true +rustls.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_with.workspace = true +sha2.workspace = true +teepot.workspace = true +thiserror.workspace = true +tracing.workspace = true +webpki-roots.workspace = true +x509-cert.workspace = true + +[dev-dependencies] +base64.workspace = true diff --git a/crates/teepot-vault/README.md b/crates/teepot-vault/README.md new file mode 100644 index 0000000..016abe7 --- /dev/null +++ b/crates/teepot-vault/README.md @@ -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> { + 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, +} + +// 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 { + // 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. \ No newline at end of file diff --git a/bin/tee-stress-client/Cargo.toml b/crates/teepot-vault/bin/tee-stress-client/Cargo.toml similarity index 93% rename from bin/tee-stress-client/Cargo.toml rename to crates/teepot-vault/bin/tee-stress-client/Cargo.toml index 1ce3cae..e55b189 100644 --- a/bin/tee-stress-client/Cargo.toml +++ b/crates/teepot-vault/bin/tee-stress-client/Cargo.toml @@ -13,6 +13,7 @@ anyhow.workspace = true clap.workspace = true serde.workspace = true teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/bin/tee-stress-client/src/main.rs b/crates/teepot-vault/bin/tee-stress-client/src/main.rs similarity index 89% rename from bin/tee-stress-client/src/main.rs rename to crates/teepot-vault/bin/tee-stress-client/src/main.rs index f691401..a937869 100644 --- a/bin/tee-stress-client/src/main.rs +++ b/crates/teepot-vault/bin/tee-stress-client/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2024 Matter Labs +// Copyright (c) 2024-2025 Matter Labs //! Server to handle requests to the Vault TEE @@ -11,14 +11,17 @@ use anyhow::{Context, Result}; use clap::Parser; use serde::{Deserialize, Serialize}; use std::time::Duration; -use teepot::client::vault::VaultConnection; -use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; -use teepot::server::pki::make_self_signed_cert; -use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot::quote::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot_vault::{ + client::vault::VaultConnection, + server::{ + attestation::{get_quote_and_collateral, VaultAttestationArgs}, + pki::make_self_signed_cert, + }, +}; use tracing::{error, trace}; use tracing_log::LogTracer; -use tracing_subscriber::Registry; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] diff --git a/crates/teepot/tests/data/stress.json b/crates/teepot-vault/bin/tee-stress-client/src/stress.json similarity index 100% rename from crates/teepot/tests/data/stress.json rename to crates/teepot-vault/bin/tee-stress-client/src/stress.json diff --git a/crates/teepot/tests/data/stress.json.asc b/crates/teepot-vault/bin/tee-stress-client/src/stress.json.asc similarity index 100% rename from crates/teepot/tests/data/stress.json.asc rename to crates/teepot-vault/bin/tee-stress-client/src/stress.json.asc diff --git a/bin/tee-stress-client/tee-stress-client.manifest.template b/crates/teepot-vault/bin/tee-stress-client/tee-stress-client.manifest.template similarity index 100% rename from bin/tee-stress-client/tee-stress-client.manifest.template rename to crates/teepot-vault/bin/tee-stress-client/tee-stress-client.manifest.template diff --git a/bin/tee-vault-admin/Cargo.toml b/crates/teepot-vault/bin/tee-vault-admin/Cargo.toml similarity index 94% rename from bin/tee-vault-admin/Cargo.toml rename to crates/teepot-vault/bin/tee-vault-admin/Cargo.toml index 7fa206c..7ff77a9 100644 --- a/bin/tee-vault-admin/Cargo.toml +++ b/crates/teepot-vault/bin/tee-vault-admin/Cargo.toml @@ -18,6 +18,7 @@ rustls.workspace = true serde_json.workspace = true sha2.workspace = true teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-actix-web.workspace = true tracing-log.workspace = true diff --git a/bin/tee-vault-admin/src/command.rs b/crates/teepot-vault/bin/tee-vault-admin/src/command.rs similarity index 90% rename from bin/tee-vault-admin/src/command.rs rename to crates/teepot-vault/bin/tee-vault-admin/src/command.rs index 607bca3..ea08299 100644 --- a/bin/tee-vault-admin/src/command.rs +++ b/crates/teepot-vault/bin/tee-vault-admin/src/command.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! post commands @@ -9,12 +9,12 @@ use anyhow::{anyhow, Context, Result}; use awc::http::StatusCode; use sha2::{Digest, Sha256}; use std::sync::Arc; -use teepot::client::vault::VaultConnection; -use teepot::json::http::{ +use teepot_vault::client::vault::VaultConnection; +use teepot_vault::json::http::{ VaultCommandRequest, VaultCommandResponse, VaultCommands, VaultCommandsResponse, }; -use teepot::json::secrets::{AdminConfig, AdminState}; -use teepot::server::{signatures::VerifySig, HttpResponseError, Status}; +use teepot_vault::json::secrets::{AdminConfig, AdminState}; +use teepot_vault::server::{signatures::VerifySig, HttpResponseError, Status}; use tracing::instrument; /// Post command diff --git a/bin/tee-vault-admin/src/digest.rs b/crates/teepot-vault/bin/tee-vault-admin/src/digest.rs similarity index 82% rename from bin/tee-vault-admin/src/digest.rs rename to crates/teepot-vault/bin/tee-vault-admin/src/digest.rs index bd87b02..70ec016 100644 --- a/bin/tee-vault-admin/src/digest.rs +++ b/crates/teepot-vault/bin/tee-vault-admin/src/digest.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! digest @@ -9,9 +9,9 @@ use anyhow::{Context, Result}; use awc::http::StatusCode; use serde_json::json; use std::sync::Arc; -use teepot::client::vault::VaultConnection; -use teepot::json::secrets::AdminState; -use teepot::server::{HttpResponseError, Status}; +use teepot_vault::client::vault::VaultConnection; +use teepot_vault::json::secrets::AdminState; +use teepot_vault::server::{HttpResponseError, Status}; use tracing::instrument; /// Get last digest diff --git a/bin/tee-vault-admin/src/main.rs b/crates/teepot-vault/bin/tee-vault-admin/src/main.rs similarity index 83% rename from bin/tee-vault-admin/src/main.rs rename to crates/teepot-vault/bin/tee-vault-admin/src/main.rs index 9cd54f8..08ef10e 100644 --- a/bin/tee-vault-admin/src/main.rs +++ b/crates/teepot-vault/bin/tee-vault-admin/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Server to handle requests to the Vault TEE @@ -9,26 +9,27 @@ mod command; mod digest; mod sign; -use actix_web::web::Data; -use actix_web::{web, App, HttpServer}; +use actix_web::{web, web::Data, App, HttpServer}; use anyhow::{Context, Result}; use clap::Parser; use command::post_command; use digest::get_digest; use rustls::ServerConfig; use sign::post_sign; -use std::net::Ipv6Addr; -use std::sync::Arc; -use teepot::json::http::{SignRequest, VaultCommandRequest, DIGEST_URL}; -use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; -use teepot::server::new_json_cfg; -use teepot::server::pki::make_self_signed_cert; -use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; +use std::{net::Ipv6Addr, sync::Arc}; +use teepot::quote::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot_vault::{ + json::http::{SignRequest, VaultCommandRequest, DIGEST_URL}, + server::{ + attestation::{get_quote_and_collateral, VaultAttestationArgs}, + new_json_cfg, + pki::make_self_signed_cert, + }, +}; use tracing::{error, info}; use tracing_actix_web::TracingLogger; use tracing_log::LogTracer; -use tracing_subscriber::Registry; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; /// Server state pub struct ServerState { @@ -70,6 +71,8 @@ async fn main() -> Result<()> { // don't return for now, we can still serve requests but we won't be able to attest } + let _ = rustls::crypto::ring::default_provider().install_default(); + // init server config builder with safe defaults let config = ServerConfig::builder() .with_no_client_auth() @@ -95,7 +98,7 @@ async fn main() -> Result<()> { .service(web::resource(SignRequest::URL).route(web::post().to(post_sign))) .service(web::resource(DIGEST_URL).route(web::get().to(get_digest))) }) - .bind_rustls_0_22((Ipv6Addr::UNSPECIFIED, args.port), config) + .bind_rustls_0_23((Ipv6Addr::UNSPECIFIED, args.port), config) { Ok(c) => c, Err(e) => { @@ -115,9 +118,9 @@ async fn main() -> Result<()> { #[cfg(test)] mod tests { use serde_json::json; - use teepot::json::http::{VaultCommand, VaultCommands}; + use teepot_vault::json::http::{VaultCommand, VaultCommands}; - const TEST_DATA: &str = include_str!("../../../crates/teepot/tests/data/test.json"); + const TEST_DATA: &str = include_str!("../../../tests/data/test.json"); #[test] fn test_vault_commands() { diff --git a/bin/tee-vault-admin/src/sign.rs b/crates/teepot-vault/bin/tee-vault-admin/src/sign.rs similarity index 93% rename from bin/tee-vault-admin/src/sign.rs rename to crates/teepot-vault/bin/tee-vault-admin/src/sign.rs index 973df93..d5b1718 100644 --- a/bin/tee-vault-admin/src/sign.rs +++ b/crates/teepot-vault/bin/tee-vault-admin/src/sign.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! post signing request @@ -9,14 +9,14 @@ use actix_web::web; use anyhow::{anyhow, Context, Result}; use sha2::{Digest, Sha256}; use std::sync::Arc; -use teepot::client::vault::VaultConnection; -use teepot::json::http::{SignRequest, SignRequestData, SignResponse}; -use teepot::json::secrets::{AdminConfig, AdminState, SGXSigningKey}; -use teepot::server::signatures::VerifySig as _; -use teepot::server::{HttpResponseError, Status}; use teepot::sgx::sign::PrivateKey as _; use teepot::sgx::sign::{Author, Signature}; use teepot::sgx::sign::{Body, RS256PrivateKey}; +use teepot_vault::client::vault::VaultConnection; +use teepot_vault::json::http::{SignRequest, SignRequestData, SignResponse}; +use teepot_vault::json::secrets::{AdminConfig, AdminState, SGXSigningKey}; +use teepot_vault::server::signatures::VerifySig as _; +use teepot_vault::server::{HttpResponseError, Status}; use tracing::instrument; /// Sign command diff --git a/bin/tee-vault-unseal/Cargo.toml b/crates/teepot-vault/bin/tee-vault-unseal/Cargo.toml similarity index 93% rename from bin/tee-vault-unseal/Cargo.toml rename to crates/teepot-vault/bin/tee-vault-unseal/Cargo.toml index b803346..d1ab395 100644 --- a/bin/tee-vault-unseal/Cargo.toml +++ b/crates/teepot-vault/bin/tee-vault-unseal/Cargo.toml @@ -15,6 +15,7 @@ clap.workspace = true rustls.workspace = true serde_json.workspace = true teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/bin/tee-vault-unseal/src/admin-policy.hcl b/crates/teepot-vault/bin/tee-vault-unseal/src/admin-policy.hcl similarity index 100% rename from bin/tee-vault-unseal/src/admin-policy.hcl rename to crates/teepot-vault/bin/tee-vault-unseal/src/admin-policy.hcl diff --git a/bin/tee-vault-unseal/src/init.rs b/crates/teepot-vault/bin/tee-vault-unseal/src/init.rs similarity index 94% rename from bin/tee-vault-unseal/src/init.rs rename to crates/teepot-vault/bin/tee-vault-unseal/src/init.rs index 302c5fa..063a696 100644 --- a/bin/tee-vault-unseal/src/init.rs +++ b/crates/teepot-vault/bin/tee-vault-unseal/src/init.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs use crate::{get_vault_status, UnsealServerState, Worker}; use actix_web::error::ErrorBadRequest; @@ -7,10 +7,10 @@ use actix_web::{web, HttpResponse}; use anyhow::{anyhow, Context, Result}; use awc::http::StatusCode; use serde_json::json; -use teepot::client::TeeConnection; -use teepot::json::http::{Init, InitResponse, VaultInitRequest}; -use teepot::json::secrets::AdminConfig; -use teepot::server::{HttpResponseError, Status}; +use teepot_vault::client::TeeConnection; +use teepot_vault::json::http::{Init, InitResponse, VaultInitRequest}; +use teepot_vault::json::secrets::AdminConfig; +use teepot_vault::server::{HttpResponseError, Status}; use tracing::{debug, error, info, instrument, trace}; #[instrument(level = "info", name = "/v1/sys/init", skip_all)] diff --git a/bin/tee-vault-unseal/src/main.rs b/crates/teepot-vault/bin/tee-vault-unseal/src/main.rs similarity index 92% rename from bin/tee-vault-unseal/src/main.rs rename to crates/teepot-vault/bin/tee-vault-unseal/src/main.rs index b30b977..9498699 100644 --- a/bin/tee-vault-unseal/src/main.rs +++ b/crates/teepot-vault/bin/tee-vault-unseal/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Server to initialize and unseal the Vault TEE. @@ -9,9 +9,7 @@ mod init; mod unseal; -use actix_web::rt::time::sleep; -use actix_web::web::Data; -use actix_web::{web, App, HttpServer}; +use actix_web::{rt::time::sleep, web, web::Data, App, HttpServer}; use anyhow::{bail, Context, Result}; use awc::Client; use clap::Parser; @@ -23,13 +21,13 @@ use std::net::Ipv6Addr; use std::path::PathBuf; use std::sync::{Arc, RwLock}; use std::time::Duration; -use teepot::client::{AttestationArgs, TeeConnection}; -use teepot::json::http::{Init, Unseal}; -use teepot::json::secrets::AdminConfig; -use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; -use teepot::server::new_json_cfg; -use teepot::server::pki::make_self_signed_cert; -use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot::pki::make_self_signed_cert; +use teepot::quote::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot_vault::client::{AttestationArgs, TeeConnection}; +use teepot_vault::json::http::{Init, Unseal}; +use teepot_vault::json::secrets::AdminConfig; +use teepot_vault::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; +use teepot_vault::server::new_json_cfg; use tracing::{error, info}; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; @@ -136,6 +134,8 @@ async fn main() -> Result<()> { let (report_data, cert_chain, priv_key) = make_self_signed_cert("CN=localhost", None)?; + let _ = rustls::crypto::ring::default_provider().install_default(); + // init server config builder with safe defaults let config = ServerConfig::builder() .with_no_client_auth() @@ -186,7 +186,7 @@ async fn main() -> Result<()> { .service(web::resource(Init::URL).route(web::post().to(post_init))) .service(web::resource(Unseal::URL).route(web::post().to(post_unseal))) }) - .bind_rustls_0_22((Ipv6Addr::UNSPECIFIED, args.port), config) + .bind_rustls_0_23((Ipv6Addr::UNSPECIFIED, args.port), config) { Ok(c) => c, Err(e) => { diff --git a/bin/tee-vault-unseal/src/unseal.rs b/crates/teepot-vault/bin/tee-vault-unseal/src/unseal.rs similarity index 97% rename from bin/tee-vault-unseal/src/unseal.rs rename to crates/teepot-vault/bin/tee-vault-unseal/src/unseal.rs index 7d2c011..c72bb50 100644 --- a/bin/tee-vault-unseal/src/unseal.rs +++ b/crates/teepot-vault/bin/tee-vault-unseal/src/unseal.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs use crate::{get_vault_status, UnsealServerConfig, UnsealServerState, Worker, VAULT_TOKEN_HEADER}; use actix_web::http::StatusCode; @@ -12,11 +12,11 @@ use std::fs::File; use std::future::Future; use std::io::Read; use std::time::Duration; -use teepot::client::vault::VaultConnection; -use teepot::client::TeeConnection; -use teepot::json::http::Unseal; -use teepot::json::secrets::{AdminConfig, AdminState}; -use teepot::server::{HttpResponseError, Status}; +use teepot_vault::client::vault::VaultConnection; +use teepot_vault::client::TeeConnection; +use teepot_vault::json::http::Unseal; +use teepot_vault::json::secrets::{AdminConfig, AdminState}; +use teepot_vault::server::{HttpResponseError, Status}; use tracing::{debug, error, info, instrument, trace}; #[instrument(level = "info", name = "/v1/sys/unseal", skip_all)] diff --git a/bin/teepot-read/Cargo.toml b/crates/teepot-vault/bin/teepot-read/Cargo.toml similarity index 94% rename from bin/teepot-read/Cargo.toml rename to crates/teepot-vault/bin/teepot-read/Cargo.toml index 5c3c8e2..1e3a8c1 100644 --- a/bin/teepot-read/Cargo.toml +++ b/crates/teepot-vault/bin/teepot-read/Cargo.toml @@ -15,7 +15,7 @@ anyhow.workspace = true awc.workspace = true clap.workspace = true serde_json.workspace = true -teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/bin/teepot-read/src/main.rs b/crates/teepot-vault/bin/teepot-read/src/main.rs similarity index 95% rename from bin/teepot-read/src/main.rs rename to crates/teepot-vault/bin/teepot-read/src/main.rs index 1b1afe2..e98b014 100644 --- a/bin/teepot-read/src/main.rs +++ b/crates/teepot-vault/bin/teepot-read/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Get the secrets from a Vault TEE and pass them as environment variables to a command @@ -12,8 +12,8 @@ use serde_json::Value; use std::collections::HashMap; use std::os::unix::process::CommandExt; use std::process::Command; -use teepot::client::vault::VaultConnection; -use teepot::server::attestation::VaultAttestationArgs; +use teepot_vault::client::vault::VaultConnection; +use teepot_vault::server::attestation::VaultAttestationArgs; use tracing::{debug, info, warn}; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; diff --git a/bin/teepot-write/Cargo.toml b/crates/teepot-vault/bin/teepot-write/Cargo.toml similarity index 94% rename from bin/teepot-write/Cargo.toml rename to crates/teepot-vault/bin/teepot-write/Cargo.toml index a2f6c64..8933de0 100644 --- a/bin/teepot-write/Cargo.toml +++ b/crates/teepot-vault/bin/teepot-write/Cargo.toml @@ -15,7 +15,7 @@ anyhow.workspace = true awc.workspace = true clap.workspace = true serde_json.workspace = true -teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/bin/teepot-write/src/main.rs b/crates/teepot-vault/bin/teepot-write/src/main.rs similarity index 94% rename from bin/teepot-write/src/main.rs rename to crates/teepot-vault/bin/teepot-write/src/main.rs index 04fea08..b7f0c94 100644 --- a/bin/teepot-write/src/main.rs +++ b/crates/teepot-vault/bin/teepot-write/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Write secrets to a Vault TEE from environment variables @@ -9,10 +9,8 @@ use anyhow::{Context, Result}; use clap::Parser; use serde_json::Value; -use std::collections::HashMap; -use std::env; -use teepot::client::vault::VaultConnection; -use teepot::server::attestation::VaultAttestationArgs; +use std::{collections::HashMap, env}; +use teepot_vault::{client::vault::VaultConnection, server::attestation::VaultAttestationArgs}; use tracing::{debug, info, warn}; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; diff --git a/bin/vault-admin/Cargo.toml b/crates/teepot-vault/bin/vault-admin/Cargo.toml similarity index 86% rename from bin/vault-admin/Cargo.toml rename to crates/teepot-vault/bin/vault-admin/Cargo.toml index 3bd199b..270c6fb 100644 --- a/bin/vault-admin/Cargo.toml +++ b/crates/teepot-vault/bin/vault-admin/Cargo.toml @@ -16,6 +16,5 @@ hex.workspace = true pgp.workspace = true serde_json.workspace = true teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true -tracing-log.workspace = true -tracing-subscriber.workspace = true diff --git a/bin/vault-admin/README.md b/crates/teepot-vault/bin/vault-admin/README.md similarity index 94% rename from bin/vault-admin/README.md rename to crates/teepot-vault/bin/vault-admin/README.md index 7685279..90c0935 100644 --- a/bin/vault-admin/README.md +++ b/crates/teepot-vault/bin/vault-admin/README.md @@ -9,12 +9,12 @@ Verified signature for `81A312C59D679D930FA9E8B06D728F29A2DBABF8` ❯ RUST_LOG=info cargo run -p vault-admin -- \ - send \ + command \ --sgx-mrsigner c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d \ --sgx-allowed-tcb-levels SwHardeningNeeded \ --server https://127.0.0.1:8444 \ - bin/tee-vault-admin/tests/data/test.json \ - bin/tee-vault-admin/tests/data/test.json.asc + crates/teepot-vault/tests/data/test.json \ + crates/teepot-vault/tests/data/test.json.asc 2023-08-04T10:51:14.919941Z INFO vault_admin: Quote verified! Connection secure! 2023-08-04T10:51:14.920430Z INFO tee_client: Getting attestation report diff --git a/bin/vault-admin/src/main.rs b/crates/teepot-vault/bin/vault-admin/src/main.rs similarity index 90% rename from bin/vault-admin/src/main.rs rename to crates/teepot-vault/bin/vault-admin/src/main.rs index 91d17e0..b11c3de 100644 --- a/bin/vault-admin/src/main.rs +++ b/crates/teepot-vault/bin/vault-admin/src/main.rs @@ -1,26 +1,32 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs use anyhow::{anyhow, bail, Context, Result}; use clap::{Args, Parser, Subcommand}; -use pgp::types::KeyTrait; -use pgp::{Deserializable, SignedPublicKey}; -use serde_json::Value; -use std::default::Default; -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use teepot::client::{AttestationArgs, TeeConnection}; -use teepot::json::http::{ - SignRequest, SignRequestData, SignResponse, VaultCommandRequest, VaultCommands, - VaultCommandsResponse, DIGEST_URL, +use pgp::{ + composed::{Deserializable, SignedPublicKey}, + types::KeyDetails, }; -use teepot::server::signatures::verify_sig; -use teepot::sgx::sign::Signature; -use tracing::{error, info}; -use tracing_log::LogTracer; -use tracing_subscriber::Registry; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use serde_json::Value; +use std::{ + default::Default, + fs::{File, OpenOptions}, + io::{Read, Write}, + path::{Path, PathBuf}, +}; +use teepot::{ + log::{setup_logging, LogLevelParser}, + sgx::sign::Signature, +}; +use teepot_vault::{ + client::{AttestationArgs, TeeConnection}, + json::http::{ + SignRequest, SignRequestData, SignResponse, VaultCommandRequest, VaultCommands, + VaultCommandsResponse, DIGEST_URL, + }, + server::signatures::verify_sig, +}; +use tracing::{error, level_filters::LevelFilter}; #[derive(Args, Debug)] struct SendArgs { @@ -101,19 +107,20 @@ enum SubCommands { struct Arguments { #[clap(subcommand)] cmd: SubCommands, + /// Log level for the log output. + /// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace` + #[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)] + pub log_level: LevelFilter, } #[actix_web::main] async fn main() -> Result<()> { - LogTracer::init().context("Failed to set logger")?; - - let subscriber = Registry::default() - .with(EnvFilter::from_default_env()) - .with(fmt::layer().with_writer(std::io::stderr)); - tracing::subscriber::set_global_default(subscriber).unwrap(); - let args = Arguments::parse(); - info!("Quote verified! Connection secure!"); + + tracing::subscriber::set_global_default(setup_logging( + env!("CARGO_CRATE_NAME"), + &args.log_level, + )?)?; match args.cmd { SubCommands::Command(args) => send_commands(args).await?, @@ -188,7 +195,7 @@ fn verify( let ident_pos = verify_sig(&sig, &cmd_buf, &idents)?; println!( "Verified signature for `{}`", - hex::encode_upper(idents.get(ident_pos).unwrap().fingerprint()) + hex::encode_upper(idents.get(ident_pos).unwrap().fingerprint().as_bytes()) ); // Remove the identity from the list of identities to verify idents.remove(ident_pos); diff --git a/bin/vault-unseal/Cargo.toml b/crates/teepot-vault/bin/vault-unseal/Cargo.toml similarity index 93% rename from bin/vault-unseal/Cargo.toml rename to crates/teepot-vault/bin/vault-unseal/Cargo.toml index b191a04..ecba7f5 100644 --- a/bin/vault-unseal/Cargo.toml +++ b/crates/teepot-vault/bin/vault-unseal/Cargo.toml @@ -13,7 +13,7 @@ anyhow.workspace = true base64.workspace = true clap.workspace = true serde_json.workspace = true -teepot.workspace = true +teepot-vault.workspace = true tracing.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/bin/vault-unseal/src/main.rs b/crates/teepot-vault/bin/vault-unseal/src/main.rs similarity index 95% rename from bin/vault-unseal/src/main.rs rename to crates/teepot-vault/bin/vault-unseal/src/main.rs index 29e2bc7..c46b1e3 100644 --- a/bin/vault-unseal/src/main.rs +++ b/crates/teepot-vault/bin/vault-unseal/src/main.rs @@ -1,18 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs use anyhow::{anyhow, bail, Context, Result}; use base64::{engine::general_purpose, Engine as _}; use clap::{Args, Parser, Subcommand}; use serde_json::Value; -use std::fs::File; -use std::io::Read; -use teepot::client::{AttestationArgs, TeeConnection}; -use teepot::json::http::{Init, InitResponse, Unseal}; +use std::{fs::File, io::Read}; +use teepot_vault::{ + client::{AttestationArgs, TeeConnection}, + json::http::{Init, InitResponse, Unseal}, +}; use tracing::{error, info, trace, warn}; use tracing_log::LogTracer; -use tracing_subscriber::Registry; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; #[derive(Args, Debug)] pub struct InitArgs { diff --git a/crates/teepot/src/client/mod.rs b/crates/teepot-vault/src/client/mod.rs similarity index 85% rename from crates/teepot/src/client/mod.rs rename to crates/teepot-vault/src/client/mod.rs index 3b21841..85fa871 100644 --- a/crates/teepot/src/client/mod.rs +++ b/crates/teepot-vault/src/client/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Helper functions for CLI clients to verify Intel SGX enclaves and other TEEs. @@ -8,29 +8,35 @@ pub mod vault; -use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension}; -use crate::sgx::Quote; -pub use crate::sgx::{ - parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet, - QuoteVerificationResult, TcbLevel, +use crate::server::{ + attestation::Collateral, + pki::{RaTlsCollateralExtension, RaTlsQuoteExtension}, }; use actix_web::http::header; use anyhow::Result; use awc::{Client, Connector}; use clap::Args; use const_oid::AssociatedOid; -use intel_tee_quote_verification_rs::Collateral; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::client::WebPkiServerVerifier; -use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; -use rustls::{ClientConfig, DigitallySignedStruct, Error, SignatureScheme}; +use rustls::{ + client::{ + danger::{HandshakeSignatureValid, ServerCertVerifier}, + WebPkiServerVerifier, + }, + pki_types::{CertificateDer, ServerName, UnixTime}, + ClientConfig, DigitallySignedStruct, Error, SignatureScheme, +}; use sha2::{Digest, Sha256}; -use std::sync::Arc; -use std::time; -use std::time::Duration; +use std::{sync::Arc, time, time::Duration}; +pub use teepot::quote::{ + tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}, + verify_quote_with_collateral, QuoteVerificationResult, +}; +use teepot::quote::{Quote, Report}; use tracing::{debug, error, info, trace, warn}; -use x509_cert::der::{Decode as _, Encode as _}; -use x509_cert::Certificate; +use x509_cert::{ + der::{Decode as _, Encode as _}, + Certificate, +}; /// Options and arguments needed to attest a TEE #[derive(Args, Debug, Clone)] @@ -63,6 +69,8 @@ impl TeeConnection { /// This will verify the attestation report and check that the enclave /// is running the expected code. pub fn new(args: &AttestationArgs) -> Self { + let _ = rustls::crypto::ring::default_provider().install_default(); + let tls_config = Arc::new( ClientConfig::builder() .dangerous() @@ -73,7 +81,7 @@ impl TeeConnection { let agent = Client::builder() .add_default_header((header::USER_AGENT, "teepot/1.0")) // a "connector" wraps the stream into an encrypted connection - .connector(Connector::new().rustls_0_22(tls_config)) + .connector(Connector::new().rustls_0_23(tls_config)) .timeout(Duration::from_secs(12000)) .finish(); @@ -166,17 +174,17 @@ impl TeeConnection { debug!("Failed to get collateral in certificate"); } - let quote = Quote::try_from_bytes(quote_bytes).map_err(|e| { + let quote = Quote::parse(quote_bytes).map_err(|e| { Error::General(format!("Failed get quote in certificate {e:?}")) })?; - if "e.report_body.reportdata[..32] != hash.as_slice() { + if "e.get_report_data()[..32] != hash.as_slice() { error!("Report data mismatch"); return Err(Error::General("Report data mismatch".to_string())); } else { info!( "Report data matches `{}`", - hex::encode("e.report_body.reportdata[..32]) + hex::encode("e.get_report_data()[..32]) ); } @@ -187,7 +195,7 @@ impl TeeConnection { let QuoteVerificationResult { collateral_expired, - result, + result: tcblevel, quote, advisories, earliest_expiration_date, @@ -195,7 +203,11 @@ impl TeeConnection { } = verify_quote_with_collateral(quote_bytes, collateral.as_ref(), current_time) .unwrap(); - if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK { + let Report::SgxEnclave(report_body) = quote.report else { + return Err(Error::General("TDX quote and not SGX quote".into())); + }; + + if collateral_expired || tcblevel != TcbLevel::Ok { if collateral_expired { error!( "Collateral is out of date! Expired {}", @@ -207,11 +219,10 @@ impl TeeConnection { ))); } - let tcblevel = TcbLevel::from(result); if self .args .sgx_allowed_tcb_levels - .map_or(true, |levels| !levels.contains(tcblevel)) + .is_none_or(|levels| !levels.contains(tcblevel)) { error!("Quote verification result: {}", tcblevel); return Err(Error::General(format!( @@ -230,10 +241,10 @@ impl TeeConnection { if let Some(mrsigner) = &self.args.sgx_mrsigner { let mrsigner_bytes = hex::decode(mrsigner) .map_err(|e| Error::General(format!("Failed to decode mrsigner: {}", e)))?; - if quote.report_body.mrsigner[..] != mrsigner_bytes { + if report_body.mr_signer[..] != mrsigner_bytes { return Err(Error::General(format!( "mrsigner mismatch: got {}, expected {}", - hex::encode(quote.report_body.mrsigner), + hex::encode(report_body.mr_signer), &mrsigner ))); } else { @@ -245,10 +256,10 @@ impl TeeConnection { let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| { Error::General(format!("Failed to decode mrenclave: {}", e)) })?; - if quote.report_body.mrenclave[..] != mrenclave_bytes { + if report_body.mr_enclave[..] != mrenclave_bytes { return Err(Error::General(format!( "mrenclave mismatch: got {}, expected {}", - hex::encode(quote.report_body.mrenclave), + hex::encode(report_body.mr_enclave), &mrenclave ))); } else { @@ -256,6 +267,8 @@ impl TeeConnection { } } + info!("Quote verified! Connection secure!"); + Ok(rustls::client::danger::ServerCertVerified::assertion()) } diff --git a/crates/teepot/src/client/vault.rs b/crates/teepot-vault/src/client/vault.rs similarity index 91% rename from crates/teepot/src/client/vault.rs rename to crates/teepot-vault/src/client/vault.rs index 8d7ef43..c3cf4ad 100644 --- a/crates/teepot/src/client/vault.rs +++ b/crates/teepot-vault/src/client/vault.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Helper functions for CLI clients to verify Intel SGX enclaves and other TEEs. @@ -7,26 +7,34 @@ #![deny(clippy::all)] use super::{AttestationArgs, TeeConnection}; -use crate::json::http::{AuthRequest, AuthResponse}; -use crate::server::pki::make_self_signed_cert; -use crate::server::{AnyHowResponseError, HttpResponseError, Status}; -pub use crate::sgx::{ - parse_tcb_levels, sgx_gramine_get_quote, sgx_ql_qv_result_t, tee_qv_get_collateral, - verify_quote_with_collateral, Collateral, EnumSet, QuoteVerificationResult, TcbLevel, +use crate::{ + json::http::{AuthRequest, AuthResponse}, + server::{pki::make_self_signed_cert, AnyHowResponseError, HttpResponseError, Status}, }; use actix_http::error::PayloadError; -use actix_web::http::header; -use actix_web::ResponseError; +use actix_web::{http::header, ResponseError}; use anyhow::{anyhow, bail, Context, Result}; -use awc::error::{SendRequestError, StatusCode}; -use awc::{Client, ClientResponse, Connector}; +use awc::{ + error::{SendRequestError, StatusCode}, + Client, ClientResponse, Connector, +}; use bytes::Bytes; use futures_core::Stream; use rustls::ClientConfig; use serde_json::{json, Value}; -use std::fmt::{Display, Formatter}; -use std::sync::Arc; -use std::time; +use std::{ + fmt::{Display, Formatter}, + sync::Arc, + time, +}; +use teepot::quote::get_collateral; +pub use teepot::{ + quote::{ + tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}, + verify_quote_with_collateral, QuoteVerificationResult, + }, + sgx::{sgx_gramine_get_quote, Collateral}, +}; use tracing::{debug, error, info, trace}; const VAULT_TOKEN_HEADER: &str = "X-Vault-Token"; @@ -99,7 +107,7 @@ impl VaultConnection { let client = Client::builder() .add_default_header((header::USER_AGENT, "teepot/1.0")) // a "connector" wraps the stream into an encrypted connection - .connector(Connector::new().rustls_0_22(tls_config)) + .connector(Connector::new().rustls_0_23(tls_config)) .timeout(time::Duration::from_secs(12000)) .finish(); @@ -149,7 +157,7 @@ impl VaultConnection { info!("Getting attestation report"); let attestation_url = AuthRequest::URL; let quote = sgx_gramine_get_quote(&self.key_hash).context("Failed to get own quote")?; - let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?; + let collateral = get_collateral("e).context("Failed to get own collateral")?; let auth_req = AuthRequest { name: self.name.clone(), @@ -289,7 +297,7 @@ impl VaultConnection { } /// set a secret in the vault - pub async fn store_secret<'de, T: serde::Serialize>( + pub async fn store_secret( &self, val: T, rel_path: &str, @@ -298,7 +306,7 @@ impl VaultConnection { } /// set a secret in the vault for a different TEE - pub async fn store_secret_for_tee<'de, T: serde::Serialize>( + pub async fn store_secret_for_tee( &self, tee_name: &str, val: T, @@ -322,7 +330,7 @@ impl VaultConnection { } /// get a secret from the vault - pub async fn load_secret<'de, T: serde::de::DeserializeOwned>( + pub async fn load_secret( &self, rel_path: &str, ) -> Result, HttpResponseError> { @@ -330,7 +338,7 @@ impl VaultConnection { } /// get a secret from the vault for a specific TEE - pub async fn load_secret_for_tee<'de, T: serde::de::DeserializeOwned>( + pub async fn load_secret_for_tee( &self, tee_name: &str, rel_path: &str, diff --git a/crates/teepot/src/json/http.rs b/crates/teepot-vault/src/json/http.rs similarity index 95% rename from crates/teepot/src/json/http.rs rename to crates/teepot-vault/src/json/http.rs index d450906..4713797 100644 --- a/crates/teepot/src/json/http.rs +++ b/crates/teepot-vault/src/json/http.rs @@ -1,15 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Common types for the teepot http JSON API -use crate::sgx::Collateral; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_with::base64::Base64; -use serde_with::serde_as; +use serde_with::{base64::Base64, serde_as}; use std::fmt::Display; -use std::sync::Arc; /// The unseal request data #[derive(Debug, Serialize, Deserialize)] @@ -23,15 +20,6 @@ impl Unseal { pub const URL: &'static str = "/v1/sys/unseal"; } -/// The attestation response -#[derive(Debug, Serialize, Deserialize)] -pub struct AttestationResponse { - /// The quote - pub quote: Arc<[u8]>, - /// The collateral - pub collateral: Arc, -} - /// The init request data #[serde_as] #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/teepot/src/json/mod.rs b/crates/teepot-vault/src/json/mod.rs similarity index 100% rename from crates/teepot/src/json/mod.rs rename to crates/teepot-vault/src/json/mod.rs diff --git a/crates/teepot/src/json/secrets.rs b/crates/teepot-vault/src/json/secrets.rs similarity index 92% rename from crates/teepot/src/json/secrets.rs rename to crates/teepot-vault/src/json/secrets.rs index dd1fae5..7e60179 100644 --- a/crates/teepot/src/json/secrets.rs +++ b/crates/teepot-vault/src/json/secrets.rs @@ -1,12 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Common types for the teepot secrets JSON API -use crate::sgx::sign::Zeroizing; use serde::{Deserialize, Serialize}; use serde_with::base64::Base64; use serde_with::serde_as; +use teepot::sgx::sign::Zeroizing; /// Configuration for the admin tee #[serde_as] diff --git a/crates/teepot-vault/src/lib.rs b/crates/teepot-vault/src/lib.rs new file mode 100644 index 0000000..91fa082 --- /dev/null +++ b/crates/teepot-vault/src/lib.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Helper functions to verify Intel SGX enclaves and other TEEs. + +#![deny(missing_docs)] +#![deny(clippy::all)] + +pub mod client; +pub mod json; +pub mod server; + +/// pad a byte slice to a fixed sized array +pub fn pad(input: &[u8]) -> [u8; T] { + let mut output = [0; T]; + let len = input.len(); + if len > T { + output.copy_from_slice(&input[..T]); + } else { + output[..len].copy_from_slice(input); + } + output +} diff --git a/crates/teepot-vault/src/server/attestation.rs b/crates/teepot-vault/src/server/attestation.rs new file mode 100644 index 0000000..c710f96 --- /dev/null +++ b/crates/teepot-vault/src/server/attestation.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Common attestation API for all TEEs + +use crate::client::AttestationArgs; +use clap::Args; +use serde::{Deserialize, Serialize}; + +pub use teepot::{ + quote::{ + attestation::get_quote_and_collateral, + error::QuoteContext, + get_quote, + tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}, + verify_quote_with_collateral, QuoteVerificationResult, + }, + sgx::Collateral, +}; + +/// Options and arguments needed to attest a TEE +#[derive(Args, Debug, Clone, Serialize, Deserialize, Default)] +pub struct VaultAttestationArgs { + /// hex encoded SGX mrsigner of the enclave to attest + #[arg(long, env = "VAULT_SGX_MRSIGNER")] + pub vault_sgx_mrsigner: Option, + /// hex encoded SGX mrenclave of the enclave to attest + #[arg(long, env = "VAULT_SGX_MRENCLAVE")] + pub vault_sgx_mrenclave: Option, + /// URL of the server + #[arg(long, required = true, env = "VAULT_ADDR")] + pub vault_addr: String, + /// allowed TCB levels, comma separated: + /// Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, OutOfDate, OutOfDateConfigNeeded + #[arg(long, value_parser = parse_tcb_levels, env = "VAULT_SGX_ALLOWED_TCB_LEVELS")] + pub vault_sgx_allowed_tcb_levels: Option>, +} + +impl From for AttestationArgs { + fn from(value: VaultAttestationArgs) -> Self { + AttestationArgs { + sgx_mrsigner: value.vault_sgx_mrsigner, + sgx_mrenclave: value.vault_sgx_mrenclave, + server: value.vault_addr, + sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels, + } + } +} + +impl From<&VaultAttestationArgs> for AttestationArgs { + fn from(value: &VaultAttestationArgs) -> Self { + AttestationArgs { + sgx_mrsigner: value.vault_sgx_mrsigner.clone(), + sgx_mrenclave: value.vault_sgx_mrenclave.clone(), + server: value.vault_addr.clone(), + sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels, + } + } +} diff --git a/crates/teepot/src/server/mod.rs b/crates/teepot-vault/src/server/mod.rs similarity index 94% rename from crates/teepot/src/server/mod.rs rename to crates/teepot-vault/src/server/mod.rs index 2657e04..bc6c233 100644 --- a/crates/teepot/src/server/mod.rs +++ b/crates/teepot-vault/src/server/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! # tee-server @@ -7,18 +7,19 @@ #![deny(clippy::all)] pub mod attestation; -pub mod pki; pub mod signatures; -use actix_web::http::StatusCode; -use actix_web::web::Bytes; -use actix_web::{error, HttpRequest, HttpResponse}; -use actix_web::{HttpMessage, ResponseError}; +use actix_web::{ + error, http::StatusCode, web::Bytes, HttpMessage, HttpRequest, HttpResponse, ResponseError, +}; use anyhow::anyhow; -use awc::error::{PayloadError, SendRequestError}; -use awc::ClientResponse; +use awc::{ + error::{PayloadError, SendRequestError}, + ClientResponse, +}; use futures_core::Stream; use std::fmt::{Debug, Display, Formatter}; +pub use teepot::pki; use tracing::error; /// Anyhow error with an HTTP status code diff --git a/crates/teepot/src/server/signatures.rs b/crates/teepot-vault/src/server/signatures.rs similarity index 92% rename from crates/teepot/src/server/signatures.rs rename to crates/teepot-vault/src/server/signatures.rs index e91aa93..b3d6808 100644 --- a/crates/teepot/src/server/signatures.rs +++ b/crates/teepot-vault/src/server/signatures.rs @@ -1,14 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 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::KeyTrait; -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"); diff --git a/crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc b/crates/teepot-vault/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc similarity index 100% rename from crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc rename to crates/teepot-vault/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc diff --git a/crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 b/crates/teepot-vault/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 similarity index 100% rename from crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 rename to crates/teepot-vault/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 diff --git a/crates/teepot/tests/data/test.json b/crates/teepot-vault/tests/data/test.json similarity index 100% rename from crates/teepot/tests/data/test.json rename to crates/teepot-vault/tests/data/test.json diff --git a/crates/teepot/tests/data/test.json.asc b/crates/teepot-vault/tests/data/test.json.asc similarity index 100% rename from crates/teepot/tests/data/test.json.asc rename to crates/teepot-vault/tests/data/test.json.asc diff --git a/crates/teepot/tests/data/test2.json b/crates/teepot-vault/tests/data/test2.json similarity index 100% rename from crates/teepot/tests/data/test2.json rename to crates/teepot-vault/tests/data/test2.json diff --git a/crates/teepot/tests/data/test2.json.asc b/crates/teepot-vault/tests/data/test2.json.asc similarity index 100% rename from crates/teepot/tests/data/test2.json.asc rename to crates/teepot-vault/tests/data/test2.json.asc diff --git a/crates/teepot/Cargo.toml b/crates/teepot/Cargo.toml index a2c48bf..b10fb7f 100644 --- a/crates/teepot/Cargo.toml +++ b/crates/teepot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "teepot" -description = "TEE secret manager" +description = "TEE utilities" # no MIT license, because of copied code from: # * https://github.com/enarx/enarx # * https://github.com/enarx/sgx @@ -10,41 +10,57 @@ edition.workspace = true authors.workspace = true repository.workspace = true -[dependencies] -actix-http.workspace = true -actix-web.workspace = true -anyhow.workspace = true -awc.workspace = true -bytemuck.workspace = true +[target.'cfg(not(all(target_os = "linux", target_arch = "x86_64")))'.dependencies] +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-core.workspace = true +futures.workspace = true getrandom.workspace = true hex.workspace = true -intel-tee-quote-verification-rs.workspace = true num-integer.workspace = true num-traits.workspace = true +opentelemetry.workspace = true +opentelemetry-appender-tracing.workspace = true +opentelemetry-otlp.workspace = true +opentelemetry-semantic-conventions.workspace = true +opentelemetry_sdk.workspace = true p256.workspace = true -pgp.workspace = true pkcs8.workspace = true -rand.workspace = true +reqwest.workspace = true rsa.workspace = true rustls.workspace = true +secp256k1 = { workspace = true, features = ["recovery"] } serde.workspace = true serde_json.workspace = true -serde_with.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 -webpki-roots.workspace = true +tracing-futures.workspace = true +tracing-log.workspace = true +tracing-subscriber.workspace = true x509-cert.workspace = true zeroize.workspace = true [dev-dependencies] -anyhow.workspace = true base64.workspace = true -hex.workspace = true testaso.workspace = true +tokio.workspace = true +tracing-test.workspace = true diff --git a/crates/teepot/README.md b/crates/teepot/README.md new file mode 100644 index 0000000..2ab3478 --- /dev/null +++ b/crates/teepot/README.md @@ -0,0 +1,72 @@ +# teepot + +TEE (Trusted Execution Environment) utilities for Intel SGX and TDX attestation. + +## Overview + +Teepot provides comprehensive support for generating and verifying attestation quotes from Intel SGX enclaves and TDX trust domains. It handles the complete attestation workflow including quote generation, collateral fetching, and verification with detailed TCB (Trusted Computing Base) status reporting. + +## Features + +- **Multi-TEE Support**: Works with both Intel SGX and Intel TDX +- **Attestation Quote Generation**: Generate quotes with custom report data +- **Quote Verification**: Verify quotes with automatic collateral fetching +- **TCB Level Management**: Filter quotes by security level +- **Cross-Platform**: Native support for Linux x86_64, fallback implementation for other platforms +- **Gramine SGX Support**: Special support for Gramine-based SGX enclaves +- **Comprehensive Error Handling**: Detailed error context for debugging + +## Usage + +### Basic Quote Generation and Verification + +```rust +use teepot::quote::{get_quote, get_collateral, verify_quote_with_collateral}; + +// Generate a quote with custom data +let report_data = [0u8; 64]; // Your custom data here +let quote = get_quote(&report_data)?; + +// Fetch collateral for verification +let collateral = get_collateral("e)?; + +// Verify the quote +let result = verify_quote_with_collateral("e, collateral.as_ref(), None)?; +println!("TCB Level: {:?}", result.tcb_level); +``` + +### High-Level Attestation API + +```rust +use teepot::quote::attestation::get_quote_and_collateral; + +// Generate quote and fetch collateral in one call +let (quote, collateral, result) = get_quote_and_collateral(&report_data)?; +``` + +### TCB Level Filtering + +```rust +use teepot::quote::{TcbLevel, verify_quote_with_collateral}; + +// Only accept quotes with up-to-date TCB +let accepted_levels = vec![TcbLevel::Ok]; +let result = verify_quote_with_collateral("e, collateral.as_ref(), Some(&accepted_levels))?; +``` + +## Supported Platforms + +- **Full support**: Linux x86_64 with Intel SGX/TDX drivers +- **Verification only**: All other platforms via `dcap-qvl` + +## Dependencies + +On Linux x86_64, the crate uses Intel's DCAP libraries for quote generation. Make sure you have: +- Intel SGX DCAP Quote Generation Library +- Intel SGX DCAP Quote Verification Library + +## License + +This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details. + +Note: Some code is derived from the [Enarx project](https://github.com/enarx/). \ No newline at end of file diff --git a/crates/teepot/src/config/mod.rs b/crates/teepot/src/config/mod.rs new file mode 100644 index 0000000..95ae15f --- /dev/null +++ b/crates/teepot/src/config/mod.rs @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Configuration handling + +use async_trait::async_trait; +use config::{ + builder::AsyncState, AsyncSource, Config, ConfigBuilder, ConfigError, File, FileFormat, Format, + Map, +}; +use opentelemetry::KeyValue; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::{logs::SdkLoggerProvider, Resource}; +use opentelemetry_semantic_conventions::{ + attribute::{SERVICE_NAME, SERVICE_VERSION}, + SCHEMA_URL, +}; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use tracing::trace; +use tracing_subscriber::{ + fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, +}; + +const DEFAULT_INSTANCE_METADATA_BASE_URL: &str = + "http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_config"; + +/// Get the configuration via HTTP +#[derive(Debug)] +pub struct HttpSource { + /// the URI + pub uri: String, + /// the expected format + pub format: F, + /// if this is required, it will error, if not available + pub required: bool, +} + +#[async_trait] +impl AsyncSource for HttpSource { + async fn collect(&self) -> Result, ConfigError> { + let response = match reqwest::Client::builder() + .build() + .map_err(|e| ConfigError::Foreign(Box::new(e)))? + .get(&self.uri) + .header("Metadata-Flavor", "Google") + .send() + .await + .map_err(|e| ConfigError::Foreign(Box::new(e))) + { + Ok(response) => response, + Err(e) => { + if self.required { + return Err(e); + } else { + return Ok(Map::new()); + } + } + }; + + // error conversion is possible from custom AsyncSource impls + response + .text() + .await + .map_err(|e| ConfigError::Foreign(Box::new(e))) + .and_then(|text| { + self.format + .parse(Some(&self.uri), &text) + .map_err(ConfigError::Foreign) + }) + .or_else(|res| { + if self.required { + Err(res) + } else { + Ok(Map::new()) + } + }) + } +} + +/// Main telemetry configuration container +#[derive(Debug, Serialize, Deserialize)] +pub struct TelemetryConfig { + /// The crate name `env!("CARGO_CRATE_NAME")` + pub crate_name: String, + /// The package version `env!("CARGO_PKG_VERSION")` + pub pkg_version: String, + /// OpenTelemetry Protocol (OTLP) specific settings + pub otlp: TelemetryOtlpConfig, + /// Logging-specific configuration + pub logging: TelemetryLoggingConfig, +} + +impl TelemetryConfig { + /// Create a new TelemetryConfig, usually with + /// ```rust, + /// # use teepot::config::TelemetryConfig; + /// let telemetry_config = TelemetryConfig::new( + /// env!("CARGO_CRATE_NAME").into(), + /// env!("CARGO_PKG_VERSION").into(), + /// ); + /// ``` + pub fn new(crate_name: String, pkg_version: String) -> Self { + Self { + crate_name, + pkg_version, + otlp: TelemetryOtlpConfig::default(), + logging: TelemetryLoggingConfig::default(), + } + } +} + +/// Configuration for logging behavior +#[derive(Debug, Serialize, Deserialize)] +pub struct TelemetryLoggingConfig { + /// The logging level (e.g., "debug", "info", "warn", "error") + pub level: String, + /// Whether to output logs in JSON format + pub json: bool, + /// Whether to output logs to console + pub console: bool, +} + +impl Default for TelemetryLoggingConfig { + fn default() -> Self { + Self { + level: "warn".into(), + json: false, + console: true, + } + } +} + +/// OpenTelemetry Protocol specific configuration +#[derive(Debug, Serialize, Deserialize)] +pub struct TelemetryOtlpConfig { + /// Controls whether OpenTelemetry Protocol (OTLP) export is enabled. + /// FIXME: has no effect right now + pub enable: bool, + /// The endpoint URL for the OpenTelemetry collector + pub endpoint: String, + /// The protocol to use for OTLP communication (e.g., "grpc", "http/protobuf") + pub protocol: String, +} + +impl Default for TelemetryOtlpConfig { + fn default() -> Self { + Self { + enable: true, + endpoint: "http://127.0.0.1:4318/v1/logs".to_string(), + protocol: "http/json".to_string(), + } + } +} + +fn protocol_from_string(protocol: &str) -> Result { + match protocol.to_lowercase().as_str() { + "http/protobuf" => Ok(opentelemetry_otlp::Protocol::HttpBinary), + "http/json" => Ok(opentelemetry_otlp::Protocol::HttpJson), + "grpc" => Ok(opentelemetry_otlp::Protocol::Grpc), + _ => Err(anyhow::anyhow!("Invalid protocol")), + } +} + +/// Loads configuration and sets up logging based on the provided configuration accessor +/// +/// # Type Parameters +/// * `S` - Configuration type that implements Default, Serialize, Deserialize, and Send +/// +/// # Arguments +/// * `get_telemetry_config` - Function to extract `TelemetryConfig` from type `S` +/// +/// # Returns +/// * `Result` - The loaded configuration or error +pub async fn load_config_with_telemetry< + S: Default + Serialize + for<'a> Deserialize<'a> + Send + 'static, +>( + env_prefix: String, + get_telemetry_config: fn(&S) -> &TelemetryConfig, +) -> Result> { + with_console_logging(async move { + trace!("Loading config"); + // Load configuration + let config = { + let c = ConfigBuilder::::default() + .add_source(Config::try_from(&S::default())?) + .add_source(File::with_name("config/default").required(false)) + .add_source( + config::Environment::with_prefix(&env_prefix) + .try_parsing(true) + .separator("_"), + ); + + if std::env::var_os("GOOGLE_METADATA").is_some() { + c.add_async_source(HttpSource { + uri: DEFAULT_INSTANCE_METADATA_BASE_URL.into(), + format: FileFormat::Json, + required: false, + }) + .build() + .await? + .try_deserialize::()? + } else { + c.build().await?.try_deserialize::()? + } + }; + + // Initialize telemetry + init_telemetry(get_telemetry_config(&config))?; + Ok::>(config) + }) + .await +} + +fn create_console_format_layer() -> tracing_subscriber::fmt::Layer +where + S: for<'a> tracing::Subscriber + Send + Sync + 'static, +{ + tracing_subscriber::fmt::layer() + .with_target(true) + .with_thread_ids(true) + .with_line_number(true) + .with_file(true) + .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) + .with_ansi(true) + .with_thread_names(true) +} + +async fn with_console_logging(fut: F) -> F::Output +where + F: std::future::Future + Send + 'static, + F::Output: Send + 'static, +{ + // Configure console logging + let fmt_layer = create_console_format_layer(); + + let subs = tracing_subscriber::registry() + .with(EnvFilter::new("trace")) + .with(fmt_layer.pretty()); + + let _default = tracing::subscriber::set_default(subs); + + tracing_futures::WithSubscriber::with_current_subscriber(fut).await +} + +fn init_telemetry( + config: &TelemetryConfig, +) -> Result<(), Box> { + std::env::set_var( + "RUST_LOG", + std::env::var("RUST_LOG").unwrap_or_else(|_| { + format!( + // `otel::tracing` should be a level info to emit opentelemetry trace & span + // `otel::setup` set to debug to log detected resources, configuration read and infered + "warn,{crate_name}={log_level},teepot={log_level},otel::tracing=error,otel=error", + log_level = config.logging.level, + crate_name = config.crate_name + ) + }), + ); + // Configure OpenTelemetry resource + let resource = Resource::builder() + .with_schema_url( + [ + KeyValue::new(SERVICE_NAME, config.crate_name.clone()), + KeyValue::new(SERVICE_VERSION, config.pkg_version.clone()), + ], + SCHEMA_URL, + ) + .build(); + + // Parse the protocol from the configuration + let protocol = protocol_from_string(&config.otlp.protocol)?; + + // Configure the OTLP exporter based on the protocol + let exporter_builder = opentelemetry_otlp::LogExporter::builder(); + + // Choose transport based on protocol + let exporter = match protocol { + opentelemetry_otlp::Protocol::Grpc => exporter_builder + .with_tonic() + .with_endpoint(&config.otlp.endpoint) + .with_protocol(protocol) + .build()?, + opentelemetry_otlp::Protocol::HttpBinary | opentelemetry_otlp::Protocol::HttpJson => { + exporter_builder + .with_http() + .with_endpoint(&config.otlp.endpoint) + .with_protocol(protocol) + .build()? + } + }; + + // Configure the logging provider with the exporter + let logging_provider = SdkLoggerProvider::builder() + .with_batch_exporter(exporter) + .with_resource(resource) + .build(); + + let logging_layer = + opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new(&logging_provider); + + // Configure console logging + let fmt_layer = create_console_format_layer(); + + // Combine layers based on configuration + let subscriber = tracing_subscriber::registry() + .with(EnvFilter::from_default_env()) + .with(logging_layer); + + // Add console logging if enabled + if config.logging.console { + // Optionally configure JSON logging + if config.logging.json { + subscriber.with(fmt_layer.json()).init(); + } else { + subscriber.with(fmt_layer.pretty()).init(); + } + } else { + subscriber.init(); + } + + Ok(()) +} diff --git a/crates/teepot/src/ethereum/mod.rs b/crates/teepot/src/ethereum/mod.rs new file mode 100644 index 0000000..9f01082 --- /dev/null +++ b/crates/teepot/src/ethereum/mod.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Ethereum-specific helper functions for on-chain verification of Intel SGX attestation. + +use anyhow::Result; +use secp256k1::{ + ecdsa::{RecoverableSignature, RecoveryId}, + Message, PublicKey, SECP256K1, +}; +use sha3::{Digest, Keccak256}; + +/// Equivalent to the ecrecover precompile, ensuring that the signatures we produce off-chain +/// can be recovered on-chain. +pub fn recover_signer(sig: &[u8; 65], root_hash: &Message) -> Result<[u8; 20]> { + let sig = RecoverableSignature::from_compact( + &sig[0..64], + RecoveryId::try_from(i32::from(sig[64]) - 27)?, + )?; + let public = SECP256K1.recover_ecdsa(*root_hash, &sig)?; + Ok(public_key_to_ethereum_address(&public)) +} + +/// Converts a public key into an Ethereum address by hashing the encoded public key with Keccak256. +pub fn public_key_to_ethereum_address(public: &PublicKey) -> [u8; 20] { + let public_key_bytes = public.serialize_uncompressed(); + + // Skip the first byte (0x04) which indicates uncompressed key + let hash: [u8; 32] = Keccak256::digest(&public_key_bytes[1..]).into(); + + // Take the last 20 bytes of the hash to get the Ethereum address + let mut address = [0u8; 20]; + address.copy_from_slice(&hash[12..]); + address +} + +#[cfg(test)] +mod tests { + use secp256k1::{Secp256k1, SecretKey}; + + use super::*; + + /// 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 (rec_id, data) = s.serialize_compact(); + + let mut signature = [0u8; 65]; + signature[..64].copy_from_slice(&data); + // as defined in the Ethereum Yellow Paper (Appendix F) + // https://ethereum.github.io/yellowpaper/paper.pdf + signature[64] = 27 + i32::from(rec_id) as u8; + + Ok(signature) + } + + #[test] + fn recover() { + // Decode the sample secret key, generate the public key, and derive the Ethereum address + // from the public key + let secp = Secp256k1::new(); + let secret_key_bytes = + hex::decode("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3") + .unwrap(); + let secret_key = + SecretKey::from_byte_array(secret_key_bytes.as_slice().try_into().unwrap()).unwrap(); + let public_key = PublicKey::from_secret_key(&secp, &secret_key); + let expected_address = hex::decode("627306090abaB3A6e1400e9345bC60c78a8BEf57").unwrap(); + let address = public_key_to_ethereum_address(&public_key); + + assert_eq!(address, expected_address.as_slice()); + + // Take a root hash, create a message from the hash, and sign the message using + // the secret key + let root_hash = b"12345678901234567890123456789012"; + let root_hash_bytes = root_hash.as_slice(); + let msg_to_sign = Message::from_digest(root_hash_bytes.try_into().unwrap()); + let signature = sign_message(&secret_key, msg_to_sign).unwrap(); + + // Recover the signer's Ethereum address from the signature and the message, and verify it + // matches the expected address + let proof_addr = recover_signer(&signature, &msg_to_sign).unwrap(); + + assert_eq!(proof_addr, expected_address.as_slice()); + } +} diff --git a/crates/teepot/src/lib.rs b/crates/teepot/src/lib.rs index 8d38678..9016d01 100644 --- a/crates/teepot/src/lib.rs +++ b/crates/teepot/src/lib.rs @@ -1,14 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Helper functions to verify Intel SGX enclaves and other TEEs. #![deny(missing_docs)] #![deny(clippy::all)] -pub mod client; -pub mod json; -pub mod server; -pub mod sgx; - +pub mod config; +pub mod ethereum; +pub mod log; +#[cfg(feature = "quote_op")] +pub mod pki; +pub mod prover; pub mod quote; +pub mod sgx; +pub mod tdx; +pub mod util; diff --git a/crates/teepot/src/log/mod.rs b/crates/teepot/src/log/mod.rs new file mode 100644 index 0000000..795cc10 --- /dev/null +++ b/crates/teepot/src/log/mod.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Matter Labs + +//! Logging related stuff + +use anyhow::Context; +use tracing::level_filters::LevelFilter; +use tracing::Subscriber; +use tracing_log::LogTracer; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::Registry; +use tracing_subscriber::{prelude::*, EnvFilter}; + +/// A log level parser for clap, with "off", "error", "warn", "info", "debug", "trace" as valid values +#[derive(Clone)] +pub struct LogLevelParser; + +impl clap::builder::TypedValueParser for LogLevelParser { + type Value = LevelFilter; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> anyhow::Result { + clap::builder::TypedValueParser::parse(self, cmd, arg, value.to_owned()) + } + + fn parse( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: std::ffi::OsString, + ) -> std::result::Result { + use std::str::FromStr; + let p = clap::builder::PossibleValuesParser::new([ + "off", "error", "warn", "info", "debug", "trace", + ]); + let v = p.parse(cmd, arg, value)?; + + Ok(LevelFilter::from_str(&v).unwrap()) + } +} + +/// Setup standard logging and loglevel for the current crate and the `teepot` crate. +pub fn setup_logging( + crate_name: &str, + log_level: &LevelFilter, +) -> anyhow::Result { + LogTracer::init().context("Failed to set logger")?; + let filter = EnvFilter::builder() + .try_from_env() + .unwrap_or(match *log_level { + LevelFilter::OFF => EnvFilter::new("off"), + _ => EnvFilter::new(format!("warn,{crate_name}={log_level},teepot={log_level}")), + }); + + let fmt_layer = tracing_subscriber::fmt::layer() + .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) + .with_file(false) + .with_line_number(false) + .with_writer(std::io::stderr); + + let subscriber = Registry::default().with(filter).with(fmt_layer); + + Ok(subscriber) +} diff --git a/crates/teepot/src/server/pki.rs b/crates/teepot/src/pki/mod.rs similarity index 83% rename from crates/teepot/src/server/pki.rs rename to crates/teepot/src/pki/mod.rs index ce3c110..b79f864 100644 --- a/crates/teepot/src/server/pki.rs +++ b/crates/teepot/src/pki/mod.rs @@ -1,41 +1,36 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Create a private key and a signed and self-signed certificates - -use crate::quote::get_quote; -use crate::sgx::tee_qv_get_collateral; -pub use crate::sgx::{ - parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet, - QuoteVerificationResult, TcbLevel, -}; +use crate::quote::{get_collateral, get_quote}; use anyhow::{Context, Result}; -use const_oid::db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH}; -use const_oid::AssociatedOid; -use getrandom::getrandom; -use p256::ecdsa::DerSignature; -use p256::pkcs8::EncodePrivateKey; +use const_oid::{ + db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH}, + AssociatedOid, +}; +use p256::{ecdsa::DerSignature, pkcs8::EncodePrivateKey}; use pkcs8::der; use rustls::pki_types::PrivatePkcs8KeyDer; use sha2::{Digest, Sha256}; -use signature::Signer; -use std::str::FromStr; -use std::time::Duration; +use signature::{rand_core::OsRng, Signer}; +use std::{str::FromStr, time::Duration}; use tracing::debug; -use x509_cert::builder::{Builder, CertificateBuilder, Profile}; -use x509_cert::der::pem::LineEnding; -use x509_cert::der::{asn1::OctetString, Encode as _, EncodePem as _, Length}; -use x509_cert::ext::pkix::name::GeneralNames; -use x509_cert::ext::pkix::{ExtendedKeyUsage, SubjectAltName}; -use x509_cert::ext::{AsExtension, Extension}; -use x509_cert::name::{Name, RdnSequence}; -use x509_cert::serial_number::SerialNumber; -use x509_cert::spki::{ - DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding, - SubjectPublicKeyInfoOwned, +use x509_cert::{ + builder::{Builder, CertificateBuilder, Profile}, + der::{asn1::OctetString, pem::LineEnding, Encode as _, EncodePem as _, Length}, + ext::{ + pkix::{name::GeneralNames, ExtendedKeyUsage, SubjectAltName}, + AsExtension, Extension, + }, + name::{Name, RdnSequence}, + serial_number::SerialNumber, + spki::{ + DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, + SignatureBitStringEncoding, SubjectPublicKeyInfoOwned, + }, + time::Validity, + Certificate, }; -use x509_cert::time::Validity; -use x509_cert::Certificate; use zeroize::Zeroizing; /// The OID for the `gramine-ra-tls` quote extension @@ -137,7 +132,7 @@ pub fn make_self_signed_cert( rustls::pki_types::PrivateKeyDer<'static>, )> { // Generate a keypair. - let mut rng = rand::thread_rng(); + let mut rng = OsRng; let signing_key = p256::ecdsa::SigningKey::random(&mut rng); let verifying_key = signing_key.verifying_key(); let verifying_key_der = verifying_key @@ -148,14 +143,14 @@ pub fn make_self_signed_cert( let hash = Sha256::digest(verifying_key_der.as_bytes()); key_hash[..32].copy_from_slice(&hash); - let quote = get_quote(&key_hash)?; + let (_tee_type, quote) = get_quote(&key_hash)?; debug!("quote.len: {:?}", quote.len()); // Create a relative distinguished name. let rdns = RdnSequence::from_str(dn)?; - let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?; + let collateral = get_collateral("e).context("Failed to get own collateral")?; let mut serial = [0u8; 16]; - getrandom(&mut serial)?; + getrandom::fill(&mut serial)?; let mut builder = CertificateBuilder::new( Profile::Leaf { @@ -185,6 +180,7 @@ pub fn make_self_signed_cert( .context("failed to add SubjectAltName")?; } + // FIXME: OID for tee_type builder .add_extension(&RaTlsQuoteExtension { quote: quote.to_vec(), @@ -223,7 +219,7 @@ where S::VerifyingKey: EncodePublicKey, { // Generate a keypair. - let mut rng = rand::thread_rng(); + let mut rng = OsRng; let signing_key = p256::ecdsa::SigningKey::random(&mut rng); let verifying_key = signing_key.verifying_key(); let verifying_key_der = verifying_key @@ -234,13 +230,13 @@ where let hash = Sha256::digest(verifying_key_der.as_bytes()); key_hash[..32].copy_from_slice(&hash); - let quote = get_quote(&key_hash).context("Failed to get own quote")?; + let (_tee_type, quote) = get_quote(&key_hash).context("Failed to get own quote")?; // Create a relative distinguished name. let subject = Name::from_str(dn)?; let mut serial = [0u8; 16]; - getrandom(&mut serial)?; + getrandom::fill(&mut serial)?; let mut builder = CertificateBuilder::new( Profile::Leaf { @@ -269,6 +265,7 @@ where .context("failed to add SubjectAltName")?; } + // FIXME: oid according to tee_type builder .add_extension(&RaTlsQuoteExtension { quote: quote.to_vec(), diff --git a/crates/teepot/src/prover/mod.rs b/crates/teepot/src/prover/mod.rs new file mode 100644 index 0000000..d7b3e63 --- /dev/null +++ b/crates/teepot/src/prover/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023 Matter Labs + +//! Common functionality for TEE provers and verifiers. + +pub mod reportdata; diff --git a/crates/teepot/src/prover/reportdata.rs b/crates/teepot/src/prover/reportdata.rs new file mode 100644 index 0000000..fd849bf --- /dev/null +++ b/crates/teepot/src/prover/reportdata.rs @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Matter Labs + +//! Versioning of Intel SGX/TDX quote's report data for TEE prover and verifier. + +use core::convert::Into; + +use anyhow::{anyhow, Result}; +use secp256k1::{constants::PUBLIC_KEY_SIZE, PublicKey}; + +/// Report data length for Intel SGX/TDX. +const REPORT_DATA_LENGTH: usize = 64; + +/// Ethereum address length. +const ETHEREUM_ADDR_LENGTH: usize = 20; + +/// Report data version. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ReportData { + /// Legacy version of report data that was initially not intended to be versioned. + /// The report_data was on-chain incompatible and consisted of a compressed ECDSA public key. + /// + /// +-------------------------------------+--------------------------------+------------------+ + /// | compressed ECDSA pubkey (33 bytes) | zeroes (30 bytes) | version (1 byte) | + /// +-------------------------------------+--------------------------------+------------------+ + V0(ReportDataV0), + /// Latest version of report data compatible with on-chain verification. + /// + /// +--------------------------+-------------------------------------------+------------------+ + /// | Ethereum addr (20 bytes) | zeros (43 bytes) | version (1 byte) | + /// +--------------------------+-------------------------------------------+------------------+ + V1(ReportDataV1), + /// Unknown version of report data. + Unknown(Vec), +} + +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 {REPORT_DATA_LENGTH} bytes", + ); + let mut bytes = [0u8; REPORT_DATA_LENGTH]; + bytes[..data.len()].copy_from_slice(data); + bytes[REPORT_DATA_LENGTH - 1] = version; + bytes +} + +fn invalid_length_error() -> anyhow::Error { + anyhow!( + "Invalid report data length, expected {} bytes", + REPORT_DATA_LENGTH + ) +} + +impl TryFrom<&[u8]> for ReportData { + type Error = anyhow::Error; + + fn try_from(report_data: &[u8]) -> Result { + if report_data.len() != REPORT_DATA_LENGTH { + return Err(invalid_length_error()); + } + + let version = report_data[REPORT_DATA_LENGTH - 1]; + match version { + 0 => Ok(Self::V0(ReportDataV0::try_from(report_data)?)), + 1 => Ok(Self::V1(ReportDataV1::try_from(report_data)?)), + _ => Ok(Self::Unknown(report_data.into())), + } + } +} + +impl From for [u8; REPORT_DATA_LENGTH] { + fn from(report_data: ReportData) -> Self { + match report_data { + ReportData::V0(data) => data.into(), + ReportData::V1(data) => data.into(), + ReportData::Unknown(data) => { + let mut bytes = [0u8; REPORT_DATA_LENGTH]; + bytes.copy_from_slice(&data); + bytes + } + } + } +} + +trait ReportDataVersion { + fn version(&self) -> u8; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[allow(missing_docs)] +pub struct ReportDataV0 { + pub pubkey: PublicKey, +} + +impl ReportDataVersion for ReportDataV0 { + fn version(&self) -> u8 { + 0 + } +} + +impl TryFrom<&[u8]> for ReportDataV0 { + type Error = anyhow::Error; + + fn try_from(report_data: &[u8]) -> Result { + if report_data.len() != REPORT_DATA_LENGTH { + return Err(invalid_length_error()); + } + let pubkey = PublicKey::from_slice(&report_data[..PUBLIC_KEY_SIZE])?; + Ok(Self { pubkey }) + } +} + +impl From for [u8; REPORT_DATA_LENGTH] { + fn from(report_data: ReportDataV0) -> Self { + report_data_to_bytes(&report_data.pubkey.serialize(), report_data.version()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[allow(missing_docs)] +pub struct ReportDataV1 { + pub ethereum_address: [u8; ETHEREUM_ADDR_LENGTH], +} + +impl ReportDataVersion for ReportDataV1 { + fn version(&self) -> u8 { + 1 + } +} + +impl TryFrom<&[u8]> for ReportDataV1 { + type Error = anyhow::Error; + + fn try_from(report_data: &[u8]) -> Result { + if report_data.len() != REPORT_DATA_LENGTH { + return Err(invalid_length_error()); + } + let mut ethereum_address = [0u8; ETHEREUM_ADDR_LENGTH]; + ethereum_address.copy_from_slice(&report_data[..ETHEREUM_ADDR_LENGTH]); + Ok(Self { ethereum_address }) + } +} + +impl From for [u8; REPORT_DATA_LENGTH] { + fn from(report_data: ReportDataV1) -> Self { + report_data_to_bytes(&report_data.ethereum_address, report_data.version()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hex; + use secp256k1::{Secp256k1, SecretKey}; + + const ETHEREUM_ADDR: [u8; ETHEREUM_ADDR_LENGTH] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, + ]; + + fn generate_test_report_data(version_byte: u8) -> [u8; REPORT_DATA_LENGTH] { + let mut data = [0u8; REPORT_DATA_LENGTH]; + data[..ETHEREUM_ADDR.len()].copy_from_slice(ÐEREUM_ADDR); + data[REPORT_DATA_LENGTH - 1] = version_byte; + data + } + + fn generate_test_pubkey() -> PublicKey { + let secp = Secp256k1::new(); + let secret_key_bytes = + hex::decode("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3") + .unwrap(); + let secret_key = SecretKey::from_slice(&secret_key_bytes).unwrap(); + PublicKey::from_secret_key(&secp, &secret_key) + } + + fn generate_test_report_data_v0(pubkey: PublicKey) -> [u8; REPORT_DATA_LENGTH] { + let pubkey_bytes = pubkey.serialize(); + let mut report_data = [0u8; REPORT_DATA_LENGTH]; + report_data[..PUBLIC_KEY_SIZE].copy_from_slice(&pubkey_bytes); + report_data + } + + #[test] + fn test_from_bytes_v0() { + let pubkey = generate_test_pubkey(); + let report_data = generate_test_report_data_v0(pubkey); + let report_data = ReportData::try_from(report_data.as_ref()).unwrap(); + assert_eq!(report_data, ReportData::V0(ReportDataV0 { pubkey })); + } + + #[test] + fn report_data_from_bytes_v1() { + let data = generate_test_report_data(1); + let report_data = ReportData::try_from(data.as_ref()).unwrap(); + assert_eq!( + report_data, + ReportData::V1(ReportDataV1 { + ethereum_address: ETHEREUM_ADDR + }) + ); + } + + #[test] + fn report_data_from_bytes_unknown() { + let report_data_bytes = generate_test_report_data(99); + let report_data = ReportData::try_from(report_data_bytes.as_ref()).unwrap(); + assert_eq!(report_data, ReportData::Unknown(report_data_bytes.into())); + } + + #[test] + fn report_data_to_bytes_v0() { + let pubkey = generate_test_pubkey(); + let report_data = ReportDataV0 { pubkey }; + let report_data: [u8; REPORT_DATA_LENGTH] = report_data.into(); + assert_eq!(&report_data[..PUBLIC_KEY_SIZE], pubkey.serialize().as_ref()); + assert_eq!(report_data[REPORT_DATA_LENGTH - 1], 0); + assert!(report_data[PUBLIC_KEY_SIZE..REPORT_DATA_LENGTH - 1] + .iter() + .all(|&byte| byte == 0)); + } + + #[test] + fn report_data_to_bytes_v1() { + let report_data = ReportDataV1 { + ethereum_address: ETHEREUM_ADDR, + }; + let report_data: [u8; REPORT_DATA_LENGTH] = report_data.into(); + assert_eq!(&report_data[..ETHEREUM_ADDR_LENGTH], ÐEREUM_ADDR); + assert_eq!(report_data[REPORT_DATA_LENGTH - 1], 1); + assert!(report_data[ETHEREUM_ADDR_LENGTH..REPORT_DATA_LENGTH - 1] + .iter() + .all(|&byte| byte == 0)); + } +} diff --git a/crates/teepot/src/server/attestation.rs b/crates/teepot/src/quote/attestation.rs similarity index 51% rename from crates/teepot/src/server/attestation.rs rename to crates/teepot/src/quote/attestation.rs index e390187..371545c 100644 --- a/crates/teepot/src/server/attestation.rs +++ b/crates/teepot/src/quote/attestation.rs @@ -1,19 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Common attestation API for all TEEs -use crate::client::AttestationArgs; -use crate::json::http::AttestationResponse; -use crate::sgx::{ - parse_tcb_levels, sgx_gramine_get_quote, tee_qv_get_collateral, verify_quote_with_collateral, - Collateral, EnumSet, QuoteVerificationResult, TcbLevel, +use crate::quote::{ + get_collateral, get_quote, + tcblevel::{EnumSet, TcbLevel}, + verify_quote_with_collateral, Collateral, QuoteVerificationResult, }; use anyhow::{bail, Context, Result}; -use clap::Args; use serde::{Deserialize, Serialize}; -use std::sync::{Arc, RwLock}; -use std::time::{Duration, UNIX_EPOCH}; +use std::{ + sync::{Arc, RwLock}, + time::{Duration, UNIX_EPOCH}, +}; use tracing::{debug, error, info, trace, warn}; struct Attestation { @@ -23,6 +23,15 @@ struct Attestation { earliest_expiration_date: i64, } +/// The attestation response +#[derive(Debug, Serialize, Deserialize)] +pub struct AttestationResponse { + /// The quote + pub quote: Arc<[u8]>, + /// The collateral + pub collateral: Arc, +} + /// Returns the quote and collateral for the current TEE. /// /// if `allowed_tcb_levels` is `None`, then any TCB level is accepted. @@ -35,8 +44,7 @@ pub fn get_quote_and_collateral( static ATTESTATION: RwLock> = RwLock::new(None); let unix_time: i64 = std::time::SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() + .duration_since(UNIX_EPOCH)? .as_secs() as _; if let Some(attestation) = ATTESTATION.read().unwrap().as_ref() { @@ -53,12 +61,12 @@ pub fn get_quote_and_collateral( } } - let myquote = sgx_gramine_get_quote(report_data).context("Failed to get own quote")?; - let collateral = tee_qv_get_collateral(&myquote).context("Failed to get own collateral")?; + let (_tee_type, myquote) = get_quote(report_data).context("Failed to get own quote")?; + let collateral = get_collateral(&myquote).context("Failed to get own collateral")?; let QuoteVerificationResult { collateral_expired, - result, + result: tcblevel, earliest_expiration_date, tcb_level_date_tag, quote, @@ -72,9 +80,8 @@ pub fn get_quote_and_collateral( bail!("Freshly fetched collateral expired"); } - let tcblevel = TcbLevel::from(result); if tcblevel != TcbLevel::Ok - && allowed_tcb_levels.map_or(false, |levels| !levels.contains(tcblevel)) + && allowed_tcb_levels.is_some_and(|levels| !levels.contains(tcblevel)) { error!("Quote verification result: {}", tcblevel); bail!("Quote verification result: {}", tcblevel); @@ -89,8 +96,8 @@ pub fn get_quote_and_collateral( "Earliest expiration in {:?}", Duration::from_secs((earliest_expiration_date - unix_time) as _) ); - info!("mrsigner: {}", hex::encode(quote.report_body.mrsigner)); - info!("mrenclave: {}", hex::encode(quote.report_body.mrenclave)); + + info!("{:#}", quote.report); let quote: Arc<[u8]> = Arc::from(myquote); let collateral = Arc::from(collateral); @@ -105,43 +112,3 @@ pub fn get_quote_and_collateral( Ok(AttestationResponse { quote, collateral }) } - -/// Options and arguments needed to attest a TEE -#[derive(Args, Debug, Clone, Serialize, Deserialize, Default)] -pub struct VaultAttestationArgs { - /// hex encoded SGX mrsigner of the enclave to attest - #[arg(long, env = "VAULT_SGX_MRSIGNER")] - pub vault_sgx_mrsigner: Option, - /// hex encoded SGX mrenclave of the enclave to attest - #[arg(long, env = "VAULT_SGX_MRENCLAVE")] - pub vault_sgx_mrenclave: Option, - /// URL of the server - #[arg(long, required = true, env = "VAULT_ADDR")] - pub vault_addr: String, - /// allowed TCB levels, comma separated: - /// Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, OutOfDate, OutOfDateConfigNeeded - #[arg(long, value_parser = parse_tcb_levels, env = "VAULT_SGX_ALLOWED_TCB_LEVELS")] - pub vault_sgx_allowed_tcb_levels: Option>, -} - -impl From for AttestationArgs { - fn from(value: VaultAttestationArgs) -> Self { - AttestationArgs { - sgx_mrsigner: value.vault_sgx_mrsigner, - sgx_mrenclave: value.vault_sgx_mrenclave, - server: value.vault_addr, - sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels, - } - } -} - -impl From<&VaultAttestationArgs> for AttestationArgs { - fn from(value: &VaultAttestationArgs) -> Self { - AttestationArgs { - sgx_mrsigner: value.vault_sgx_mrsigner.clone(), - sgx_mrenclave: value.vault_sgx_mrenclave.clone(), - server: value.vault_addr.clone(), - sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels, - } - } -} diff --git a/crates/teepot/src/quote/error.rs b/crates/teepot/src/quote/error.rs new file mode 100644 index 0000000..192cbda --- /dev/null +++ b/crates/teepot/src/quote/error.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +//! Quote Error type + +use std::io; +#[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 +#[derive(Error, Debug)] +#[allow(missing_docs)] +pub enum QuoteError { + #[error("I/O Error")] + IoError { context: String, source: io::Error }, + #[error("parsing bytes")] + ConvertError(#[from] bytemuck::PodCastError), + #[error("unsupported quote version")] + QuoteVersion, + #[error("invalid tee type")] + InvalidTeeType, + #[error("unsupported body type")] + UnsupportedBodyType, + #[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, + msg: String, + }, + #[error("invalid version")] + InvalidVersion, + #[error("report data too long")] + ReportDataSize, + #[error("can't get a quote: unknown TEE")] + UnknownTee, + #[error("{0}: invalid parameter")] + InvalidParameter(String), + #[error("{0}: platform lib unavailable")] + PlatformLibUnavailable(String), + #[error("{0}: pck cert chain error")] + PckCertChainError(String), + #[error("{0}: pck cert unsupported format")] + PckCertUnsupportedFormat(String), + #[error("{0}: quote format unsupported")] + QuoteFormatUnsupported(String), + #[error("{0}: out of memory")] + OutOfMemory(String), + #[error("{0}: no quote collateral data")] + NoQuoteCollateralData(String), + #[error("{0}: unexpected error")] + Unexpected(String), + #[error("{0}: quote certification data unsupported")] + QuoteCertificationDataUnsupported(String), + #[error("{0}: unable to generate report")] + UnableToGenerateReport(String), + #[error("{0}: CRL unsupported format")] + CrlUnsupportedFormat(String), +} + +#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))] +impl From for QuoteError { + fn from(code: tdx_attest_error_t) -> Self { + Self::TdxAttGetQuote { + inner: code, + msg: "code".to_string(), + } + } +} + +/// Usability trait for easy QuoteError annotation +pub trait QuoteContext { + /// The Ok Type + type Ok; + /// The Context + fn context>(self, msg: I) -> Result; +} + +impl QuoteContext for Result { + type Ok = T; + fn context>(self, msg: I) -> Result { + self.map_err(|e| QuoteError::IoError { + context: msg.into(), + source: e, + }) + } +} + +impl QuoteContext for Option { + type Ok = T; + fn context>(self, msg: I) -> Result { + self.ok_or(QuoteError::Unexpected(msg.into())) + } +} + +/// Usability trait for easy QuoteError annotation +pub trait QuoteContextErr { + /// The Ok Type + type Ok; + /// The Context + fn str_context(self, msg: I) -> Result; +} + +impl QuoteContextErr for Result { + type Ok = T; + fn str_context(self, msg: I) -> Result { + self.map_err(|e| QuoteError::Unexpected(format!("{msg}: {e}"))) + } +} + +#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))] +impl QuoteContext for Result { + type Ok = T; + fn context>(self, msg: I) -> Result { + self.map_err(|e| QuoteError::TdxAttGetQuote { + msg: msg.into(), + inner: e, + }) + } +} diff --git a/crates/teepot/src/quote/intel.rs b/crates/teepot/src/quote/intel.rs new file mode 100644 index 0000000..97ad1b6 --- /dev/null +++ b/crates/teepot/src/quote/intel.rs @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2025 Matter Labs + +//! Drive Intel DCAP verification crate, which is using the C library + +use crate::{ + quote::{ + error::QuoteError, tcblevel::TcbLevel, Collateral, Quote, QuoteVerificationResult, TEEType, + }, + sgx::sgx_gramine_get_quote, +}; +use bytemuck::cast_slice; +use std::{ffi::CStr, mem, mem::MaybeUninit, pin::Pin}; +use teepot_tee_quote_verification_rs::{ + quote3_error_t as _quote3_error_t, sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, + tdx_attest_rs::{tdx_att_get_quote, tdx_attest_error_t, tdx_report_data_t}, + tee_get_supplemental_data_version_and_size, tee_qv_get_collateral, tee_supp_data_descriptor_t, + tee_verify_quote, Collateral as IntelCollateral, +}; +use tracing::{trace, warn}; + +/// Convert SGX QV result to our TcbLevel enum +fn convert_tcb_level(value: sgx_ql_qv_result_t) -> TcbLevel { + match value { + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => TcbLevel::Ok, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => TcbLevel::OutOfDate, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => { + TcbLevel::OutOfDateConfigNeeded + } + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => TcbLevel::SwHardeningNeeded, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { + TcbLevel::ConfigAndSwHardeningNeeded + } + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => TcbLevel::ConfigNeeded, + _ => TcbLevel::Invalid, + } +} + +/// Convert quote3_error_t to QuoteError with context +fn convert_quote_error(error: _quote3_error_t, context: impl Into) -> QuoteError { + let context = context.into(); + match error { + _quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER => QuoteError::InvalidParameter(context), + _quote3_error_t::SGX_QL_PCK_CERT_CHAIN_ERROR => QuoteError::PckCertChainError(context), + _quote3_error_t::SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT => { + QuoteError::PckCertUnsupportedFormat(context) + } + _quote3_error_t::SGX_QL_QUOTE_FORMAT_UNSUPPORTED => { + QuoteError::QuoteFormatUnsupported(context) + } + _quote3_error_t::SGX_QL_ERROR_OUT_OF_MEMORY => QuoteError::OutOfMemory(context), + _quote3_error_t::SGX_QL_NO_QUOTE_COLLATERAL_DATA => { + QuoteError::NoQuoteCollateralData(context) + } + _quote3_error_t::SGX_QL_ERROR_UNEXPECTED => QuoteError::Unexpected(context), + _quote3_error_t::SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED => { + QuoteError::QuoteCertificationDataUnsupported(context) + } + _quote3_error_t::SGX_QL_UNABLE_TO_GENERATE_REPORT => { + QuoteError::UnableToGenerateReport(context) + } + _quote3_error_t::SGX_QL_CRL_UNSUPPORTED_FORMAT => QuoteError::CrlUnsupportedFormat(context), + _ => QuoteError::Unexpected(context), + } +} + +/// Convert internal Collateral type to Intel library Collateral type +fn convert_to_intel_collateral(collateral: &Collateral) -> IntelCollateral { + IntelCollateral { + major_version: collateral.major_version, + minor_version: collateral.minor_version, + tee_type: collateral.tee_type, + pck_crl_issuer_chain: collateral.pck_crl_issuer_chain.clone(), + root_ca_crl: collateral.root_ca_crl.clone(), + pck_crl: collateral.pck_crl.clone(), + tcb_info_issuer_chain: collateral.tcb_info_issuer_chain.clone(), + tcb_info: collateral.tcb_info.clone(), + qe_identity_issuer_chain: collateral.qe_identity_issuer_chain.clone(), + qe_identity: collateral.qe_identity.clone(), + } +} + +/// Get collateral data for a quote +pub fn get_collateral(quote: &[u8]) -> Result { + let collateral = tee_qv_get_collateral(quote) + .map_err(|e| convert_quote_error(e, "tee_qv_get_collateral"))?; + + Ok(Collateral { + major_version: collateral.major_version, + minor_version: collateral.minor_version, + tee_type: collateral.tee_type, + pck_crl_issuer_chain: collateral.pck_crl_issuer_chain, + root_ca_crl: collateral.root_ca_crl, + pck_crl: collateral.pck_crl, + tcb_info_issuer_chain: collateral.tcb_info_issuer_chain, + tcb_info: collateral.tcb_info, + qe_identity_issuer_chain: collateral.qe_identity_issuer_chain, + qe_identity: collateral.qe_identity, + }) +} + +/// Extract advisory information from supplemental data +fn extract_supplemental_data_info(supp_data: sgx_ql_qv_supplemental_t) -> (Vec, i64, i64) { + // Convert to valid UTF-8 string + let advisories = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..])) + .ok() + .and_then(|s| CStr::to_str(s).ok()) + .into_iter() + .flat_map(|s| s.split(',').map(str::trim).map(String::from)) + .filter(|s| !s.is_empty()) + .collect(); + + ( + advisories, + supp_data.earliest_expiration_date, + supp_data.tcb_level_date_tag, + ) +} + +/// Verifies a quote with optional collateral material +pub(crate) fn verify_quote_with_collateral( + quote: &[u8], + collateral: Option<&crate::quote::Collateral>, + current_time: i64, +) -> Result { + // Convert collateral if provided + let intel_collateral = collateral.map(convert_to_intel_collateral); + + let TeeSuppDataDescriptor { + supp_data, + mut supp_data_descriptor, + } = initialize_supplemental_data(quote)?; + + let (collateral_expiration_status, result) = tee_verify_quote( + quote, + intel_collateral.as_ref(), + current_time, + None, + supp_data_descriptor.as_mut(), + ) + .map_err(|e| convert_quote_error(e, "tee_verify_quote"))?; + + // Extract supplemental data if available + let (advisories, earliest_expiration_date, tcb_level_date_tag) = + if supp_data_descriptor.is_some() { + let supp_data = unsafe { supp_data.assume_init() }; + extract_supplemental_data_info(supp_data) + } else { + (vec![], 0, 0) + }; + + let quote = Quote::parse(quote)?; + + Ok(QuoteVerificationResult { + collateral_expired: collateral_expiration_status != 0, + earliest_expiration_date, + tcb_level_date_tag, + result: convert_tcb_level(result), + quote, + advisories, + }) +} + +struct TeeSuppDataDescriptor { + supp_data: Pin>>, + supp_data_descriptor: Option, +} + +fn initialize_supplemental_data(quote: &[u8]) -> Result { + let mut supp_data = Box::pin(mem::MaybeUninit::zeroed()); + let mut supp_data_desc = tee_supp_data_descriptor_t { + major_version: 0, + data_size: 0, + p_data: supp_data.as_mut_ptr() as *mut u8, + }; + trace!("tee_get_supplemental_data_version_and_size"); + let (_, supp_size) = tee_get_supplemental_data_version_and_size(quote) + .map_err(|e| convert_quote_error(e, "tee_get_supplemental_data_version_and_size"))?; + + trace!( + "tee_get_supplemental_data_version_and_size supp_size: {}", + supp_size + ); + + let p_supplemental_data = if supp_size == mem::size_of::() as u32 { + supp_data_desc.data_size = supp_size; + Some(supp_data_desc) + } else { + supp_data_desc.data_size = 0; + trace!( + "tee_get_supplemental_data_version_and_size supp_size: {}", + supp_size + ); + trace!( + "mem::size_of::(): {}", + mem::size_of::() + ); + warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you install DCAP QVL and QvE from the same release."); + None + }; + + Ok(TeeSuppDataDescriptor { + supp_data, + supp_data_descriptor: p_supplemental_data, + }) +} + +/// Prepare report data for quote generation +fn prepare_report_data(report_data: &[u8]) -> Result<[u8; 64], QuoteError> { + if report_data.len() > 64 { + return Err(QuoteError::ReportDataSize); + } + + let mut report_data_fixed = [0u8; 64]; + report_data_fixed[..report_data.len()].copy_from_slice(report_data); + Ok(report_data_fixed) +} + +/// Get a TDX quote +fn tdx_get_quote(report_data_bytes: &[u8; 64]) -> Result, QuoteError> { + let mut tdx_report_data = tdx_report_data_t { d: [0; 64usize] }; + tdx_report_data.d.copy_from_slice(report_data_bytes); + + let (error, quote) = tdx_att_get_quote(Some(&tdx_report_data), None, None, 0); + + if error == tdx_attest_error_t::TDX_ATTEST_SUCCESS { + if let Some(quote) = quote { + Ok(quote.into()) + } else { + Err(QuoteError::TdxAttGetQuote { + msg: "tdx_att_get_quote: No quote returned".into(), + inner: error, + }) + } + } else { + Err(error.into()) + } +} + +/// Detect the TEE environment +fn detect_tee_environment() -> Result { + if std::fs::metadata("/dev/attestation").is_ok() { + Ok(TEEType::SGX) + } else if std::fs::metadata("/dev/tdx_guest").is_ok() { + Ok(TEEType::TDX) + } else { + Err(QuoteError::UnknownTee) + } +} + +/// Get the attestation quote from a TEE +pub(crate) fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> { + let report_data_fixed = prepare_report_data(report_data)?; + + match detect_tee_environment()? { + TEEType::SGX => Ok((TEEType::SGX, sgx_gramine_get_quote(&report_data_fixed)?)), + TEEType::TDX => Ok((TEEType::TDX, tdx_get_quote(&report_data_fixed)?)), + _ => Err(QuoteError::UnknownTee), // For future TEE types + } +} diff --git a/crates/teepot/src/quote/mod.rs b/crates/teepot/src/quote/mod.rs index 4d8ae51..19b1243 100644 --- a/crates/teepot/src/quote/mod.rs +++ b/crates/teepot/src/quote/mod.rs @@ -1,40 +1,725 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs + +// Parts of it are Copyright (c) 2024 Phala Network +// and copied from https://github.com/Phala-Network/dcap-qvl //! Get a quote from a TEE -use crate::sgx::sgx_gramine_get_quote; -use std::io; +#[cfg(feature = "quote_op")] +pub mod attestation; +pub mod error; +pub mod tcblevel; + +#[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 = "phala.rs" +)] +#[cfg(feature = "quote_op")] +mod os; +mod utils; + +use crate::quote::{ + error::{QuoteContext as _, QuoteError}, + tcblevel::TcbLevel, +}; +use bytemuck::AnyBitPattern; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Display, Formatter}, + io::Read, + str::FromStr, +}; +use tracing::trace; -#[derive(Debug, thiserror::Error)] #[allow(missing_docs)] -#[error("{msg}")] -pub struct GetQuoteError { - pub(crate) msg: Box, - #[source] // optional if field name is `source` - pub(crate) source: io::Error, +pub const TEE_TYPE_SGX: u32 = 0x0000_0000; +#[allow(missing_docs)] +pub const TEE_TYPE_TDX: u32 = 0x0000_0081; + +#[allow(missing_docs)] +pub const BODY_SGX_ENCLAVE_REPORT_TYPE: u16 = 1; +#[allow(missing_docs)] +pub const BODY_TD_REPORT10_TYPE: u16 = 2; +#[allow(missing_docs)] +pub const BODY_TD_REPORT15_TYPE: u16 = 3; +#[allow(missing_docs)] +pub const ENCLAVE_REPORT_BYTE_LEN: usize = 384; + +#[allow(missing_docs)] +pub const ECDSA_SIGNATURE_BYTE_LEN: usize = 64; +#[allow(missing_docs)] +pub const ECDSA_PUBKEY_BYTE_LEN: usize = 64; +#[allow(missing_docs)] +pub const QE_REPORT_SIG_BYTE_LEN: usize = ECDSA_SIGNATURE_BYTE_LEN; + +mod serde_bytes { + use serde::Deserialize; + + pub(crate) trait FromBytes { + fn from_bytes(bytes: Vec) -> Option + where + Self: Sized; + } + impl FromBytes for Vec { + fn from_bytes(bytes: Vec) -> Option { + Some(bytes) + } + } + impl FromBytes for [u8; N] { + fn from_bytes(bytes: Vec) -> Option { + bytes.try_into().ok() + } + } + + pub(crate) fn serialize( + data: impl AsRef<[u8]>, + serializer: S, + ) -> Result { + let hex_str = hex::encode(data); + serializer.serialize_str(&hex_str) + } + + pub(crate) fn deserialize<'de, D: serde::Deserializer<'de>, T: FromBytes>( + deserializer: D, + ) -> Result { + let hex_str = String::deserialize(deserializer)?; + let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?; + T::from_bytes(bytes).ok_or_else(|| serde::de::Error::custom("invalid bytes")) + } } -/// Get the attestation quote from a TEE -pub fn get_quote(report_data: &[u8]) -> Result, GetQuoteError> { - // check, if we are running in a TEE - if std::fs::metadata("/dev/attestation").is_ok() { - if report_data.len() > 64 { - return Err(GetQuoteError { - msg: "Report data too long".into(), - source: io::Error::new(io::ErrorKind::Other, "Report data too long"), - }); - } +/// Trait that allows zero-copy read of value-references from slices in LE format. +pub trait Decode: Sized { + /// Attempt to deserialise the value from input. + fn decode(input: &mut I) -> Result; +} - let mut report_data_fixed = [0u8; 64]; - report_data_fixed[..report_data.len()].copy_from_slice(report_data); +impl Decode for T { + fn decode(input: &mut I) -> Result { + let mut bytes = vec![0u8; size_of::()]; + input.read(&mut bytes).context("parsing bytes")?; + bytemuck::try_pod_read_unaligned(&bytes).map_err(Into::into) + } +} - sgx_gramine_get_quote(&report_data_fixed) - } else { - // if not, return an error - Err(GetQuoteError { - msg: "Not running in a TEE".into(), - source: io::Error::new(io::ErrorKind::Other, "Not running in a TEE"), +#[derive(Debug, Clone)] +#[allow(missing_docs)] +#[repr(C)] +pub struct Data { + pub data: Vec, + _marker: core::marker::PhantomData, +} + +impl Serialize for Data { + fn serialize(&self, serializer: S) -> Result { + serde_bytes::serialize(&self.data, serializer) + } +} + +impl<'de, T> Deserialize<'de> for Data { + fn deserialize>(deserializer: D) -> Result { + let data = serde_bytes::deserialize(deserializer)?; + Ok(Data { + data, + _marker: core::marker::PhantomData, }) } } + +impl> Decode for Data { + fn decode(input: &mut I) -> Result { + let len = T::decode(input)?; + let mut data = vec![0u8; len.into() as usize]; + input.read(&mut data).context("reading bytes")?; + Ok(Data { + data, + _marker: core::marker::PhantomData, + }) + } +} + +#[allow(missing_docs)] +#[derive(AnyBitPattern, Debug, Serialize, Deserialize, Copy, Clone)] +#[repr(C, packed)] +pub struct Header { + pub version: u16, + pub attestation_key_type: u16, + pub tee_type: u32, + pub qe_svn: u16, + pub pce_svn: u16, + #[serde(with = "serde_bytes")] + pub qe_vendor_id: [u8; 16], + #[serde(with = "serde_bytes")] + pub user_data: [u8; 20], +} + +#[derive(AnyBitPattern, Debug, Copy, Clone)] +#[allow(missing_docs)] +#[repr(C, packed)] +pub struct Body { + pub body_type: u16, + pub size: u32, +} + +#[derive(Serialize, Deserialize, AnyBitPattern, Debug, Clone, Copy)] +#[allow(missing_docs)] +#[repr(C, packed)] +pub struct EnclaveReport { + #[serde(with = "serde_bytes")] + pub cpu_svn: [u8; 16], + pub misc_select: u32, + #[serde(with = "serde_bytes")] + pub reserved1: [u8; 28], + #[serde(with = "serde_bytes")] + pub attributes: [u8; 16], + #[serde(with = "serde_bytes")] + pub mr_enclave: [u8; 32], + #[serde(with = "serde_bytes")] + pub reserved2: [u8; 32], + #[serde(with = "serde_bytes")] + pub mr_signer: [u8; 32], + #[serde(with = "serde_bytes")] + pub reserved3: [u8; 96], + pub isv_prod_id: u16, + pub isv_svn: u16, + #[serde(with = "serde_bytes")] + pub reserved4: [u8; 60], + #[serde(with = "serde_bytes")] + pub report_data: [u8; 64], +} + +#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C, packed)] +pub struct TDReport10 { + #[serde(with = "serde_bytes")] + pub tee_tcb_svn: [u8; 16], + #[serde(with = "serde_bytes")] + pub mr_seam: [u8; 48], + #[serde(with = "serde_bytes")] + pub mr_signer_seam: [u8; 48], + #[serde(with = "serde_bytes")] + pub seam_attributes: [u8; 8], + #[serde(with = "serde_bytes")] + pub td_attributes: [u8; 8], + #[serde(with = "serde_bytes")] + pub xfam: [u8; 8], + #[serde(with = "serde_bytes")] + pub mr_td: [u8; 48], + #[serde(with = "serde_bytes")] + pub mr_config_id: [u8; 48], + #[serde(with = "serde_bytes")] + pub mr_owner: [u8; 48], + #[serde(with = "serde_bytes")] + pub mr_owner_config: [u8; 48], + #[serde(with = "serde_bytes")] + pub rt_mr0: [u8; 48], + #[serde(with = "serde_bytes")] + pub rt_mr1: [u8; 48], + #[serde(with = "serde_bytes")] + pub rt_mr2: [u8; 48], + #[serde(with = "serde_bytes")] + pub rt_mr3: [u8; 48], + #[serde(with = "serde_bytes")] + pub report_data: [u8; 64], +} + +#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C, packed)] +pub struct TDReport15 { + pub base: TDReport10, + #[serde(with = "serde_bytes")] + pub tee_tcb_svn2: [u8; 16], + #[serde(with = "serde_bytes")] + pub mr_service_td: [u8; 48], +} + +#[allow(missing_docs)] +#[derive(Serialize, Deserialize, Clone)] +#[repr(C)] +pub struct CertificationData { + pub cert_type: u16, + pub body: Data, +} + +impl Decode for CertificationData { + fn decode(input: &mut I) -> Result { + Ok(Self { + cert_type: Decode::decode(input)?, + body: Decode::decode(input)?, + }) + } +} + +impl core::fmt::Debug for CertificationData { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let body_str = String::from_utf8_lossy(&self.body.data); + f.debug_struct("CertificationData") + .field("cert_type", &self.cert_type) + .field("body", &body_str) + .finish() + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +pub struct QEReportCertificationData { + #[serde(with = "serde_bytes")] + pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN], + #[serde(with = "serde_bytes")] + pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN], + pub qe_auth_data: Data, + pub certification_data: CertificationData, +} + +impl Decode for QEReportCertificationData { + fn decode(input: &mut I) -> Result { + Ok(Self { + qe_report: Decode::decode(input)?, + qe_report_signature: Decode::decode(input)?, + qe_auth_data: Decode::decode(input)?, + certification_data: Decode::decode(input)?, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +pub struct AuthDataV3 { + #[serde(with = "serde_bytes")] + pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN], + #[serde(with = "serde_bytes")] + pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN], + #[serde(with = "serde_bytes")] + pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN], + #[serde(with = "serde_bytes")] + pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN], + pub qe_auth_data: Data, + pub certification_data: CertificationData, +} + +impl Decode for AuthDataV3 { + fn decode(input: &mut I) -> Result { + Ok(Self { + ecdsa_signature: Decode::decode(input)?, + ecdsa_attestation_key: Decode::decode(input)?, + qe_report: Decode::decode(input)?, + qe_report_signature: Decode::decode(input)?, + qe_auth_data: Decode::decode(input)?, + certification_data: Decode::decode(input)?, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +pub struct AuthDataV4 { + #[serde(with = "serde_bytes")] + pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN], + #[serde(with = "serde_bytes")] + pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN], + pub certification_data: CertificationData, + pub qe_report_data: QEReportCertificationData, +} + +impl AuthDataV4 { + #[allow(missing_docs)] + pub fn into_v3(self) -> AuthDataV3 { + AuthDataV3 { + ecdsa_signature: self.ecdsa_signature, + ecdsa_attestation_key: self.ecdsa_attestation_key, + qe_report: self.qe_report_data.qe_report, + qe_report_signature: self.qe_report_data.qe_report_signature, + qe_auth_data: self.qe_report_data.qe_auth_data, + certification_data: self.qe_report_data.certification_data, + } + } +} + +impl Decode for AuthDataV4 { + fn decode(input: &mut I) -> Result { + let ecdsa_signature = Decode::decode(input)?; + let ecdsa_attestation_key = Decode::decode(input)?; + let certification_data: CertificationData = Decode::decode(input)?; + let qe_report_data = + QEReportCertificationData::decode(&mut &certification_data.body.data[..])?; + Ok(AuthDataV4 { + ecdsa_signature, + ecdsa_attestation_key, + certification_data, + qe_report_data, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +pub enum AuthData { + V3(AuthDataV3), + V4(AuthDataV4), +} + +impl AuthData { + #[allow(missing_docs)] + pub fn into_v3(self) -> AuthDataV3 { + match self { + AuthData::V3(data) => data, + AuthData::V4(data) => data.into_v3(), + } + } +} + +fn decode_auth_data(ver: u16, input: &mut &[u8]) -> Result { + match ver { + 3 => { + let auth_data = AuthDataV3::decode(input)?; + Ok(AuthData::V3(auth_data)) + } + 4 => { + let auth_data = AuthDataV4::decode(input)?; + Ok(AuthData::V4(auth_data)) + } + _ => Err(error::QuoteError::QuoteVersion), + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +#[non_exhaustive] +pub enum Report { + SgxEnclave(EnclaveReport), + TD10(TDReport10), + TD15(TDReport15), +} + +impl Report { + #[allow(missing_docs)] + pub fn is_sgx(&self) -> bool { + matches!(self, Report::SgxEnclave(_)) + } + + #[allow(missing_docs)] + pub fn as_td10(&self) -> Option<&TDReport10> { + match self { + Report::TD10(report) => Some(report), + Report::TD15(report) => Some(&report.base), + _ => None, + } + } + + #[allow(missing_docs)] + pub fn as_td15(&self) -> Option<&TDReport15> { + match self { + Report::TD15(report) => Some(report), + _ => None, + } + } + + #[allow(missing_docs)] + pub fn as_sgx(&self) -> Option<&EnclaveReport> { + match self { + Report::SgxEnclave(report) => Some(report), + _ => None, + } + } +} + +impl Display for Report { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn space_or_newline(f: &mut Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + writeln!(f) + } else { + write!(f, " ") + } + } + match self { + Report::SgxEnclave(report_body) => { + write!(f, "mrsigner: {}", hex::encode(report_body.mr_signer))?; + space_or_newline(f)?; + write!(f, "mrenclave: {}", hex::encode(report_body.mr_enclave))?; + space_or_newline(f)?; + write!( + f, + "reportdata: {}", + hex::encode(report_body.report_data.as_slice()) + )?; + } + Report::TD10(report_body) => { + write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?; + space_or_newline(f)?; + write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?; + space_or_newline(f)?; + write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?; + space_or_newline(f)?; + write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?; + space_or_newline(f)?; + write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?; + space_or_newline(f)?; + write!( + f, + "reportdata: {}", + hex::encode(report_body.report_data.as_slice()) + )?; + } + Report::TD15(report_body) => { + let report_body = &report_body.base; + write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?; + space_or_newline(f)?; + write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?; + space_or_newline(f)?; + write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?; + space_or_newline(f)?; + write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?; + space_or_newline(f)?; + write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?; + space_or_newline(f)?; + write!( + f, + "reportdata: {}", + hex::encode(report_body.report_data.as_slice()) + )?; + } + } + Ok(()) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[allow(missing_docs)] +#[repr(C)] +pub struct Quote { + pub header: Header, + pub report: Report, + pub auth_data: AuthData, +} + +impl Decode for Quote { + fn decode(input: &mut I) -> Result { + let header = Header::decode(input)?; + trace!(?header); + let report; + match header.version { + 3 => { + if header.tee_type != TEE_TYPE_SGX { + return Err(error::QuoteError::InvalidTeeType); + } + report = Report::SgxEnclave(EnclaveReport::decode(input)?); + } + 4 => match header.tee_type { + TEE_TYPE_SGX => { + report = Report::SgxEnclave(EnclaveReport::decode(input)?); + } + TEE_TYPE_TDX => { + report = Report::TD10(TDReport10::decode(input)?); + } + _ => return Err(error::QuoteError::InvalidTeeType), + }, + 5 => { + let body = Body::decode(input)?; + match body.body_type { + BODY_SGX_ENCLAVE_REPORT_TYPE => { + report = Report::SgxEnclave(EnclaveReport::decode(input)?); + } + BODY_TD_REPORT10_TYPE => { + report = Report::TD10(TDReport10::decode(input)?); + } + BODY_TD_REPORT15_TYPE => { + report = Report::TD15(TDReport15::decode(input)?); + } + _ => return Err(error::QuoteError::UnsupportedBodyType), + } + } + _ => return Err(error::QuoteError::QuoteVersion), + } + let data = Data::::decode(input)?; + let auth_data = decode_auth_data(header.version, &mut &data.data[..])?; + Ok(Quote { + header, + report, + auth_data, + }) + } +} + +/// FMSPC (Family-Model-Stepping-Platform-CustomSKU) is a 6-byte identifier +/// that uniquely identifies a platform's SGX TCB level. +/// It is extracted from the PCK certificate in the SGX quote and is used to +/// fetch TCB information from Intel's Provisioning Certification Service. +pub type Fmspc = [u8; 6]; + +/// CPU Security Version Number (CPUSVN) is a 16-byte value representing +/// the security version of the CPU microcode and firmware. +/// It is used in SGX attestation to determine the security patch level +/// of the platform. +pub type CpuSvn = [u8; 16]; + +/// Security Version Number (SVN) is a 16-bit value representing the +/// security version of a component (like PCE or QE). +/// Higher values indicate newer security patches have been applied. +pub type Svn = u16; + +impl Quote { + /// Parse a TEE quote from a byte slice. + pub fn parse(quote: &[u8]) -> Result { + let mut input = quote; + let quote = Quote::decode(&mut input)?; + Ok(quote) + } + + /// Returns the TEE type of this quote. + /// + /// The TEE type is extracted from the quote header and can be either SGX or TDX for now. + /// Due to validation during quote parsing, this is guaranteed to return only + /// valid TEE types. + pub fn tee_type(&self) -> TEEType { + match self.header.tee_type { + TEE_TYPE_SGX => TEEType::SGX, + TEE_TYPE_TDX => TEEType::TDX, + // Creating `Self` via `parse()`, + // should guarantee that the TEE type + // is nothing else than SGX or TDX + _ => unreachable!(), + } + } + + /// Get the raw certificate chain from the quote. + pub fn raw_cert_chain(&self) -> Result<&[u8], QuoteError> { + let cert_data = match &self.auth_data { + AuthData::V3(data) => &data.certification_data, + AuthData::V4(data) => &data.qe_report_data.certification_data, + }; + if cert_data.cert_type != 5 { + QuoteError::QuoteCertificationDataUnsupported(format!( + "Unsupported cert type: {}", + cert_data.cert_type + )); + } + Ok(&cert_data.body.data) + } + + /// Get the FMSPC from the quote. + pub fn fmspc(&self) -> Result { + let raw_cert_chain = self.raw_cert_chain()?; + let certs = utils::extract_certs(raw_cert_chain)?; + let cert = certs + .first() + .ok_or(QuoteError::Unexpected("Invalid certificate".into()))?; + let extension_section = utils::get_intel_extension(cert)?; + utils::get_fmspc(&extension_section) + } + + /// Get the report data + pub fn get_report_data(&self) -> &[u8] { + match &self.report { + Report::SgxEnclave(r) => r.report_data.as_slice(), + Report::TD10(r) => r.report_data.as_slice(), + Report::TD15(r) => r.base.report_data.as_slice(), + } + } +} + +/// TEE type +#[non_exhaustive] +pub enum TEEType { + /// Intel SGX + SGX, + /// Intel TDX + TDX, + /// AMD SEV-SNP + SNP, +} + +impl Display for TEEType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + TEEType::SGX => "sgx", + TEEType::TDX => "tdx", + TEEType::SNP => "snp", + }; + write!(f, "{str}") + } +} + +impl FromStr for TEEType { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "sgx" => Ok(TEEType::SGX), + "tdx" => Ok(TEEType::TDX), + "snp" => Ok(TEEType::SNP), + _ => Err("Invalid TEE type".to_string()), + } + } +} + +/// Get the attestation quote from a TEE +#[cfg(feature = "quote_op")] +pub fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> { + os::get_quote(report_data) +} + +/// The result of the quote verification +pub struct QuoteVerificationResult { + /// the raw result + pub result: TcbLevel, + /// indicates if the collateral is expired + pub collateral_expired: bool, + /// the earliest expiration date of the collateral + pub earliest_expiration_date: i64, + /// Date of the TCB level + pub tcb_level_date_tag: i64, + /// the advisory string + pub advisories: Vec, + /// the quote + pub quote: Quote, +} + +/// The collateral data needed to do remote attestation for SGX and TDX +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Collateral { + /// Major version of the collateral data + pub major_version: u16, + /// Minor version of the collateral data + pub minor_version: u16, + /// Type of TEE (SGX=0, TDX=0x81) + pub tee_type: u32, + /// The PCK CRL issuer chain used for validating the PCK CRL + pub pck_crl_issuer_chain: Box<[u8]>, + /// The root CA CRL used for validating the PCK CRL issuer chain + pub root_ca_crl: Box<[u8]>, + /// The PCK CRL used for validating the PCK certificate + pub pck_crl: Box<[u8]>, + /// The TCB info issuer chain used for validating the TCB info + pub tcb_info_issuer_chain: Box<[u8]>, + /// The TCB info used for determining the TCB level + pub tcb_info: Box<[u8]>, + /// The QE identity issuer chain used for validating the QE identity + pub qe_identity_issuer_chain: Box<[u8]>, + /// The QE identity used for validating the QE + pub qe_identity: Box<[u8]>, +} + +/// Get the collateral data from an SGX or TDX quote +#[cfg(feature = "quote_op")] +pub fn get_collateral(quote: &[u8]) -> Result { + os::get_collateral(quote) +} + +/// Verifies a quote with optional collateral material +#[cfg(feature = "quote_op")] +pub fn verify_quote_with_collateral( + quote: &[u8], + collateral: Option<&Collateral>, + current_time: i64, +) -> Result { + os::verify_quote_with_collateral(quote, collateral, current_time) +} diff --git a/crates/teepot/src/quote/phala.rs b/crates/teepot/src/quote/phala.rs new file mode 100644 index 0000000..fcbd48e --- /dev/null +++ b/crates/teepot/src/quote/phala.rs @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Emulate Intel DCAP library collateral and verification + +use crate::quote::{ + error::{QuoteContext, QuoteContextErr, QuoteError}, + tcblevel::TcbLevel, + Collateral, Quote, QuoteVerificationResult, TEEType, +}; +use bytes::Bytes; +use dcap_qvl::{verify::VerifiedReport, QuoteCollateralV3}; +use std::ffi::{CStr, CString}; +use std::str::FromStr; + +/// Helper function to extract a required header value from a response +fn extract_header_value( + response: &reqwest::Response, + header_name: &str, +) -> Result { + response + .headers() + .get(header_name) + .ok_or_else(|| QuoteError::Unexpected(format!("Missing required header: {header_name}")))? + .to_str() + .map_err(|e| QuoteError::Unexpected(format!("Invalid header value: {e}"))) + .map(str::to_string) +} + +/// Fetch collateral data from Intel's Provisioning Certification Service +async fn fetch_pcs_collateral( + quote: &[u8], +) -> Result<(QuoteCollateralV3, String, Bytes), QuoteError> { + let client = reqwest::Client::new(); + let response = client + .get("https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=platform") + .send() + .await + .map_err(|e| QuoteError::Unexpected(format!("Failed to fetch collateral: {e}")))?; + + // Extract required fields + let issuer_chain = extract_header_value(&response, "SGX-PCK-CRL-Issuer-Chain")?; + let pck_crl_data = response + .bytes() + .await + .map_err(|e| QuoteError::Unexpected(format!("Failed to fetch collateral data: {e}")))?; + + // Fetch the full collateral + dcap_qvl::collateral::get_collateral_from_pcs(quote, std::time::Duration::from_secs(1000)) + .await + .map(|collateral| (collateral, issuer_chain, pck_crl_data)) + .str_context("Fetching PCS collateral with `get_collateral_from_pcs`") +} + +/// Get collateral for a quote, handling the async operations +pub(crate) fn get_collateral(quote: &[u8]) -> Result { + // Execute the async operation in a separate thread + let result = std::thread::scope(|s| { + s.spawn(|| { + // Create a minimal runtime for this thread only + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .context("Failed to build tokio runtime")?; + + // Run the async function + rt.block_on(fetch_pcs_collateral(quote)) + }) + .join() + .map_err(|_| QuoteError::Unexpected("Thread panic in get_collateral".into())) + })??; + + // Destructure the result + let (collateral, pck_crl, pck_issuer_chain) = result; + + // Convert QuoteCollateralV3 to Collateral + convert_to_collateral(collateral, &pck_crl, &pck_issuer_chain) +} + +// Helper function to convert QuoteCollateralV3 to Collateral +fn convert_to_collateral( + collateral: QuoteCollateralV3, + pck_crl: &str, + pck_issuer_chain: &[u8], +) -> Result { + let QuoteCollateralV3 { + tcb_info_issuer_chain, + tcb_info, + tcb_info_signature, + qe_identity_issuer_chain, + qe_identity, + qe_identity_signature, + } = collateral; + + let tcb_info_signature = hex::encode(tcb_info_signature); + let qe_identity_signature = hex::encode(qe_identity_signature); + + // Create strings with proper formatting + let tcb_info_json = + format!("{{ \"tcbInfo\": {tcb_info}, \"signature\": \"{tcb_info_signature}\" }}"); + + let qe_identity_json = format!( + "{{ \"enclaveIdentity\": {qe_identity}, \"signature\": \"{qe_identity_signature}\" }}" + ); + + // Helper to create CString and convert to Box<[u8]> + let to_bytes_with_nul = |s: String, context: &str| -> Result, QuoteError> { + Ok(CString::new(s) + .str_context(context)? + .as_bytes_with_nul() + .into()) + }; + + Ok(Collateral { + // Default/unhandled values + major_version: 0, + minor_version: 0, + tee_type: 0, + root_ca_crl: Box::new([]), + + // Converted values + pck_crl_issuer_chain: pck_issuer_chain.into(), + pck_crl: pck_crl.as_bytes().into(), + tcb_info_issuer_chain: to_bytes_with_nul(tcb_info_issuer_chain, "tcb_info_issuer_chain")?, + tcb_info: to_bytes_with_nul(tcb_info_json, "tcb_info")?, + qe_identity_issuer_chain: to_bytes_with_nul( + qe_identity_issuer_chain, + "qe_identity_issuer_chain", + )?, + qe_identity: to_bytes_with_nul(qe_identity_json, "qe_identity")?, + }) +} + +/// Split the last zero byte +fn get_str_from_bytes(bytes: &[u8], context: &str) -> Result { + let c_str = CStr::from_bytes_until_nul(bytes) + .str_context(format!("Failed to extract CString: {context}"))?; + Ok(c_str.to_string_lossy().into_owned()) +} + +/// Parse JSON field from collateral data +fn parse_json_field(data: &[u8], context: &str) -> Result { + serde_json::from_str(&get_str_from_bytes(data, context)?) + .str_context(format!("Failed to parse JSON: {context}")) +} + +/// Convert Collateral to QuoteCollateralV3 +fn convert_collateral(collateral: &Collateral) -> Result { + // Parse TCB info + let tcb_info_json = parse_json_field(collateral.tcb_info.as_ref(), "tcb_info_json")?; + + let tcb_info = tcb_info_json["tcbInfo"].to_string(); + let tcb_info_signature = tcb_info_json + .get("signature") + .context("TCB Info missing 'signature' field")? + .as_str() + .context("TCB Info signature must be a string")?; + + let tcb_info_signature = hex::decode(tcb_info_signature) + .ok() + .context("TCB Info signature must be valid hex")?; + + // Parse QE identity + let qe_identity_json = parse_json_field(collateral.qe_identity.as_ref(), "qe_identity_json")?; + + let qe_identity = qe_identity_json + .get("enclaveIdentity") + .context("QE Identity missing 'enclaveIdentity' field")? + .to_string(); + + let qe_identity_signature = qe_identity_json + .get("signature") + .context("QE Identity missing 'signature' field")? + .as_str() + .context("QE Identity signature must be a string")?; + + let qe_identity_signature = hex::decode(qe_identity_signature) + .ok() + .context("QE Identity signature must be valid hex")?; + + Ok(QuoteCollateralV3 { + tcb_info_issuer_chain: get_str_from_bytes( + collateral.tcb_info_issuer_chain.as_ref(), + "convert_collateral: tcb_info_issuer_chain", + )?, + tcb_info, + tcb_info_signature, + qe_identity_issuer_chain: get_str_from_bytes( + collateral.qe_identity_issuer_chain.as_ref(), + "convert_collateral: qe_identity_issuer_chain", + )?, + qe_identity, + qe_identity_signature, + }) +} + +/// Verify a quote with the provided collateral +pub(crate) fn verify_quote_with_collateral( + quote: &[u8], + collateral: Option<&Collateral>, + current_time: i64, +) -> Result { + // Convert collateral or return error if not provided + let collateral = collateral + .ok_or_else(|| QuoteError::Unexpected("No collateral provided".into())) + .and_then(convert_collateral)?; + + // Convert current time to u64 + let current_time_u64 = current_time + .try_into() + .str_context("Failed to convert current_time to u64")?; + + // Verify the quote + let verified_report = dcap_qvl::verify::verify(quote, &collateral, current_time_u64) + .expect("Failed to verify quote"); + + let VerifiedReport { + status, + advisory_ids, + report: _, + } = verified_report; + + // Parse TCB level from status + let result = + TcbLevel::from_str(&status).str_context("Failed to parse TCB level from status")?; + + // Parse quote + let quote = Quote::parse(quote)?; + + let tcb_info_json: serde_json::Value = + serde_json::from_str(&String::from_utf8_lossy(collateral.tcb_info.as_ref())) + .str_context("verify_quote_with_collateral tcb_info_json")?; + + let next_update = tcb_info_json + .get("nextUpdate") + .context("verify_quote_with_collateral: TCB Info missing 'nextUpdate' field")? + .as_str() + .context("verify_quote_with_collateral: TCB Info nextUpdate must be a string")?; + + let next_update = chrono::DateTime::parse_from_rfc3339(next_update) + .ok() + .context("verify_quote_with_collateral: Failed to parse next update")?; + + Ok(QuoteVerificationResult { + result, + collateral_expired: result == TcbLevel::OutOfDate, + earliest_expiration_date: next_update + .signed_duration_since(chrono::DateTime::UNIX_EPOCH) + .num_seconds(), + tcb_level_date_tag: 0, + advisories: advisory_ids, + quote, + }) +} + +/// Get the attestation quote from a TEE +pub fn get_quote(_report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> { + Err(QuoteError::UnknownTee) +} diff --git a/crates/teepot/src/sgx/tcblevel.rs b/crates/teepot/src/quote/tcblevel.rs similarity index 55% rename from crates/teepot/src/sgx/tcblevel.rs rename to crates/teepot/src/quote/tcblevel.rs index 2e05c92..96cd5a0 100644 --- a/crates/teepot/src/sgx/tcblevel.rs +++ b/crates/teepot/src/quote/tcblevel.rs @@ -1,17 +1,21 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs //! Intel SGX Enclave TCB level wrapper use enumset::EnumSetType; -use intel_tee_quote_verification_rs::sgx_ql_qv_result_t; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; pub use enumset::EnumSet; /// TCB level -#[derive(EnumSetType, Debug)] +#[derive(EnumSetType, Debug, Serialize, Deserialize)] +#[enumset(serialize_repr = "list")] +#[non_exhaustive] pub enum TcbLevel { /// TCB is up to date Ok, @@ -29,37 +33,19 @@ pub enum TcbLevel { Invalid, } -impl From for TcbLevel { - fn from(value: sgx_ql_qv_result_t) -> Self { - match value { - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => TcbLevel::Ok, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => TcbLevel::OutOfDate, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => { - TcbLevel::OutOfDateConfigNeeded - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => TcbLevel::SwHardeningNeeded, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { - TcbLevel::ConfigAndSwHardeningNeeded - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => TcbLevel::ConfigNeeded, - _ => TcbLevel::Invalid, - } - } -} - impl FromStr for TcbLevel { - type Err = (); + type Err = String; fn from_str(s: &str) -> Result { - match s { - "Ok" => Ok(TcbLevel::Ok), - "ConfigNeeded" => Ok(TcbLevel::ConfigNeeded), - "ConfigAndSwHardeningNeeded" => Ok(TcbLevel::ConfigAndSwHardeningNeeded), - "SwHardeningNeeded" => Ok(TcbLevel::SwHardeningNeeded), - "OutOfDate" => Ok(TcbLevel::OutOfDate), - "OutOfDateConfigNeeded" => Ok(TcbLevel::OutOfDateConfigNeeded), - "Invalid" => Ok(TcbLevel::Invalid), - _ => Err(()), + match s.to_ascii_lowercase().as_str() { + "ok" | "uptodate" => Ok(TcbLevel::Ok), + "configneeded" => Ok(TcbLevel::ConfigNeeded), + "configandswhardeningneeded" => Ok(TcbLevel::ConfigAndSwHardeningNeeded), + "swhardeningneeded" => Ok(TcbLevel::SwHardeningNeeded), + "outofdate" => Ok(TcbLevel::OutOfDate), + "outofdateconfigneeded" => Ok(TcbLevel::OutOfDateConfigNeeded), + "invalid" => Ok(TcbLevel::Invalid), + _ => Err(format!("Invalid TCB level: {s}")), } } } @@ -85,8 +71,8 @@ pub fn parse_tcb_levels( let mut set = EnumSet::new(); for level_str in s.split(',') { let level_str = level_str.trim(); - let level = TcbLevel::from_str(level_str) - .map_err(|_| format!("Invalid TCB level: {}", level_str))?; + let level = + TcbLevel::from_str(level_str).map_err(|_| format!("Invalid TCB level: {level_str}"))?; set.insert(level); } Ok(set) diff --git a/crates/teepot/src/quote/utils.rs b/crates/teepot/src/quote/utils.rs new file mode 100644 index 0000000..23faa0e --- /dev/null +++ b/crates/teepot/src/quote/utils.rs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +// Parts of it are Copyright (c) 2024 Phala Network +// and copied from https://github.com/Phala-Network/dcap-qvl + +use crate::quote::{error::QuoteError, Fmspc}; +use asn1_der::{ + typed::{DerDecodable, Sequence}, + DerObject, +}; +use x509_cert::certificate::CertificateInner; + +pub mod oids { + use const_oid::ObjectIdentifier as OID; + + const fn oid(s: &str) -> OID { + OID::new_unwrap(s) + } + + pub const SGX_EXTENSION: OID = oid("1.2.840.113741.1.13.1"); + pub const FMSPC: OID = oid("1.2.840.113741.1.13.1.4"); + + #[test] + fn const_oid_works() { + assert_eq!( + SGX_EXTENSION.as_bytes(), + oid("1.2.840.113741.1.13.1").as_bytes() + ); + } +} +pub fn get_intel_extension(cert: &CertificateInner) -> Result, QuoteError> { + let mut extension_iter = cert + .tbs_certificate + .extensions + .as_deref() + .unwrap_or(&[]) + .iter() + .filter(|e| e.extn_id == oids::SGX_EXTENSION) + .map(|e| e.extn_value.clone()); + + let extension = extension_iter + .next() + .ok_or_else(|| QuoteError::Unexpected("Intel extension not found".into()))?; + if extension_iter.next().is_some() { + //"There should only be one section containing Intel extensions" + return Err(QuoteError::Unexpected("Intel extension ambiguity".into())); + } + Ok(extension.into_bytes()) +} + +pub fn find_extension(path: &[&[u8]], raw: &[u8]) -> Result, QuoteError> { + let obj = DerObject::decode(raw) + .map_err(|_| QuoteError::Unexpected("Failed to decode DER object".into()))?; + let subobj = + get_obj(path, obj).map_err(|_| QuoteError::Unexpected("Failed to get subobject".into()))?; + Ok(subobj.value().to_vec()) +} + +fn get_obj<'a>(path: &[&[u8]], mut obj: DerObject<'a>) -> Result, QuoteError> { + for oid in path { + let seq = Sequence::load(obj) + .map_err(|_| QuoteError::Unexpected("Failed to load sequence".into()))?; + obj = sub_obj(oid, seq) + .map_err(|_| QuoteError::Unexpected("Failed to get subobject".into()))?; + } + Ok(obj) +} + +fn sub_obj<'a>(oid: &[u8], seq: Sequence<'a>) -> Result, QuoteError> { + for i in 0..seq.len() { + let entry = seq + .get(i) + .map_err(|_| QuoteError::Unexpected("Failed to get entry".into()))?; + let entry = Sequence::load(entry) + .map_err(|_| QuoteError::Unexpected("Failed to load sequence".into()))?; + let name = entry + .get(0) + .map_err(|_| QuoteError::Unexpected("Failed to get name".into()))?; + let value = entry + .get(1) + .map_err(|_| QuoteError::Unexpected("Failed to get value".into()))?; + if name.value() == oid { + return Ok(value); + } + } + Err(QuoteError::Unexpected("Oid is missing".into())) +} + +pub fn get_fmspc(extension_section: &[u8]) -> Result { + let data = find_extension(&[oids::FMSPC.as_bytes()], extension_section) + .map_err(|_| QuoteError::Unexpected("Failed to find Fmspc".into()))?; + if data.len() != 6 { + return Err(QuoteError::Unexpected("Fmspc length mismatch".into())); + } + + data.try_into() + .map_err(|_| QuoteError::Unexpected("Failed to decode Fmspc".into())) +} + +pub fn extract_certs(cert_chain: &[u8]) -> Result, QuoteError> { + let cert_chain = cert_chain.strip_suffix(&[0]).unwrap_or(cert_chain); + + CertificateInner::::load_pem_chain(cert_chain) + .map_err(|e| QuoteError::Unexpected(format!("Could not load a PEM chain: {}", e))) +} diff --git a/crates/teepot/src/sgx/error.rs b/crates/teepot/src/sgx/error.rs deleted file mode 100644 index 492a42a..0000000 --- a/crates/teepot/src/sgx/error.rs +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs - -//! Intel SGX Enclave error wrapper - -use bytemuck::PodCastError; -use intel_tee_quote_verification_rs::quote3_error_t; -use std::fmt::Formatter; - -/// Wrapper for the quote verification Error -#[derive(Copy, Clone)] -pub struct Quote3Error { - /// error message - pub msg: &'static str, - /// raw error code - pub inner: quote3_error_t, -} - -impl std::fmt::Display for Quote3Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {:?}", self.msg, self.inner) - } -} - -impl std::fmt::Debug for Quote3Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {:?}", self.msg, self.inner) - } -} - -impl std::error::Error for Quote3Error {} - -impl From for Quote3Error { - fn from(inner: quote3_error_t) -> Self { - Self { - msg: "Generic", - inner, - } - } -} - -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub enum QuoteFromError { - #[error(transparent)] - PodCastError(#[from] PodCastError), - - #[error("Quote version is invalid")] - InvalidVersion, -} diff --git a/crates/teepot/src/sgx/mod.rs b/crates/teepot/src/sgx/mod.rs index 2fc289b..2b66c78 100644 --- a/crates/teepot/src/sgx/mod.rs +++ b/crates/teepot/src/sgx/mod.rs @@ -1,249 +1,39 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs // Copyright (c) The Enarx Project Developers https://github.com/enarx/sgx //! Intel SGX Enclave report structures. -pub mod error; pub mod sign; -pub mod tcblevel; -use bytemuck::{cast_slice, try_from_bytes, AnyBitPattern, PodCastError}; -use intel_tee_quote_verification_rs::{ - quote3_error_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size, - tee_supp_data_descriptor_t, tee_verify_quote, +use crate::quote::error::QuoteContext; +pub use crate::quote::{error::QuoteError, Collateral}; +use std::{ + fs::OpenOptions, + io::{Read, Write}, }; -use std::ffi::CStr; -use std::fs::OpenOptions; -use std::io::{Read, Write}; -use std::mem; -use tracing::{trace, warn}; - -use crate::quote::GetQuoteError; -pub use error::{Quote3Error, QuoteFromError}; -pub use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral}; -pub use tcblevel::{parse_tcb_levels, EnumSet, TcbLevel}; - -/// Structure of a quote -#[derive(Copy, Clone, Debug, AnyBitPattern)] -#[repr(C)] -pub struct Quote { - version: [u8; 2], - key_type: [u8; 2], - reserved: [u8; 4], - qe_svn: [u8; 2], - pce_svn: [u8; 2], - qe_vendor_id: [u8; 16], - /// The user data that was passed, when creating the enclave - pub user_data: [u8; 20], - /// The report body - pub report_body: ReportBody, -} - -impl Quote { - /// Creates a quote from a byte slice - pub fn try_from_bytes(bytes: &[u8]) -> Result<&Self, QuoteFromError> { - if bytes.len() < mem::size_of::() { - return Err(PodCastError::SizeMismatch.into()); - } - let this: &Self = try_from_bytes(&bytes[..mem::size_of::()])?; - if this.version() != 3 { - return Err(QuoteFromError::InvalidVersion); - } - Ok(this) - } - - /// Version of the `Quote` structure - pub fn version(&self) -> u16 { - u16::from_le_bytes(self.version) - } -} - -/// The enclave report body. -/// -/// For more information see the following documents: -/// -/// [Intel® Software Guard Extensions (Intel® SGX) Data Center Attestation Primitives: ECDSA Quote Library API](https://download.01.org/intel-sgx/dcap-1.0/docs/SGX_ECDSA_QuoteGenReference_DCAP_API_Linux_1.0.pdf) -/// -/// Table 5, A.4. Quote Format -/// -/// [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3 (3A, 3B, 3C & 3D): System Programming Guide](https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3d-part-4-manual.html) -/// -/// Table 38-21. Layout of REPORT -#[derive(Copy, Clone, Debug, AnyBitPattern)] -#[repr(C)] -pub struct ReportBody { - /// The security version number of the enclave. - pub cpusvn: [u8; 16], - /// The Misc section of the StateSaveArea of the enclave - pub miscselect: [u8; 4], - reserved1: [u8; 28], - /// The allowed Features of the enclave. - pub features: [u8; 8], - /// The allowed XCr0Flags of the enclave. - pub xfrm: [u8; 8], - /// The measurement of the enclave - pub mrenclave: [u8; 32], - reserved2: [u8; 32], - /// The hash of the public key, that signed the enclave - pub mrsigner: [u8; 32], - reserved3: [u8; 96], - /// ISV assigned Product ID of the enclave. - pub isv_prodid: [u8; 2], - /// ISV assigned SVN (security version number) of the enclave. - pub isv_svn: [u8; 2], - reserved4: [u8; 60], - /// The enclave report data, injected when requesting the quote, that is used for attestation. - pub reportdata: [u8; 64], -} - -/// The result of the quote verification -pub struct QuoteVerificationResult<'a> { - /// the raw result - pub result: sgx_ql_qv_result_t, - /// indicates if the collateral is expired - pub collateral_expired: bool, - /// the earliest expiration date of the collateral - pub earliest_expiration_date: i64, - /// Date of the TCB level - pub tcb_level_date_tag: i64, - /// the advisory string - pub advisories: Vec, - /// the quote - pub quote: &'a Quote, -} - -/// Verifies a quote with optional collateral material -pub fn verify_quote_with_collateral<'a>( - quote: &'a [u8], - collateral: Option<&Collateral>, - current_time: i64, -) -> Result, Quote3Error> { - let mut supp_data: mem::MaybeUninit = mem::MaybeUninit::zeroed(); - let mut supp_data_desc = tee_supp_data_descriptor_t { - major_version: 0, - data_size: 0, - p_data: supp_data.as_mut_ptr() as *mut u8, - }; - trace!("tee_get_supplemental_data_version_and_size"); - let (_, supp_size) = - tee_get_supplemental_data_version_and_size(quote).map_err(|e| Quote3Error { - msg: "tee_get_supplemental_data_version_and_size", - inner: e, - })?; - - trace!( - "tee_get_supplemental_data_version_and_size supp_size: {}", - supp_size - ); - - if supp_size == mem::size_of::() as u32 { - supp_data_desc.data_size = supp_size; - } else { - supp_data_desc.data_size = 0; - trace!( - "tee_get_supplemental_data_version_and_size supp_size: {}", - supp_size - ); - trace!( - "mem::size_of::(): {}", - mem::size_of::() - ); - warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release.") - } - - let p_supplemental_data = match supp_data_desc.data_size { - 0 => None, - _ => Some(&mut supp_data_desc), - }; - - let has_sup = p_supplemental_data.is_some(); - - trace!("tee_verify_quote"); - - let (collateral_expiration_status, result) = - tee_verify_quote(quote, collateral, current_time, None, p_supplemental_data).map_err( - |e| Quote3Error { - msg: "tee_verify_quote", - inner: e, - }, - )?; - - // check supplemental data if necessary - let (advisories, earliest_expiration_date, tcb_level_date_tag) = if has_sup { - unsafe { - let supp_data = supp_data.assume_init(); - // convert to valid UTF-8 string - let ads = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..])) - .ok() - .and_then(|s| CStr::to_str(s).ok()) - .into_iter() - .flat_map(|s| s.split(',').map(str::trim).map(String::from)) - .filter(|s| !s.is_empty()) - .collect(); - ( - ads, - supp_data.earliest_expiration_date, - supp_data.tcb_level_date_tag, - ) - } - } else { - (vec![], 0, 0) - }; - - let quote = Quote::try_from_bytes(quote).map_err(|_| Quote3Error { - msg: "Quote::try_from_bytes", - inner: quote3_error_t::SGX_QL_QUOTE_FORMAT_UNSUPPORTED, - })?; - - let res = QuoteVerificationResult { - collateral_expired: collateral_expiration_status != 0, - earliest_expiration_date, - tcb_level_date_tag, - result, - quote, - advisories, - }; - - Ok(res) -} /// Get the attestation report in a Gramine enclave -pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result, GetQuoteError> { +pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result, QuoteError> { let mut file = OpenOptions::new() .write(true) .open("/dev/attestation/user_report_data") - .map_err(|e| GetQuoteError { - msg: "Failed to open `/dev/attestation/user_report_data`".into(), - source: e, - })?; + .context("opening `/dev/attestation/user_report_data`")?; - file.write(report_data).map_err(|e| GetQuoteError { - msg: "Failed to write `/dev/attestation/user_report_data`".into(), - source: e, - })?; + file.write(report_data) + .context("writing `/dev/attestation/user_report_data`")?; drop(file); let mut file = OpenOptions::new() .read(true) .open("/dev/attestation/quote") - .map_err(|e| GetQuoteError { - msg: "Failed to open `/dev/attestation/quote`".into(), - source: e, - })?; + .context("opening `/dev/attestation/quote`")?; let mut quote = Vec::new(); - file.read_to_end(&mut quote).map_err(|e| GetQuoteError { - msg: "Failed to read `/dev/attestation/quote`".into(), - source: e, - })?; + file.read_to_end(&mut quote) + .context("reading `/dev/attestation/quote`")?; + Ok(quote.into_boxed_slice()) } - -/// Wrapper func for error -/// TODO: move to intel_tee_quote_verification_rs -pub fn tee_qv_get_collateral(quote: &[u8]) -> Result { - intel_tee_quote_verification_rs::tee_qv_get_collateral(quote).map_err(Into::into) -} diff --git a/crates/teepot/src/sgx/sign.rs b/crates/teepot/src/sgx/sign.rs index 7b58f43..6d136a3 100644 --- a/crates/teepot/src/sgx/sign.rs +++ b/crates/teepot/src/sgx/sign.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs // Copyright (c) The Enarx Project Developers https://github.com/enarx/sgx @@ -12,14 +12,13 @@ use bytemuck::{bytes_of, Pod, Zeroable}; use num_integer::Integer; use num_traits::ToPrimitive; -use rand::thread_rng; use rsa::{ pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, LineEnding}, + rand_core::OsRng, traits::PublicKeyParts, BigUint, Pkcs1v15Sign, RsaPrivateKey, }; -use sha2::Digest as _; -use sha2::Sha256; +use sha2::{Digest as _, Sha256}; pub use zeroize::Zeroizing; /// Enclave CPU attributes @@ -51,8 +50,8 @@ pub struct Author { unsafe impl Zeroable for Author {} impl Author { - const HEADER1: [u8; 16] = 0x06000000E10000000000010000000000u128.to_be_bytes(); - const HEADER2: [u8; 16] = 0x01010000600000006000000001000000u128.to_be_bytes(); + const HEADER1: [u8; 16] = 0x0600_0000_E100_0000_0000_0100_0000_0000u128.to_be_bytes(); + const HEADER2: [u8; 16] = 0x0101_0000_6000_0000_6000_0000_0100_0000u128.to_be_bytes(); #[allow(clippy::unreadable_literal)] /// Creates a new Author from a date and software defined value. @@ -246,7 +245,7 @@ impl Digest for S256Digest { #[inline] fn update(&mut self, bytes: &[u8]) { - self.0.update(bytes) + self.0.update(bytes); } #[inline] @@ -270,7 +269,7 @@ impl PrivateKey for RS256PrivateKey { type Error = rsa::errors::Error; fn generate(exponent: u8) -> Result { - let mut rng = thread_rng(); + let mut rng = OsRng; let exp = BigUint::from(exponent); let key = RsaPrivateKey::new_with_exp(&mut rng, 384 * 8, &exp)?; Ok(Self::new(key)) diff --git a/crates/teepot/src/tdx/mod.rs b/crates/teepot/src/tdx/mod.rs new file mode 100644 index 0000000..89fa9e8 --- /dev/null +++ b/crates/teepot/src/tdx/mod.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! Intel TDX helper functions. + +#[cfg(all(feature = "quote_op", target_os = "linux", target_arch = "x86_64"))] +pub mod rtmr; + +/// The sha384 digest of 0u32, which is used in the UEFI TPM protocol +/// as a marker. Used to advance the PCR. +/// ```shell +/// $ echo -n -e "\000\000\000\000" | sha384sum -b +/// 394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0 *- +/// ``` +pub const UEFI_MARKER_DIGEST_BYTES: [u8; 48] = [ + 0x39, 0x43, 0x41, 0xb7, 0x18, 0x2c, 0xd2, 0x27, 0xc5, 0xc6, 0xb0, 0x7e, 0xf8, 0x00, 0x0c, 0xdf, + 0xd8, 0x61, 0x36, 0xc4, 0x29, 0x2b, 0x8e, 0x57, 0x65, 0x73, 0xad, 0x7e, 0xd9, 0xae, 0x41, 0x01, + 0x9f, 0x58, 0x18, 0xb4, 0xb9, 0x71, 0xc9, 0xef, 0xfc, 0x60, 0xe1, 0xad, 0x9f, 0x12, 0x89, 0xf0, +]; diff --git a/crates/teepot/src/tdx/rtmr.rs b/crates/teepot/src/tdx/rtmr.rs new file mode 100644 index 0000000..bb26c6b --- /dev/null +++ b/crates/teepot/src/tdx/rtmr.rs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +//! rtmr event data + +use crate::sgx::QuoteError; +use teepot_tee_quote_verification_rs::tdx_attest_rs::{tdx_att_extend, tdx_attest_error_t}; + +/// The actual rtmr event data handled in DCAP +#[repr(C, packed)] +pub struct TdxRtmrEvent { + /// Always 1 + version: u32, + + /// The RTMR that will be extended. As defined in + /// https://github.com/confidential-containers/td-shim/blob/main/doc/tdshim_spec.md#td-measurement + /// we will use RTMR 3 for guest application code and configuration. + rtmr_index: u64, + + /// Data that will be used to extend RTMR + extend_data: [u8; 48usize], + + /// Not used in DCAP + event_type: u32, + + /// Always 0 + event_data_size: u32, + + /// Not used in DCAP + event_data: Vec, +} + +impl Default for TdxRtmrEvent { + fn default() -> Self { + Self { + extend_data: [0; 48], + version: 1, + rtmr_index: 3, + event_type: 0, + event_data_size: 0, + event_data: Vec::new(), + } + } +} + +impl TdxRtmrEvent { + /// use the extend data + pub fn with_extend_data(mut self, extend_data: [u8; 48]) -> Self { + self.extend_data = extend_data; + self + } + + /// extend the rtmr index + pub fn with_rtmr_index(mut self, rtmr_index: u64) -> Self { + self.rtmr_index = rtmr_index; + self + } + + /// extending the index, consuming self + pub fn extend(self) -> Result<(), QuoteError> { + let event: Vec = self.into(); + + match tdx_att_extend(&event) { + tdx_attest_error_t::TDX_ATTEST_SUCCESS => Ok(()), + error_code => Err(error_code.into()), + } + } +} + +impl From for Vec { + fn from(val: TdxRtmrEvent) -> Self { + let event_ptr = &val as *const TdxRtmrEvent as *const u8; + let event_data_size = std::mem::size_of::() * val.event_data_size as usize; + let res_size = std::mem::size_of::() * 3 + + std::mem::size_of::() + + std::mem::size_of::<[u8; 48]>() + + event_data_size; + let mut res = vec![0; res_size]; + unsafe { + for (i, chunk) in res.iter_mut().enumerate().take(res_size - event_data_size) { + *chunk = *event_ptr.add(i); + } + } + let event_data = val.event_data; + for i in 0..event_data_size { + res[i + res_size - event_data_size] = event_data[i]; + } + + res + } +} + +#[cfg(test)] +mod test { + use crate::tdx::UEFI_MARKER_DIGEST_BYTES; + + #[test] + fn test_uefi_marker_digest() { + assert_eq!( + UEFI_MARKER_DIGEST_BYTES.to_vec(), + hex::decode("394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0").unwrap() + ); + } +} diff --git a/crates/teepot/src/util/mod.rs b/crates/teepot/src/util/mod.rs new file mode 100644 index 0000000..1951142 --- /dev/null +++ b/crates/teepot/src/util/mod.rs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2023-2025 Matter Labs + +//! utility functions. + +use thiserror::Error; + +/// Errors that can occur when padding byte vectors to fixed-size arrays. +#[derive(Error, Debug)] +pub enum PadError { + /// Indicates that the input vector's length exceeds the target array size. + /// + /// # Fields + /// * `expected` - The target size of the array in bytes + /// * `actual` - The actual length of the input vector in bytes + /// + /// # Example + /// ```rust + /// # use teepot::util::{pad, PadError}; + /// let long_input = vec![1, 2, 3, 4]; + /// let result = pad::<2>(&long_input); + /// assert!(matches!( + /// result, + /// Err(PadError::InputTooLong { expected: 2, actual: 4 }) + /// )); + /// ``` + #[error("Input vector is too long - expected {expected} bytes, got {actual}")] + InputTooLong { + /// The expected length (target array size) + expected: usize, + /// The actual length of the provided input + actual: usize, + }, +} + +/// Pad a byte vector to a fixed-size array by appending zeros. If the input is longer +/// than the target size, returns an error. +/// +/// # Arguments +/// * `input` - Input byte vector to be padded with zeros +/// +/// # Returns +/// * `Result<[u8; T], PadError>` - A fixed-size array of length T, or a PadError if input is too long +/// +/// # Errors +/// Returns `PadError::InputTooLong` if the input vector length exceeds the target array size T, +/// containing both the expected and actual sizes +/// +/// # Examples +/// ```rust +/// # use teepot::util::{pad, PadError}; +/// let input = vec![1, 2, 3]; +/// let padded: [u8; 5] = pad(&input)?; +/// assert_eq!(padded, [1, 2, 3, 0, 0]); +/// +/// // Error case: input too long +/// let long_input = vec![1, 2, 3, 4, 5, 6]; +/// assert!(matches!( +/// pad::<5>(&long_input), +/// Err(PadError::InputTooLong { expected: 5, actual: 6 }) +/// )); +/// # Ok::<(), PadError>(()) +/// ``` +/// +/// # Type Parameters +/// * `T` - The fixed size of the output array in bytes +pub fn pad(input: &[u8]) -> Result<[u8; T], PadError> { + let mut output = [0u8; T]; + match input.len().cmp(&T) { + std::cmp::Ordering::Greater => Err(PadError::InputTooLong { + expected: T, + actual: input.len(), + }), + std::cmp::Ordering::Equal => { + output.copy_from_slice(input); + Ok(output) + } + std::cmp::Ordering::Less => { + output[..input.len()].copy_from_slice(input); + Ok(output) + } + } +} diff --git a/crates/teepot/tests/sgx_quote_fmspc.rs b/crates/teepot/tests/sgx_quote_fmspc.rs new file mode 100644 index 0000000..4924643 --- /dev/null +++ b/crates/teepot/tests/sgx_quote_fmspc.rs @@ -0,0 +1,1394 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024-2025 Matter Labs + +mod fmspc { + use anyhow::Result; + use teepot::quote::{Fmspc, Quote}; + + fn check_quote(raw_quote: &[u8], expected_result: &Fmspc) -> Result<()> { + let quote = Quote::parse(raw_quote).unwrap(); + let fmspc = quote.fmspc().unwrap(); + assert_eq!(&fmspc, expected_result); + Ok(()) + } + + #[test] + // alternative quote verification cannot cope with old collateral data format + fn sw_hardening() { + let quote = [ + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a, + 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07, + 0xe2, 0x5d, 0x0f, 0x4e, 0x64, 0x9c, 0xd7, 0xd8, 0xc7, 0x88, 0xbf, 0xc2, 0x7b, 0xca, + 0x4f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0b, 0x0f, 0x0e, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0xfb, 0xda, 0x9c, 0x05, 0x62, 0xfb, 0xab, 0x7d, 0x5c, 0x11, 0xeb, 0xcb, 0xd2, + 0x93, 0x32, 0x63, 0x97, 0xb4, 0x3e, 0x18, 0x20, 0x55, 0x0a, 0x0b, 0xb0, 0x92, 0x93, + 0x08, 0x8c, 0x8d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x80, 0x37, 0xd8, 0x87, 0x82, + 0xe0, 0x22, 0xe0, 0x19, 0xb3, 0x02, 0x07, 0x45, 0xb7, 0x8a, 0xa4, 0x0e, 0xd9, 0x5c, + 0x77, 0xda, 0x4b, 0xf7, 0xf3, 0x25, 0x3d, 0x3a, 0x44, 0xc4, 0xfd, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xca, 0x10, + 0x00, 0x00, 0x83, 0x90, 0x4f, 0x15, 0x14, 0x92, 0xe7, 0xfb, 0xcf, 0xa9, 0xf6, 0x11, + 0xf9, 0xce, 0x13, 0xc1, 0xb9, 0x80, 0xbc, 0x39, 0x38, 0x15, 0x18, 0x1f, 0x4a, 0x75, + 0x18, 0x70, 0x69, 0xb9, 0x1c, 0x1c, 0x8c, 0x7a, 0x38, 0x9d, 0x93, 0x77, 0x32, 0x14, + 0x05, 0xd3, 0x97, 0x22, 0x4f, 0x7d, 0xa8, 0x83, 0x40, 0xcc, 0x4f, 0x7a, 0x20, 0x9d, + 0xfe, 0x9b, 0xfe, 0x9c, 0xf0, 0x29, 0xa9, 0xd9, 0x03, 0xb7, 0xcd, 0xd2, 0x06, 0xa2, + 0xec, 0x48, 0x1b, 0xd8, 0x66, 0x0b, 0xbb, 0xa3, 0xed, 0xcc, 0xa7, 0x75, 0x2c, 0x12, + 0x30, 0xd9, 0x23, 0xfd, 0x37, 0x7f, 0x38, 0x45, 0xa5, 0x87, 0x03, 0xbe, 0x5d, 0x02, + 0x03, 0xbe, 0x4a, 0x16, 0xd3, 0x8c, 0x88, 0xc5, 0xd4, 0x75, 0x51, 0x20, 0x5e, 0xbd, + 0xc5, 0xfc, 0x41, 0x36, 0xc8, 0xe4, 0x80, 0x60, 0x3e, 0x0f, 0xbb, 0x36, 0x76, 0x07, + 0x34, 0x3e, 0xac, 0x85, 0x0b, 0x0b, 0x0f, 0x0e, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, + 0xa5, 0x0c, 0xe1, 0xc0, 0xce, 0xf0, 0x3c, 0xcf, 0x89, 0xe7, 0xb5, 0xb1, 0x6b, 0x0d, + 0x79, 0x78, 0xf5, 0xc2, 0xb1, 0xed, 0xcf, 0x77, 0x4d, 0x87, 0x70, 0x2e, 0x81, 0x54, + 0xd8, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x4f, 0x57, 0x75, 0xd7, 0x96, 0x50, 0x3e, + 0x96, 0x13, 0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, 0x00, 0x56, 0xac, 0x8d, 0xed, 0x70, + 0x14, 0x0b, 0x08, 0x1b, 0x09, 0x44, 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7a, 0xce, 0x06, 0x57, 0x43, 0xb2, 0x7d, 0x7e, 0xf9, 0x2d, 0x74, 0xf6, + 0x68, 0x1e, 0x6b, 0x61, 0x34, 0x08, 0xfc, 0xe1, 0x9b, 0xcf, 0x73, 0xda, 0x46, 0xab, + 0xaa, 0xe8, 0xb3, 0x99, 0xd9, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x6a, 0x09, 0x0d, + 0xd1, 0x55, 0xb5, 0x8c, 0x29, 0xd8, 0x73, 0x2d, 0x82, 0xf2, 0x76, 0x0b, 0xad, 0x26, + 0x5e, 0xa5, 0x4b, 0x95, 0x18, 0x4a, 0xf5, 0x6d, 0xa5, 0xd4, 0xe1, 0x34, 0x78, 0x11, + 0xcc, 0x78, 0x7c, 0x40, 0xe2, 0xa0, 0xb1, 0x5a, 0x79, 0x88, 0x1b, 0x4f, 0xa1, 0xf4, + 0x32, 0x5c, 0x74, 0x16, 0xf6, 0x9c, 0xfe, 0x56, 0xd5, 0x47, 0x58, 0x93, 0x7a, 0x70, + 0x49, 0xf5, 0xa6, 0x9b, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x05, 0x00, 0x62, 0x0e, + 0x00, 0x00, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, 0x38, 0x6a, 0x43, 0x43, 0x42, 0x4a, 0x69, 0x67, + 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x4a, 0x66, 0x43, 0x6a, 0x2b, 0x35, + 0x73, 0x48, 0x74, 0x48, 0x47, 0x68, 0x55, 0x78, 0x45, 0x36, 0x45, 0x4d, 0x71, 0x79, + 0x6e, 0x34, 0x73, 0x43, 0x6c, 0x4a, 0x77, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, + 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, 0x63, 0x44, 0x45, + 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x5a, 0x53, + 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, + 0x44, 0x53, 0x79, 0x42, 0x51, 0x62, 0x47, 0x46, 0x30, 0x5a, 0x6d, 0x39, 0x79, 0x62, + 0x53, 0x42, 0x44, 0x51, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x43, 0x67, 0x77, 0x52, 0x0a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, + 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, 0x70, 0x62, 0x32, + 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x63, 0x4d, + 0x43, 0x31, 0x4e, 0x68, 0x62, 0x6e, 0x52, 0x68, 0x49, 0x45, 0x4e, 0x73, 0x59, 0x58, + 0x4a, 0x68, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x49, + 0x0a, 0x44, 0x41, 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, + 0x31, 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, 0x48, 0x68, 0x63, + 0x4e, 0x4d, 0x6a, 0x4d, 0x77, 0x4e, 0x44, 0x45, 0x7a, 0x4d, 0x54, 0x63, 0x7a, 0x4e, + 0x7a, 0x41, 0x33, 0x57, 0x68, 0x63, 0x4e, 0x4d, 0x7a, 0x41, 0x77, 0x4e, 0x44, 0x45, + 0x7a, 0x4d, 0x54, 0x63, 0x7a, 0x4e, 0x7a, 0x41, 0x33, 0x0a, 0x57, 0x6a, 0x42, 0x77, + 0x4d, 0x53, 0x49, 0x77, 0x49, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x42, + 0x6c, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, 0x43, 0x42, 0x54, 0x52, 0x31, 0x67, 0x67, + 0x55, 0x45, 0x4e, 0x4c, 0x49, 0x45, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, + 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x4b, 0x0a, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, + 0x43, 0x42, 0x44, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, + 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, + 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, 0x32, 0x78, + 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x0a, + 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x42, 0x5a, + 0x4d, 0x42, 0x4d, 0x47, 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, + 0x45, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, 0x48, + 0x41, 0x30, 0x49, 0x41, 0x42, 0x47, 0x5a, 0x4c, 0x0a, 0x48, 0x38, 0x70, 0x47, 0x4f, + 0x5a, 0x69, 0x7a, 0x38, 0x6a, 0x4e, 0x34, 0x37, 0x4e, 0x2b, 0x50, 0x59, 0x6b, 0x58, + 0x65, 0x42, 0x31, 0x57, 0x79, 0x6e, 0x53, 0x4a, 0x34, 0x48, 0x2f, 0x53, 0x49, 0x61, + 0x30, 0x30, 0x47, 0x6a, 0x57, 0x5a, 0x78, 0x4b, 0x55, 0x62, 0x44, 0x67, 0x32, 0x58, + 0x4d, 0x78, 0x47, 0x45, 0x64, 0x4d, 0x74, 0x4c, 0x47, 0x7a, 0x79, 0x79, 0x4c, 0x50, + 0x35, 0x63, 0x70, 0x0a, 0x7a, 0x30, 0x62, 0x4d, 0x32, 0x6f, 0x58, 0x32, 0x57, 0x2b, + 0x6a, 0x4d, 0x51, 0x39, 0x5a, 0x2f, 0x4a, 0x36, 0x79, 0x6a, 0x67, 0x67, 0x4d, 0x4f, + 0x4d, 0x49, 0x49, 0x44, 0x43, 0x6a, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, + 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x53, 0x56, 0x62, 0x31, 0x33, 0x4e, + 0x76, 0x52, 0x76, 0x68, 0x36, 0x55, 0x42, 0x4a, 0x79, 0x64, 0x54, 0x30, 0x0a, 0x4d, + 0x38, 0x34, 0x42, 0x56, 0x77, 0x76, 0x65, 0x56, 0x44, 0x42, 0x72, 0x42, 0x67, 0x4e, + 0x56, 0x48, 0x52, 0x38, 0x45, 0x5a, 0x44, 0x42, 0x69, 0x4d, 0x47, 0x43, 0x67, 0x58, + 0x71, 0x42, 0x63, 0x68, 0x6c, 0x70, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, + 0x76, 0x4c, 0x32, 0x46, 0x77, 0x61, 0x53, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, + 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x0a, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, + 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, + 0x4c, 0x33, 0x4e, 0x6e, 0x65, 0x43, 0x39, 0x6a, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, + 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4c, 0x33, 0x59, 0x7a, + 0x4c, 0x33, 0x42, 0x6a, 0x61, 0x32, 0x4e, 0x79, 0x62, 0x44, 0x39, 0x6a, 0x59, 0x54, + 0x31, 0x77, 0x0a, 0x62, 0x47, 0x46, 0x30, 0x5a, 0x6d, 0x39, 0x79, 0x62, 0x53, 0x5a, + 0x6c, 0x62, 0x6d, 0x4e, 0x76, 0x5a, 0x47, 0x6c, 0x75, 0x5a, 0x7a, 0x31, 0x6b, 0x5a, + 0x58, 0x49, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, + 0x45, 0x46, 0x4b, 0x47, 0x44, 0x38, 0x50, 0x75, 0x50, 0x42, 0x63, 0x63, 0x54, 0x43, + 0x64, 0x76, 0x6e, 0x41, 0x75, 0x58, 0x46, 0x49, 0x56, 0x66, 0x36, 0x0a, 0x76, 0x49, + 0x6b, 0x48, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, 0x45, 0x42, + 0x2f, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49, 0x47, 0x77, 0x44, 0x41, 0x4d, 0x42, 0x67, + 0x4e, 0x56, 0x48, 0x52, 0x4d, 0x42, 0x41, 0x66, 0x38, 0x45, 0x41, 0x6a, 0x41, 0x41, + 0x4d, 0x49, 0x49, 0x43, 0x4f, 0x77, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x0a, 0x42, 0x49, 0x49, 0x43, 0x4c, 0x44, 0x43, + 0x43, 0x41, 0x69, 0x67, 0x77, 0x48, 0x67, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, + 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x51, 0x51, 0x51, 0x55, 0x59, 0x50, + 0x38, 0x65, 0x34, 0x73, 0x4c, 0x7a, 0x66, 0x73, 0x45, 0x34, 0x54, 0x64, 0x62, 0x6f, + 0x53, 0x79, 0x44, 0x52, 0x54, 0x43, 0x43, 0x41, 0x57, 0x55, 0x47, 0x43, 0x69, 0x71, + 0x47, 0x0a, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x77, + 0x67, 0x67, 0x46, 0x56, 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x42, 0x41, 0x67, 0x45, 0x4c, + 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, + 0x45, 0x4e, 0x41, 0x51, 0x49, 0x43, 0x41, 0x67, 0x45, 0x4c, 0x0a, 0x4d, 0x42, 0x41, + 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, + 0x51, 0x49, 0x44, 0x41, 0x67, 0x45, 0x44, 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, + 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x45, 0x41, + 0x67, 0x45, 0x44, 0x4d, 0x42, 0x45, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, + 0x34, 0x54, 0x51, 0x45, 0x4e, 0x0a, 0x41, 0x51, 0x49, 0x46, 0x41, 0x67, 0x49, 0x41, + 0x2f, 0x7a, 0x41, 0x52, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x42, 0x67, 0x49, 0x43, 0x41, 0x50, 0x38, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x63, 0x43, 0x41, 0x51, 0x45, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x67, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6b, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6f, 0x43, 0x0a, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x0a, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, + 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, + 0x30, 0x77, 0x48, 0x77, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0a, 0x45, 0x41, 0x73, 0x4c, 0x41, + 0x77, 0x50, 0x2f, 0x2f, 0x77, 0x45, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, + 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, + 0x77, 0x46, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x0a, 0x42, 0x41, 0x51, 0x47, 0x41, 0x47, 0x42, 0x71, 0x41, 0x41, + 0x41, 0x41, 0x4d, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, + 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x55, 0x4b, 0x41, 0x51, 0x45, 0x77, 0x48, 0x67, + 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, + 0x42, 0x67, 0x51, 0x51, 0x55, 0x48, 0x50, 0x72, 0x61, 0x78, 0x7a, 0x61, 0x0a, 0x57, + 0x6e, 0x42, 0x4d, 0x69, 0x67, 0x2f, 0x42, 0x49, 0x75, 0x44, 0x66, 0x75, 0x6a, 0x42, + 0x45, 0x42, 0x67, 0x6f, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x48, 0x4d, 0x44, 0x59, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x45, 0x42, 0x41, + 0x66, 0x38, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x49, 0x42, 0x41, 0x66, 0x38, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x42, 0x77, 0x4d, 0x42, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x41, + 0x41, 0x77, 0x0a, 0x52, 0x51, 0x49, 0x67, 0x52, 0x35, 0x4e, 0x4e, 0x76, 0x6d, 0x77, + 0x73, 0x50, 0x61, 0x77, 0x78, 0x48, 0x4b, 0x61, 0x76, 0x5a, 0x74, 0x33, 0x59, 0x79, + 0x6a, 0x69, 0x58, 0x4e, 0x76, 0x55, 0x7a, 0x79, 0x30, 0x58, 0x31, 0x4a, 0x70, 0x6d, + 0x45, 0x66, 0x77, 0x2b, 0x4c, 0x4a, 0x58, 0x30, 0x43, 0x49, 0x51, 0x44, 0x43, 0x67, + 0x74, 0x76, 0x2f, 0x74, 0x78, 0x42, 0x67, 0x74, 0x6c, 0x54, 0x6b, 0x0a, 0x70, 0x6e, + 0x45, 0x47, 0x73, 0x6c, 0x51, 0x6a, 0x51, 0x6c, 0x39, 0x51, 0x30, 0x46, 0x62, 0x35, + 0x79, 0x73, 0x2f, 0x73, 0x42, 0x51, 0x4e, 0x42, 0x31, 0x75, 0x65, 0x68, 0x75, 0x67, + 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6c, 0x6a, 0x43, 0x43, 0x41, 0x6a, 0x32, 0x67, 0x41, + 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4a, 0x56, 0x76, 0x58, 0x63, 0x32, + 0x39, 0x47, 0x2b, 0x48, 0x70, 0x51, 0x45, 0x6e, 0x4a, 0x31, 0x50, 0x51, 0x7a, 0x7a, + 0x67, 0x46, 0x58, 0x43, 0x39, 0x35, 0x55, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, + 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, 0x4d, 0x47, 0x67, 0x78, + 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x45, 0x55, + 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x53, + 0x62, 0x32, 0x39, 0x30, 0x49, 0x45, 0x4e, 0x42, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, + 0x62, 0x43, 0x42, 0x44, 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, + 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, + 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, + 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x0a, + 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, + 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, + 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, 0x30, 0x7a, 0x4d, 0x7a, + 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, + 0x4d, 0x48, 0x41, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, + 0x41, 0x4d, 0x4d, 0x47, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, + 0x48, 0x57, 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, + 0x47, 0x5a, 0x76, 0x63, 0x6d, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, + 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, + 0x47, 0x56, 0x73, 0x0a, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, + 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, + 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x0a, 0x43, + 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, + 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, + 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x4e, 0x53, 0x42, 0x2f, 0x37, + 0x74, 0x32, 0x31, 0x6c, 0x58, 0x53, 0x4f, 0x0a, 0x32, 0x43, 0x75, 0x7a, 0x70, 0x78, + 0x77, 0x37, 0x34, 0x65, 0x4a, 0x42, 0x37, 0x32, 0x45, 0x79, 0x44, 0x47, 0x67, 0x57, + 0x35, 0x72, 0x58, 0x43, 0x74, 0x78, 0x32, 0x74, 0x56, 0x54, 0x4c, 0x71, 0x36, 0x68, + 0x4b, 0x6b, 0x36, 0x7a, 0x2b, 0x55, 0x69, 0x52, 0x5a, 0x43, 0x6e, 0x71, 0x52, 0x37, + 0x70, 0x73, 0x4f, 0x76, 0x67, 0x71, 0x46, 0x65, 0x53, 0x78, 0x6c, 0x6d, 0x54, 0x6c, + 0x4a, 0x6c, 0x0a, 0x65, 0x54, 0x6d, 0x69, 0x32, 0x57, 0x59, 0x7a, 0x33, 0x71, 0x4f, + 0x42, 0x75, 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, + 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, + 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, + 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x0a, 0x42, 0x67, + 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, 0x4a, 0x4d, 0x45, 0x65, 0x67, + 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, + 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, + 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, + 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x0a, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, 0x4d, + 0x75, 0x61, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, + 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, + 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, + 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x6c, 0x57, 0x39, + 0x64, 0x0a, 0x7a, 0x62, 0x30, 0x62, 0x34, 0x65, 0x6c, 0x41, 0x53, 0x63, 0x6e, 0x55, + 0x39, 0x44, 0x50, 0x4f, 0x41, 0x56, 0x63, 0x4c, 0x33, 0x6c, 0x51, 0x77, 0x44, 0x67, + 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, + 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, + 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x0a, 0x41, 0x66, 0x38, + 0x43, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, + 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x52, 0x77, 0x41, 0x77, 0x52, 0x41, 0x49, + 0x67, 0x58, 0x73, 0x56, 0x6b, 0x69, 0x30, 0x77, 0x2b, 0x69, 0x36, 0x56, 0x59, 0x47, + 0x57, 0x33, 0x55, 0x46, 0x2f, 0x32, 0x32, 0x75, 0x61, 0x58, 0x65, 0x30, 0x59, 0x4a, + 0x44, 0x6a, 0x31, 0x55, 0x65, 0x0a, 0x6e, 0x41, 0x2b, 0x54, 0x6a, 0x44, 0x31, 0x61, + 0x69, 0x35, 0x63, 0x43, 0x49, 0x43, 0x59, 0x62, 0x31, 0x53, 0x41, 0x6d, 0x44, 0x35, + 0x78, 0x6b, 0x66, 0x54, 0x56, 0x70, 0x76, 0x6f, 0x34, 0x55, 0x6f, 0x79, 0x69, 0x53, + 0x59, 0x78, 0x72, 0x44, 0x57, 0x4c, 0x6d, 0x55, 0x52, 0x34, 0x43, 0x49, 0x39, 0x4e, + 0x4b, 0x79, 0x66, 0x50, 0x4e, 0x2b, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, + 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, + 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6a, 0x7a, 0x43, 0x43, 0x41, + 0x6a, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, + 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, + 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, + 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, + 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, + 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, + 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, + 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, + 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x0a, 0x63, 0x6e, 0x42, 0x76, 0x63, + 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, + 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, + 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, + 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, + 0x7a, 0x41, 0x4a, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, + 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4d, 0x44, 0x55, 0x79, + 0x4d, 0x54, 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, 0x4d, 0x46, 0x6f, 0x58, 0x44, 0x54, + 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, 0x49, 0x7a, 0x4e, 0x54, 0x6b, 0x31, + 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x0a, 0x41, + 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, + 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, + 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, + 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, + 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, + 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, + 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, + 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, + 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, + 0x59, 0x54, 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, + 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, + 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, + 0x45, 0x43, 0x36, 0x6e, 0x45, 0x77, 0x4d, 0x44, 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, + 0x69, 0x50, 0x57, 0x73, 0x43, 0x7a, 0x61, 0x45, 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, + 0x69, 0x4f, 0x53, 0x4c, 0x52, 0x46, 0x68, 0x57, 0x47, 0x6a, 0x62, 0x6e, 0x42, 0x56, + 0x4a, 0x66, 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, 0x33, 0x49, 0x6a, 0x6b, 0x44, 0x59, + 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, 0x71, 0x73, 0x79, 0x59, 0x6a, 0x6c, + 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, 0x78, 0x46, 0x50, 0x32, 0x73, 0x4a, 0x42, 0x4b, + 0x35, 0x7a, 0x6c, 0x4b, 0x4f, 0x42, 0x0a, 0x75, 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, + 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, + 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, + 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, + 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, + 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, + 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, + 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, + 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x64, 0x6d, + 0x6c, 0x6a, 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x0a, 0x5a, 0x57, 0x77, + 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, + 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, + 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, + 0x67, 0x51, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, + 0x7a, 0x67, 0x37, 0x53, 0x56, 0x0a, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, + 0x42, 0x71, 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, + 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, + 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, + 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, + 0x51, 0x41, 0x77, 0x52, 0x67, 0x49, 0x68, 0x41, 0x4f, 0x57, 0x2f, 0x35, 0x51, 0x6b, + 0x52, 0x2b, 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4e, 0x6f, 0x6f, 0x77, 0x4c, + 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, 0x2f, 0x59, 0x69, 0x37, 0x47, 0x53, + 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, 0x67, 0x0a, 0x41, 0x69, 0x45, 0x41, + 0x34, 0x4a, 0x30, 0x6c, 0x72, 0x48, 0x6f, 0x4d, 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, + 0x2f, 0x73, 0x58, 0x36, 0x4f, 0x39, 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, + 0x55, 0x47, 0x4f, 0x64, 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, + 0x49, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x00, + ]; + + check_quote("e, &[0, 96, 106, 0, 0, 0]).unwrap(); + } + + #[test] + // alternative quote verification cannot cope with old collateral data format + fn out_of_date() { + let quote = [ + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a, + 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07, + 0x46, 0x72, 0x07, 0x77, 0x7b, 0x2a, 0x56, 0xdf, 0xa2, 0xb1, 0x0e, 0xdd, 0x20, 0x16, + 0x0d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb1, 0xab, 0x35, 0x6e, 0xa0, 0x6e, 0x53, 0x25, 0xae, 0x16, 0x7d, 0x29, 0x0b, 0x3a, + 0x66, 0xb6, 0x1b, 0x89, 0x79, 0xd1, 0x2e, 0x6b, 0xbd, 0x5a, 0x97, 0xf3, 0x85, 0x72, + 0xc0, 0x32, 0x38, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x80, 0x37, 0xd8, 0x87, 0x82, + 0xe0, 0x22, 0xe0, 0x19, 0xb3, 0x02, 0x07, 0x45, 0xb7, 0x8a, 0xa4, 0x0e, 0xd9, 0x5c, + 0x77, 0xda, 0x4b, 0xf7, 0xf3, 0x25, 0x3d, 0x3a, 0x44, 0xc4, 0xfd, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x44, 0x10, + 0x00, 0x00, 0xb6, 0xdc, 0x93, 0xed, 0xc9, 0x25, 0x9d, 0x21, 0x6b, 0x17, 0xe4, 0xfa, + 0x00, 0x68, 0x21, 0x80, 0x5a, 0x8e, 0xbd, 0x81, 0x9a, 0x39, 0xc5, 0xb0, 0xb1, 0xbd, + 0x0b, 0xfc, 0x4b, 0x4b, 0xe3, 0x5c, 0x7a, 0x44, 0x46, 0xe1, 0x5a, 0xc6, 0xb1, 0x2e, + 0x8c, 0xee, 0x58, 0xce, 0x09, 0x3c, 0xed, 0xe6, 0xe5, 0xdc, 0x2c, 0xbe, 0xef, 0xbe, + 0xee, 0x21, 0x96, 0xd6, 0xa3, 0x6e, 0x4f, 0x7c, 0xc8, 0x21, 0xdc, 0x8c, 0x78, 0xda, + 0x8c, 0x50, 0xe0, 0xb3, 0x86, 0xa1, 0x4d, 0x55, 0x96, 0x93, 0xb2, 0xc7, 0xf7, 0x97, + 0xe9, 0x75, 0x5f, 0xc7, 0x22, 0xdd, 0x23, 0xe7, 0xab, 0x8c, 0x97, 0xe9, 0xe9, 0x1d, + 0xa5, 0x1a, 0x02, 0xe8, 0x23, 0xf7, 0xbe, 0x88, 0xa0, 0x8b, 0x1a, 0x6b, 0xdc, 0x58, + 0x44, 0xa0, 0xaf, 0x68, 0xdc, 0x16, 0xb0, 0x8c, 0xe5, 0x3a, 0x53, 0xb2, 0xba, 0xf1, + 0x36, 0xd1, 0x4f, 0x7d, 0x0b, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, + 0xa5, 0x0c, 0xe1, 0xc0, 0xce, 0xf0, 0x3c, 0xcf, 0x89, 0xe7, 0xb5, 0xb1, 0x6b, 0x0d, + 0x79, 0x78, 0xf5, 0xc2, 0xb1, 0xed, 0xcf, 0x77, 0x4d, 0x87, 0x70, 0x2e, 0x81, 0x54, + 0xd8, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x4f, 0x57, 0x75, 0xd7, 0x96, 0x50, 0x3e, + 0x96, 0x13, 0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, 0x00, 0x56, 0xac, 0x8d, 0xed, 0x70, + 0x14, 0x0b, 0x08, 0x1b, 0x09, 0x44, 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4b, 0xba, 0x65, 0x76, 0x56, 0xb9, 0xb1, 0x19, 0x79, 0xbd, 0x5c, 0x8e, + 0xf7, 0x68, 0x82, 0xf7, 0x54, 0x82, 0x12, 0xf6, 0x85, 0xa2, 0x37, 0x65, 0x04, 0x3f, + 0x46, 0x54, 0x3e, 0x64, 0x18, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xd6, 0xbf, 0x3d, + 0x17, 0x99, 0x41, 0xbb, 0xf2, 0xd1, 0x94, 0xd7, 0xce, 0x30, 0xa0, 0xaa, 0xf9, 0x4e, + 0x8f, 0xe3, 0x46, 0xb6, 0xd8, 0x07, 0x7e, 0x6a, 0x06, 0xcb, 0x80, 0x15, 0x3f, 0xd1, + 0x00, 0xcc, 0x6c, 0x46, 0xd2, 0xd0, 0x2c, 0x02, 0x8d, 0xd9, 0x2c, 0x7d, 0x95, 0xff, + 0x13, 0x10, 0xed, 0xf8, 0x69, 0x00, 0xc8, 0x9c, 0x67, 0x68, 0x59, 0xbe, 0xc6, 0xfa, + 0x1e, 0x79, 0x74, 0x50, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x05, 0x00, 0xdc, 0x0d, + 0x00, 0x00, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, 0x6a, 0x44, 0x43, 0x43, 0x42, 0x44, 0x4b, 0x67, + 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x57, 0x74, 0x46, 0x4d, 0x63, 0x4b, + 0x63, 0x64, 0x75, 0x30, 0x43, 0x37, 0x62, 0x39, 0x30, 0x65, 0x38, 0x32, 0x67, 0x46, + 0x2f, 0x4d, 0x36, 0x63, 0x56, 0x2b, 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, + 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, 0x63, 0x54, 0x45, + 0x6a, 0x4d, 0x43, 0x45, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x61, 0x53, + 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, + 0x44, 0x53, 0x79, 0x42, 0x51, 0x63, 0x6d, 0x39, 0x6a, 0x5a, 0x58, 0x4e, 0x7a, 0x62, + 0x33, 0x49, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, + 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x0a, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, + 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, + 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, + 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, + 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x0a, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x42, 0x34, + 0x58, 0x44, 0x54, 0x49, 0x7a, 0x4d, 0x44, 0x55, 0x77, 0x4f, 0x54, 0x45, 0x79, 0x4e, + 0x44, 0x59, 0x78, 0x4e, 0x6c, 0x6f, 0x58, 0x44, 0x54, 0x4d, 0x77, 0x4d, 0x44, 0x55, + 0x77, 0x4f, 0x54, 0x45, 0x79, 0x4e, 0x44, 0x59, 0x78, 0x0a, 0x4e, 0x6c, 0x6f, 0x77, + 0x63, 0x44, 0x45, 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, + 0x77, 0x5a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, + 0x49, 0x46, 0x42, 0x44, 0x53, 0x79, 0x42, 0x44, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, + 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, 0x5a, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x0a, 0x43, 0x67, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, + 0x57, 0x77, 0x67, 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, + 0x70, 0x62, 0x32, 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x42, + 0x41, 0x63, 0x4d, 0x43, 0x31, 0x4e, 0x68, 0x62, 0x6e, 0x52, 0x68, 0x49, 0x45, 0x4e, + 0x73, 0x59, 0x58, 0x4a, 0x68, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x0a, + 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, + 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, + 0x57, 0x54, 0x41, 0x54, 0x42, 0x67, 0x63, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, + 0x49, 0x42, 0x42, 0x67, 0x67, 0x71, 0x68, 0x6b, 0x6a, 0x4f, 0x50, 0x51, 0x4d, 0x42, + 0x42, 0x77, 0x4e, 0x43, 0x41, 0x41, 0x51, 0x57, 0x0a, 0x49, 0x41, 0x6e, 0x45, 0x63, + 0x72, 0x51, 0x43, 0x78, 0x51, 0x48, 0x41, 0x77, 0x6d, 0x66, 0x43, 0x2f, 0x6d, 0x53, + 0x5a, 0x37, 0x33, 0x59, 0x4d, 0x31, 0x39, 0x45, 0x76, 0x43, 0x30, 0x2b, 0x62, 0x75, + 0x51, 0x6c, 0x35, 0x6e, 0x65, 0x6a, 0x65, 0x64, 0x77, 0x36, 0x6c, 0x6d, 0x62, 0x5a, + 0x55, 0x31, 0x4e, 0x45, 0x39, 0x5a, 0x55, 0x52, 0x48, 0x4d, 0x31, 0x57, 0x44, 0x44, + 0x4b, 0x72, 0x4e, 0x0a, 0x6d, 0x62, 0x35, 0x53, 0x7a, 0x6c, 0x79, 0x33, 0x46, 0x66, + 0x57, 0x79, 0x4f, 0x4a, 0x59, 0x67, 0x4f, 0x6b, 0x4e, 0x49, 0x6f, 0x34, 0x49, 0x43, + 0x70, 0x7a, 0x43, 0x43, 0x41, 0x71, 0x4d, 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, + 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6f, 0x41, 0x55, 0x30, 0x4f, 0x69, 0x71, + 0x32, 0x6e, 0x58, 0x58, 0x2b, 0x53, 0x35, 0x4a, 0x46, 0x35, 0x67, 0x38, 0x0a, 0x65, + 0x78, 0x52, 0x6c, 0x30, 0x4e, 0x58, 0x79, 0x57, 0x55, 0x30, 0x77, 0x62, 0x41, 0x59, + 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x47, 0x55, 0x77, 0x59, 0x7a, 0x42, 0x68, 0x6f, + 0x46, 0x2b, 0x67, 0x58, 0x59, 0x5a, 0x62, 0x61, 0x48, 0x52, 0x30, 0x63, 0x48, 0x4d, + 0x36, 0x4c, 0x79, 0x39, 0x68, 0x63, 0x47, 0x6b, 0x75, 0x64, 0x48, 0x4a, 0x31, 0x63, + 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, 0x6c, 0x0a, 0x63, 0x6e, 0x5a, 0x70, 0x59, 0x32, + 0x56, 0x7a, 0x4c, 0x6d, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4c, 0x6d, 0x4e, 0x76, + 0x62, 0x53, 0x39, 0x7a, 0x5a, 0x33, 0x67, 0x76, 0x59, 0x32, 0x56, 0x79, 0x64, 0x47, + 0x6c, 0x6d, 0x61, 0x57, 0x4e, 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x69, 0x39, 0x32, + 0x4d, 0x79, 0x39, 0x77, 0x59, 0x32, 0x74, 0x6a, 0x63, 0x6d, 0x77, 0x2f, 0x59, 0x32, + 0x45, 0x39, 0x0a, 0x63, 0x48, 0x4a, 0x76, 0x59, 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, + 0x79, 0x4a, 0x6d, 0x56, 0x75, 0x59, 0x32, 0x39, 0x6b, 0x61, 0x57, 0x35, 0x6e, 0x50, + 0x57, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, + 0x45, 0x46, 0x67, 0x51, 0x55, 0x77, 0x48, 0x49, 0x6e, 0x51, 0x41, 0x52, 0x6c, 0x67, + 0x30, 0x44, 0x4a, 0x51, 0x6c, 0x45, 0x46, 0x4b, 0x4d, 0x4e, 0x5a, 0x0a, 0x56, 0x53, + 0x4b, 0x62, 0x59, 0x42, 0x51, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, + 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x62, 0x41, 0x4d, 0x41, + 0x77, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x43, + 0x4d, 0x41, 0x41, 0x77, 0x67, 0x67, 0x48, 0x54, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, + 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x0a, 0x44, 0x51, 0x45, 0x45, 0x67, 0x67, 0x48, + 0x45, 0x4d, 0x49, 0x49, 0x42, 0x77, 0x44, 0x41, 0x65, 0x42, 0x67, 0x6f, 0x71, 0x68, + 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x42, 0x42, 0x42, 0x42, + 0x5a, 0x4c, 0x56, 0x4a, 0x69, 0x69, 0x5a, 0x4b, 0x39, 0x78, 0x4a, 0x33, 0x30, 0x41, + 0x33, 0x37, 0x6b, 0x72, 0x6b, 0x4a, 0x6b, 0x4d, 0x49, 0x49, 0x42, 0x59, 0x77, 0x59, + 0x4b, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, + 0x41, 0x6a, 0x43, 0x43, 0x41, 0x56, 0x4d, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, + 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x45, 0x43, + 0x41, 0x51, 0x6f, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x49, 0x43, 0x0a, 0x41, 0x51, 0x6f, + 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x41, 0x67, 0x4d, 0x43, 0x41, 0x51, 0x49, 0x77, 0x45, 0x41, 0x59, + 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x51, 0x43, 0x41, 0x51, 0x49, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x55, 0x43, + 0x41, 0x51, 0x49, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x59, 0x43, 0x41, 0x51, 0x45, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x63, 0x43, 0x41, 0x51, 0x51, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x67, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6b, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6f, 0x43, 0x0a, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x0a, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, + 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, + 0x73, 0x77, 0x48, 0x77, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0a, 0x45, 0x41, 0x6f, 0x4b, 0x41, + 0x67, 0x49, 0x43, 0x41, 0x51, 0x51, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, + 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, + 0x77, 0x46, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x0a, 0x42, 0x41, 0x51, 0x47, 0x41, 0x48, 0x42, 0x75, 0x52, 0x77, + 0x41, 0x41, 0x4d, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, + 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x55, 0x4b, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, + 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, + 0x53, 0x41, 0x41, 0x77, 0x52, 0x51, 0x49, 0x67, 0x4b, 0x64, 0x36, 0x2b, 0x0a, 0x58, + 0x62, 0x4e, 0x42, 0x64, 0x66, 0x49, 0x73, 0x4f, 0x30, 0x4c, 0x56, 0x48, 0x45, 0x49, + 0x50, 0x75, 0x39, 0x49, 0x66, 0x4a, 0x52, 0x7a, 0x39, 0x70, 0x4f, 0x58, 0x73, 0x4f, + 0x74, 0x68, 0x78, 0x56, 0x4d, 0x59, 0x72, 0x58, 0x48, 0x49, 0x43, 0x49, 0x51, 0x44, + 0x5a, 0x71, 0x79, 0x6c, 0x61, 0x6f, 0x52, 0x36, 0x77, 0x5a, 0x76, 0x54, 0x48, 0x49, + 0x33, 0x51, 0x55, 0x59, 0x55, 0x7a, 0x6a, 0x0a, 0x77, 0x67, 0x62, 0x6b, 0x65, 0x35, + 0x77, 0x52, 0x48, 0x4c, 0x74, 0x6c, 0x31, 0x39, 0x34, 0x79, 0x53, 0x5a, 0x75, 0x4a, + 0x55, 0x41, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, + 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, + 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6d, 0x44, 0x43, 0x43, 0x41, 0x6a, 0x36, + 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4e, 0x44, 0x6f, 0x71, + 0x74, 0x70, 0x31, 0x31, 0x2f, 0x6b, 0x75, 0x53, 0x52, 0x65, 0x59, 0x50, 0x48, 0x73, + 0x55, 0x5a, 0x64, 0x44, 0x56, 0x38, 0x6c, 0x6c, 0x4e, 0x4d, 0x41, 0x6f, 0x47, 0x43, + 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, 0x4d, 0x47, + 0x67, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, + 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, + 0x42, 0x53, 0x62, 0x32, 0x39, 0x30, 0x49, 0x45, 0x4e, 0x42, 0x4d, 0x52, 0x6f, 0x77, + 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, + 0x52, 0x6c, 0x62, 0x43, 0x42, 0x44, 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, + 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, + 0x31, 0x55, 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, + 0x67, 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, + 0x77, 0x0a, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, + 0x55, 0x7a, 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, + 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, 0x30, 0x7a, + 0x4d, 0x7a, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, + 0x42, 0x61, 0x4d, 0x48, 0x45, 0x78, 0x49, 0x7a, 0x41, 0x68, 0x0a, 0x42, 0x67, 0x4e, + 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x47, 0x6b, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, + 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x48, 0x4a, + 0x76, 0x59, 0x32, 0x56, 0x7a, 0x63, 0x32, 0x39, 0x79, 0x49, 0x45, 0x4e, 0x42, 0x4d, + 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, + 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x0a, 0x62, 0x43, 0x42, 0x44, 0x62, 0x33, 0x4a, 0x77, + 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, + 0x49, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, + 0x64, 0x47, 0x45, 0x67, 0x51, 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, + 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, + 0x0a, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, + 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x42, 0x5a, 0x4d, 0x42, 0x4d, 0x47, 0x42, 0x79, 0x71, + 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, 0x45, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, + 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, 0x48, 0x41, 0x30, 0x49, 0x41, 0x42, 0x4c, 0x39, + 0x71, 0x2b, 0x4e, 0x4d, 0x70, 0x32, 0x49, 0x4f, 0x67, 0x0a, 0x74, 0x64, 0x6c, 0x31, + 0x62, 0x6b, 0x2f, 0x75, 0x57, 0x5a, 0x35, 0x2b, 0x54, 0x47, 0x51, 0x6d, 0x38, 0x61, + 0x43, 0x69, 0x38, 0x7a, 0x37, 0x38, 0x66, 0x73, 0x2b, 0x66, 0x4b, 0x43, 0x51, 0x33, + 0x64, 0x2b, 0x75, 0x44, 0x7a, 0x58, 0x6e, 0x56, 0x54, 0x41, 0x54, 0x32, 0x5a, 0x68, + 0x44, 0x43, 0x69, 0x66, 0x79, 0x49, 0x75, 0x4a, 0x77, 0x76, 0x4e, 0x33, 0x77, 0x4e, + 0x42, 0x70, 0x39, 0x69, 0x0a, 0x48, 0x42, 0x53, 0x53, 0x4d, 0x4a, 0x4d, 0x4a, 0x72, + 0x42, 0x4f, 0x6a, 0x67, 0x62, 0x73, 0x77, 0x67, 0x62, 0x67, 0x77, 0x48, 0x77, 0x59, + 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6f, 0x41, 0x55, 0x49, + 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, + 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x0a, + 0x55, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x45, 0x73, 0x77, 0x53, 0x54, + 0x42, 0x48, 0x6f, 0x45, 0x57, 0x67, 0x51, 0x34, 0x5a, 0x42, 0x61, 0x48, 0x52, 0x30, + 0x63, 0x48, 0x4d, 0x36, 0x4c, 0x79, 0x39, 0x6a, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, + 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, 0x5a, 0x58, 0x4d, 0x75, 0x64, 0x48, 0x4a, 0x31, + 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x48, 0x4e, 0x6c, 0x0a, 0x63, 0x6e, 0x5a, 0x70, 0x59, + 0x32, 0x56, 0x7a, 0x4c, 0x6d, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4c, 0x6d, 0x4e, + 0x76, 0x62, 0x53, 0x39, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, 0x46, 0x4e, 0x48, 0x57, + 0x46, 0x4a, 0x76, 0x62, 0x33, 0x52, 0x44, 0x51, 0x53, 0x35, 0x6b, 0x5a, 0x58, 0x49, + 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46, + 0x4e, 0x44, 0x6f, 0x0a, 0x71, 0x74, 0x70, 0x31, 0x31, 0x2f, 0x6b, 0x75, 0x53, 0x52, + 0x65, 0x59, 0x50, 0x48, 0x73, 0x55, 0x5a, 0x64, 0x44, 0x56, 0x38, 0x6c, 0x6c, 0x4e, + 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, 0x45, 0x42, 0x2f, 0x77, + 0x51, 0x45, 0x41, 0x77, 0x49, 0x42, 0x42, 0x6a, 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, + 0x48, 0x52, 0x4d, 0x42, 0x41, 0x66, 0x38, 0x45, 0x43, 0x44, 0x41, 0x47, 0x0a, 0x41, + 0x51, 0x48, 0x2f, 0x41, 0x67, 0x45, 0x41, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, + 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x41, 0x30, 0x67, 0x41, 0x4d, + 0x45, 0x55, 0x43, 0x49, 0x51, 0x43, 0x4a, 0x67, 0x54, 0x62, 0x74, 0x56, 0x71, 0x4f, + 0x79, 0x5a, 0x31, 0x6d, 0x33, 0x6a, 0x71, 0x69, 0x41, 0x58, 0x4d, 0x36, 0x51, 0x59, + 0x61, 0x36, 0x72, 0x35, 0x73, 0x57, 0x53, 0x0a, 0x34, 0x79, 0x2f, 0x47, 0x37, 0x79, + 0x38, 0x75, 0x49, 0x4a, 0x47, 0x78, 0x64, 0x77, 0x49, 0x67, 0x52, 0x71, 0x50, 0x76, + 0x42, 0x53, 0x4b, 0x7a, 0x7a, 0x51, 0x61, 0x67, 0x42, 0x4c, 0x51, 0x71, 0x35, 0x73, + 0x35, 0x41, 0x37, 0x30, 0x70, 0x64, 0x6f, 0x69, 0x61, 0x52, 0x4a, 0x38, 0x7a, 0x2f, + 0x30, 0x75, 0x44, 0x7a, 0x34, 0x4e, 0x67, 0x56, 0x39, 0x31, 0x6b, 0x3d, 0x0a, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, + 0x43, 0x6a, 0x7a, 0x43, 0x43, 0x41, 0x6a, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, + 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, + 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, + 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, + 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, + 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, + 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, + 0x0a, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, + 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, + 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, + 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, + 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x0a, 0x42, 0x67, 0x4e, 0x56, + 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, + 0x45, 0x34, 0x4d, 0x44, 0x55, 0x79, 0x4d, 0x54, 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, + 0x4d, 0x46, 0x6f, 0x58, 0x44, 0x54, 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, + 0x49, 0x7a, 0x4e, 0x54, 0x6b, 0x31, 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, 0x45, 0x61, + 0x4d, 0x42, 0x67, 0x47, 0x0a, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, + 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, + 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, + 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, + 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, + 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, + 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, + 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, + 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, + 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, + 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, + 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x43, 0x36, 0x6e, 0x45, 0x77, 0x4d, 0x44, + 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, 0x69, 0x50, 0x57, 0x73, 0x43, 0x7a, 0x61, 0x45, + 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, 0x69, 0x4f, 0x53, 0x4c, 0x52, 0x46, 0x68, 0x57, + 0x47, 0x6a, 0x62, 0x6e, 0x42, 0x56, 0x4a, 0x66, 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, + 0x33, 0x49, 0x6a, 0x6b, 0x44, 0x59, 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, + 0x71, 0x73, 0x79, 0x59, 0x6a, 0x6c, 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, 0x78, 0x46, + 0x50, 0x32, 0x73, 0x4a, 0x42, 0x4b, 0x35, 0x7a, 0x6c, 0x4b, 0x4f, 0x42, 0x0a, 0x75, + 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, 0x4d, + 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, 0x57, 0x57, + 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, + 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, + 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, + 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, + 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, 0x59, 0x58, + 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, 0x56, 0x6b, + 0x63, 0x32, 0x56, 0x79, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, + 0x35, 0x30, 0x0a, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, + 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, + 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, + 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, + 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x0a, 0x55, 0x72, + 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, + 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, + 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, + 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, + 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x51, 0x41, 0x77, 0x52, 0x67, 0x49, 0x68, 0x41, + 0x4f, 0x57, 0x2f, 0x35, 0x51, 0x6b, 0x52, 0x2b, 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, + 0x63, 0x4e, 0x6f, 0x6f, 0x77, 0x4c, 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, + 0x2f, 0x59, 0x69, 0x37, 0x47, 0x53, 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, + 0x67, 0x0a, 0x41, 0x69, 0x45, 0x41, 0x34, 0x4a, 0x30, 0x6c, 0x72, 0x48, 0x6f, 0x4d, + 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, 0x2f, 0x73, 0x58, 0x36, 0x4f, 0x39, 0x51, 0x57, + 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, 0x55, 0x47, 0x4f, 0x64, 0x52, 0x51, 0x37, 0x63, + 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, 0x49, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00, + ]; + + check_quote("e, &[0, 112, 110, 71, 0, 0]).unwrap(); + } + + #[test] + fn azure() { + let quote = [ + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a, + 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07, + 0x1a, 0x24, 0xf2, 0x9c, 0x17, 0x78, 0x4f, 0x6a, 0x1b, 0xac, 0xff, 0x9b, 0x40, 0xc2, + 0x5f, 0x58, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0b, 0x10, 0x0f, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0xd0, 0xe3, 0xeb, 0xfb, 0xb9, 0x8f, 0xd0, 0x61, 0xac, 0x2e, 0x9d, 0x21, 0x97, + 0x78, 0x0f, 0x81, 0x9c, 0x91, 0x72, 0xce, 0xef, 0xf4, 0xb3, 0x13, 0x0d, 0x72, 0x47, + 0xa6, 0x42, 0x1a, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x80, 0x37, 0xd8, 0x87, 0x82, + 0xe0, 0x22, 0xe0, 0x19, 0xb3, 0x02, 0x07, 0x45, 0xb7, 0x8a, 0xa4, 0x0e, 0xd9, 0x5c, + 0x77, 0xda, 0x4b, 0xf7, 0xf3, 0x25, 0x3d, 0x3a, 0x44, 0xc4, 0xfd, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xca, 0x10, + 0x00, 0x00, 0x4d, 0xce, 0x39, 0x7e, 0x12, 0x4f, 0xd5, 0x09, 0xb6, 0x2b, 0x47, 0x97, + 0x79, 0xa8, 0x78, 0xae, 0xfe, 0x3a, 0x45, 0x42, 0x98, 0x74, 0x36, 0x06, 0xd6, 0x9e, + 0xf4, 0xce, 0x7c, 0x17, 0x67, 0xeb, 0x5a, 0xdf, 0x43, 0x3b, 0x11, 0x13, 0x40, 0x8a, + 0xa6, 0xaf, 0xb2, 0xa5, 0xd1, 0xa4, 0xd9, 0xaa, 0xa3, 0xd2, 0x40, 0x88, 0xc5, 0x50, + 0xa1, 0x4b, 0x04, 0xfc, 0x52, 0x05, 0x10, 0x2c, 0x97, 0xe7, 0x36, 0x40, 0xd7, 0x3d, + 0x6b, 0x83, 0xf0, 0xc4, 0xe1, 0x34, 0x2b, 0x4b, 0x36, 0x24, 0xba, 0x11, 0xf3, 0x51, + 0x92, 0x24, 0x59, 0x50, 0x5e, 0x54, 0xc0, 0x51, 0x59, 0xc9, 0x5d, 0x3a, 0xae, 0x00, + 0xa2, 0x3e, 0x46, 0xe0, 0x34, 0xfd, 0xee, 0x89, 0xb3, 0x85, 0xb7, 0x9f, 0xe2, 0x12, + 0xa5, 0x74, 0xea, 0x00, 0xbd, 0x8f, 0xae, 0xe2, 0xbb, 0x0c, 0x73, 0x22, 0x71, 0xc7, + 0x15, 0xad, 0x82, 0xea, 0x0b, 0x0b, 0x10, 0x0f, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, + 0xa5, 0x0c, 0xe1, 0xc0, 0xce, 0xf0, 0x3c, 0xcf, 0x89, 0xe7, 0xb5, 0xb1, 0x6b, 0x0d, + 0x79, 0x78, 0xf5, 0xc2, 0xb1, 0xed, 0xcf, 0x77, 0x4d, 0x87, 0x70, 0x2e, 0x81, 0x54, + 0xd8, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x4f, 0x57, 0x75, 0xd7, 0x96, 0x50, 0x3e, + 0x96, 0x13, 0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, 0x00, 0x56, 0xac, 0x8d, 0xed, 0x70, + 0x14, 0x0b, 0x08, 0x1b, 0x09, 0x44, 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x71, 0x4e, 0x58, 0xe9, 0x48, 0x14, 0x59, 0x70, 0x69, 0x71, 0x4b, 0x5e, + 0x68, 0x78, 0xfb, 0x3a, 0xcf, 0x58, 0x32, 0xaa, 0xf9, 0x6d, 0x30, 0xa0, 0x45, 0x5d, + 0x78, 0xa4, 0xd9, 0xae, 0xd2, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x63, 0x7f, 0x0b, + 0x53, 0xdd, 0x15, 0xdd, 0x0d, 0xfd, 0x3e, 0x70, 0x16, 0x0a, 0x70, 0x62, 0x67, 0x25, + 0xd0, 0x3c, 0x9e, 0x1c, 0x4a, 0x63, 0x6e, 0x8d, 0xb4, 0xfd, 0x52, 0xe1, 0x41, 0xb9, + 0x74, 0xf7, 0x9b, 0x34, 0x65, 0xb4, 0x5f, 0xfd, 0x51, 0xbd, 0x4f, 0x92, 0xdf, 0x66, + 0xd3, 0x05, 0xf3, 0x88, 0x9f, 0xb5, 0x50, 0xb0, 0x97, 0x1e, 0xc8, 0xb9, 0x0e, 0x0c, + 0x08, 0x1d, 0x65, 0xa1, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x05, 0x00, 0x62, 0x0e, + 0x00, 0x00, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, 0x38, 0x6a, 0x43, 0x43, 0x42, 0x4a, 0x69, 0x67, + 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x57, 0x30, 0x71, 0x70, 0x4d, 0x78, + 0x45, 0x37, 0x52, 0x37, 0x69, 0x75, 0x30, 0x4b, 0x39, 0x38, 0x62, 0x55, 0x6a, 0x37, + 0x66, 0x4b, 0x73, 0x66, 0x75, 0x71, 0x63, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, + 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, 0x63, 0x44, 0x45, + 0x69, 0x4d, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x5a, 0x53, + 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x42, + 0x44, 0x53, 0x79, 0x42, 0x51, 0x62, 0x47, 0x46, 0x30, 0x5a, 0x6d, 0x39, 0x79, 0x62, + 0x53, 0x42, 0x44, 0x51, 0x54, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x43, 0x67, 0x77, 0x52, 0x0a, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, + 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, 0x70, 0x62, 0x32, + 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x63, 0x4d, + 0x43, 0x31, 0x4e, 0x68, 0x62, 0x6e, 0x52, 0x68, 0x49, 0x45, 0x4e, 0x73, 0x59, 0x58, + 0x4a, 0x68, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x49, + 0x0a, 0x44, 0x41, 0x4a, 0x44, 0x51, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, + 0x31, 0x55, 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x56, 0x4d, 0x77, 0x48, 0x68, 0x63, + 0x4e, 0x4d, 0x6a, 0x4d, 0x77, 0x4e, 0x44, 0x41, 0x79, 0x4d, 0x54, 0x63, 0x79, 0x4d, + 0x54, 0x45, 0x35, 0x57, 0x68, 0x63, 0x4e, 0x4d, 0x7a, 0x41, 0x77, 0x4e, 0x44, 0x41, + 0x79, 0x4d, 0x54, 0x63, 0x79, 0x4d, 0x54, 0x45, 0x35, 0x0a, 0x57, 0x6a, 0x42, 0x77, + 0x4d, 0x53, 0x49, 0x77, 0x49, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x42, + 0x6c, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, 0x43, 0x42, 0x54, 0x52, 0x31, 0x67, 0x67, + 0x55, 0x45, 0x4e, 0x4c, 0x49, 0x45, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, + 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x4b, 0x0a, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, 0x62, + 0x43, 0x42, 0x44, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, 0x47, 0x6c, + 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, + 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, 0x32, 0x78, + 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x0a, + 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, 0x42, 0x5a, + 0x4d, 0x42, 0x4d, 0x47, 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, + 0x45, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, 0x48, + 0x41, 0x30, 0x49, 0x41, 0x42, 0x45, 0x37, 0x45, 0x0a, 0x46, 0x50, 0x4d, 0x68, 0x61, + 0x55, 0x47, 0x45, 0x57, 0x6b, 0x68, 0x6b, 0x4f, 0x43, 0x78, 0x36, 0x51, 0x4c, 0x63, + 0x75, 0x71, 0x72, 0x56, 0x45, 0x32, 0x55, 0x6b, 0x62, 0x36, 0x31, 0x2f, 0x6d, 0x62, + 0x6d, 0x54, 0x66, 0x47, 0x33, 0x64, 0x69, 0x43, 0x62, 0x68, 0x56, 0x77, 0x4c, 0x38, + 0x49, 0x79, 0x4b, 0x73, 0x31, 0x33, 0x46, 0x75, 0x6b, 0x50, 0x52, 0x59, 0x58, 0x2b, + 0x64, 0x70, 0x79, 0x0a, 0x7a, 0x38, 0x6a, 0x7a, 0x76, 0x36, 0x58, 0x4a, 0x6e, 0x67, + 0x76, 0x47, 0x4c, 0x51, 0x41, 0x70, 0x37, 0x59, 0x65, 0x6a, 0x67, 0x67, 0x4d, 0x4f, + 0x4d, 0x49, 0x49, 0x44, 0x43, 0x6a, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, + 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x53, 0x56, 0x62, 0x31, 0x33, 0x4e, + 0x76, 0x52, 0x76, 0x68, 0x36, 0x55, 0x42, 0x4a, 0x79, 0x64, 0x54, 0x30, 0x0a, 0x4d, + 0x38, 0x34, 0x42, 0x56, 0x77, 0x76, 0x65, 0x56, 0x44, 0x42, 0x72, 0x42, 0x67, 0x4e, + 0x56, 0x48, 0x52, 0x38, 0x45, 0x5a, 0x44, 0x42, 0x69, 0x4d, 0x47, 0x43, 0x67, 0x58, + 0x71, 0x42, 0x63, 0x68, 0x6c, 0x70, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, + 0x76, 0x4c, 0x32, 0x46, 0x77, 0x61, 0x53, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, + 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x0a, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, + 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, + 0x4c, 0x33, 0x4e, 0x6e, 0x65, 0x43, 0x39, 0x6a, 0x5a, 0x58, 0x4a, 0x30, 0x61, 0x57, + 0x5a, 0x70, 0x59, 0x32, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4c, 0x33, 0x59, 0x7a, + 0x4c, 0x33, 0x42, 0x6a, 0x61, 0x32, 0x4e, 0x79, 0x62, 0x44, 0x39, 0x6a, 0x59, 0x54, + 0x31, 0x77, 0x0a, 0x62, 0x47, 0x46, 0x30, 0x5a, 0x6d, 0x39, 0x79, 0x62, 0x53, 0x5a, + 0x6c, 0x62, 0x6d, 0x4e, 0x76, 0x5a, 0x47, 0x6c, 0x75, 0x5a, 0x7a, 0x31, 0x6b, 0x5a, + 0x58, 0x49, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, + 0x45, 0x46, 0x41, 0x48, 0x70, 0x48, 0x38, 0x49, 0x61, 0x72, 0x51, 0x76, 0x79, 0x51, + 0x39, 0x67, 0x6d, 0x53, 0x70, 0x59, 0x35, 0x4d, 0x30, 0x49, 0x59, 0x0a, 0x4c, 0x53, + 0x34, 0x71, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, 0x45, 0x42, + 0x2f, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49, 0x47, 0x77, 0x44, 0x41, 0x4d, 0x42, 0x67, + 0x4e, 0x56, 0x48, 0x52, 0x4d, 0x42, 0x41, 0x66, 0x38, 0x45, 0x41, 0x6a, 0x41, 0x41, + 0x4d, 0x49, 0x49, 0x43, 0x4f, 0x77, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x0a, 0x42, 0x49, 0x49, 0x43, 0x4c, 0x44, 0x43, + 0x43, 0x41, 0x69, 0x67, 0x77, 0x48, 0x67, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, + 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x51, 0x51, 0x51, 0x57, 0x6f, 0x72, + 0x76, 0x4e, 0x78, 0x69, 0x57, 0x62, 0x65, 0x49, 0x32, 0x70, 0x6e, 0x62, 0x53, 0x77, + 0x61, 0x6e, 0x6f, 0x78, 0x44, 0x43, 0x43, 0x41, 0x57, 0x55, 0x47, 0x43, 0x69, 0x71, + 0x47, 0x0a, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x77, + 0x67, 0x67, 0x46, 0x56, 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x42, 0x41, 0x67, 0x45, 0x4c, + 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, + 0x45, 0x4e, 0x41, 0x51, 0x49, 0x43, 0x41, 0x67, 0x45, 0x4c, 0x0a, 0x4d, 0x42, 0x41, + 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, + 0x51, 0x49, 0x44, 0x41, 0x67, 0x45, 0x44, 0x4d, 0x42, 0x41, 0x47, 0x43, 0x79, 0x71, + 0x47, 0x53, 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x49, 0x45, 0x41, + 0x67, 0x45, 0x44, 0x4d, 0x42, 0x45, 0x47, 0x43, 0x79, 0x71, 0x47, 0x53, 0x49, 0x62, + 0x34, 0x54, 0x51, 0x45, 0x4e, 0x0a, 0x41, 0x51, 0x49, 0x46, 0x41, 0x67, 0x49, 0x41, + 0x2f, 0x7a, 0x41, 0x52, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x42, 0x67, 0x49, 0x43, 0x41, 0x50, 0x38, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x63, 0x43, 0x41, 0x51, 0x45, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, + 0x67, 0x67, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6b, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6f, 0x43, 0x0a, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x0a, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, + 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x0a, + 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, + 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, + 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, + 0x30, 0x77, 0x48, 0x77, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0a, 0x45, 0x41, 0x73, 0x4c, 0x41, + 0x77, 0x50, 0x2f, 0x2f, 0x77, 0x45, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, + 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, + 0x77, 0x46, 0x41, 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, + 0x51, 0x30, 0x42, 0x0a, 0x42, 0x41, 0x51, 0x47, 0x41, 0x47, 0x42, 0x71, 0x41, 0x41, + 0x41, 0x41, 0x4d, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, + 0x54, 0x51, 0x45, 0x4e, 0x41, 0x51, 0x55, 0x4b, 0x41, 0x51, 0x45, 0x77, 0x48, 0x67, + 0x59, 0x4b, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, + 0x42, 0x67, 0x51, 0x51, 0x70, 0x4a, 0x75, 0x73, 0x2f, 0x64, 0x35, 0x79, 0x0a, 0x7a, + 0x76, 0x39, 0x38, 0x45, 0x56, 0x63, 0x79, 0x75, 0x47, 0x47, 0x58, 0x2b, 0x54, 0x42, + 0x45, 0x42, 0x67, 0x6f, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x2b, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x48, 0x4d, 0x44, 0x59, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, + 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x45, 0x42, 0x41, + 0x66, 0x38, 0x77, 0x45, 0x41, 0x59, 0x4c, 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, + 0x68, 0x4e, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x49, 0x42, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4c, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x68, 0x4e, 0x41, 0x51, + 0x30, 0x42, 0x42, 0x77, 0x4d, 0x42, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x41, + 0x41, 0x77, 0x0a, 0x52, 0x51, 0x49, 0x68, 0x41, 0x50, 0x48, 0x62, 0x5a, 0x76, 0x49, + 0x6d, 0x50, 0x68, 0x74, 0x7a, 0x53, 0x59, 0x4f, 0x53, 0x4c, 0x47, 0x45, 0x47, 0x55, + 0x46, 0x51, 0x32, 0x68, 0x30, 0x58, 0x36, 0x70, 0x67, 0x65, 0x48, 0x77, 0x6f, 0x51, + 0x33, 0x6e, 0x6a, 0x4a, 0x37, 0x76, 0x48, 0x74, 0x57, 0x41, 0x69, 0x42, 0x43, 0x37, + 0x78, 0x75, 0x57, 0x65, 0x6e, 0x79, 0x4f, 0x46, 0x68, 0x6e, 0x6d, 0x0a, 0x71, 0x58, + 0x38, 0x6d, 0x63, 0x57, 0x76, 0x71, 0x34, 0x58, 0x32, 0x72, 0x6d, 0x64, 0x63, 0x4f, + 0x56, 0x63, 0x64, 0x53, 0x4c, 0x50, 0x5a, 0x4c, 0x4f, 0x4e, 0x4b, 0x41, 0x54, 0x77, + 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6c, 0x6a, 0x43, 0x43, 0x41, 0x6a, 0x32, 0x67, 0x41, + 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4a, 0x56, 0x76, 0x58, 0x63, 0x32, + 0x39, 0x47, 0x2b, 0x48, 0x70, 0x51, 0x45, 0x6e, 0x4a, 0x31, 0x50, 0x51, 0x7a, 0x7a, + 0x67, 0x46, 0x58, 0x43, 0x39, 0x35, 0x55, 0x4d, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, + 0x47, 0x53, 0x4d, 0x34, 0x39, 0x42, 0x41, 0x4d, 0x43, 0x0a, 0x4d, 0x47, 0x67, 0x78, + 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x45, 0x55, + 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, 0x48, 0x57, 0x43, 0x42, 0x53, + 0x62, 0x32, 0x39, 0x30, 0x49, 0x45, 0x4e, 0x42, 0x4d, 0x52, 0x6f, 0x77, 0x47, 0x41, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x4b, 0x44, 0x42, 0x46, 0x4a, 0x62, 0x6e, 0x52, 0x6c, + 0x62, 0x43, 0x42, 0x44, 0x0a, 0x62, 0x33, 0x4a, 0x77, 0x62, 0x33, 0x4a, 0x68, 0x64, + 0x47, 0x6c, 0x76, 0x62, 0x6a, 0x45, 0x55, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x42, 0x77, 0x77, 0x4c, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, + 0x32, 0x78, 0x68, 0x63, 0x6d, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, + 0x56, 0x42, 0x41, 0x67, 0x4d, 0x41, 0x6b, 0x4e, 0x42, 0x4d, 0x51, 0x73, 0x77, 0x0a, + 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55, 0x7a, + 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4f, 0x44, 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, + 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, 0x46, 0x77, 0x30, 0x7a, 0x4d, 0x7a, + 0x41, 0x31, 0x4d, 0x6a, 0x45, 0x78, 0x4d, 0x44, 0x55, 0x77, 0x4d, 0x54, 0x42, 0x61, + 0x4d, 0x48, 0x41, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, + 0x41, 0x4d, 0x4d, 0x47, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4e, + 0x48, 0x57, 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, + 0x47, 0x5a, 0x76, 0x63, 0x6d, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, + 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, + 0x47, 0x56, 0x73, 0x0a, 0x49, 0x45, 0x4e, 0x76, 0x63, 0x6e, 0x42, 0x76, 0x63, 0x6d, + 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, + 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x0a, 0x43, + 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, 0x56, + 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, + 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, + 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x4e, 0x53, 0x42, 0x2f, 0x37, + 0x74, 0x32, 0x31, 0x6c, 0x58, 0x53, 0x4f, 0x0a, 0x32, 0x43, 0x75, 0x7a, 0x70, 0x78, + 0x77, 0x37, 0x34, 0x65, 0x4a, 0x42, 0x37, 0x32, 0x45, 0x79, 0x44, 0x47, 0x67, 0x57, + 0x35, 0x72, 0x58, 0x43, 0x74, 0x78, 0x32, 0x74, 0x56, 0x54, 0x4c, 0x71, 0x36, 0x68, + 0x4b, 0x6b, 0x36, 0x7a, 0x2b, 0x55, 0x69, 0x52, 0x5a, 0x43, 0x6e, 0x71, 0x52, 0x37, + 0x70, 0x73, 0x4f, 0x76, 0x67, 0x71, 0x46, 0x65, 0x53, 0x78, 0x6c, 0x6d, 0x54, 0x6c, + 0x4a, 0x6c, 0x0a, 0x65, 0x54, 0x6d, 0x69, 0x32, 0x57, 0x59, 0x7a, 0x33, 0x71, 0x4f, + 0x42, 0x75, 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, + 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, + 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, + 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x0a, 0x42, 0x67, + 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, 0x4a, 0x4d, 0x45, 0x65, 0x67, + 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, + 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, + 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, + 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x0a, 0x64, 0x6d, 0x6c, 0x6a, 0x5a, 0x58, 0x4d, + 0x75, 0x61, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, + 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, + 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, + 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x6c, 0x57, 0x39, + 0x64, 0x0a, 0x7a, 0x62, 0x30, 0x62, 0x34, 0x65, 0x6c, 0x41, 0x53, 0x63, 0x6e, 0x55, + 0x39, 0x44, 0x50, 0x4f, 0x41, 0x56, 0x63, 0x4c, 0x33, 0x6c, 0x51, 0x77, 0x44, 0x67, + 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, + 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, + 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x0a, 0x41, 0x66, 0x38, + 0x43, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, + 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x52, 0x77, 0x41, 0x77, 0x52, 0x41, 0x49, + 0x67, 0x58, 0x73, 0x56, 0x6b, 0x69, 0x30, 0x77, 0x2b, 0x69, 0x36, 0x56, 0x59, 0x47, + 0x57, 0x33, 0x55, 0x46, 0x2f, 0x32, 0x32, 0x75, 0x61, 0x58, 0x65, 0x30, 0x59, 0x4a, + 0x44, 0x6a, 0x31, 0x55, 0x65, 0x0a, 0x6e, 0x41, 0x2b, 0x54, 0x6a, 0x44, 0x31, 0x61, + 0x69, 0x35, 0x63, 0x43, 0x49, 0x43, 0x59, 0x62, 0x31, 0x53, 0x41, 0x6d, 0x44, 0x35, + 0x78, 0x6b, 0x66, 0x54, 0x56, 0x70, 0x76, 0x6f, 0x34, 0x55, 0x6f, 0x79, 0x69, 0x53, + 0x59, 0x78, 0x72, 0x44, 0x57, 0x4c, 0x6d, 0x55, 0x52, 0x34, 0x43, 0x49, 0x39, 0x4e, + 0x4b, 0x79, 0x66, 0x50, 0x4e, 0x2b, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, + 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, + 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6a, 0x7a, 0x43, 0x43, 0x41, + 0x6a, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x49, 0x6d, 0x55, + 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, 0x7a, 0x67, 0x37, 0x53, 0x56, 0x55, + 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, 0x42, 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, + 0x49, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0a, + 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, + 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, + 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6a, + 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x45, 0x55, 0x6c, 0x75, + 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x0a, 0x63, 0x6e, 0x42, 0x76, 0x63, + 0x6d, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, + 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, + 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4c, 0x4d, 0x41, 0x6b, + 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, + 0x7a, 0x41, 0x4a, 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6c, + 0x56, 0x54, 0x4d, 0x42, 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4d, 0x44, 0x55, 0x79, + 0x4d, 0x54, 0x45, 0x77, 0x4e, 0x44, 0x55, 0x78, 0x4d, 0x46, 0x6f, 0x58, 0x44, 0x54, + 0x51, 0x35, 0x4d, 0x54, 0x49, 0x7a, 0x4d, 0x54, 0x49, 0x7a, 0x4e, 0x54, 0x6b, 0x31, + 0x4f, 0x56, 0x6f, 0x77, 0x61, 0x44, 0x45, 0x61, 0x4d, 0x42, 0x67, 0x47, 0x0a, 0x41, + 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x57, 0x77, + 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4a, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, + 0x30, 0x45, 0x78, 0x47, 0x6a, 0x41, 0x59, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, + 0x4d, 0x45, 0x55, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4e, 0x76, 0x63, + 0x6e, 0x42, 0x76, 0x63, 0x6d, 0x46, 0x30, 0x0a, 0x61, 0x57, 0x39, 0x75, 0x4d, 0x52, + 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, + 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, + 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, + 0x51, 0x30, 0x45, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, + 0x59, 0x54, 0x0a, 0x41, 0x6c, 0x56, 0x54, 0x4d, 0x46, 0x6b, 0x77, 0x45, 0x77, 0x59, + 0x48, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4b, + 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, + 0x45, 0x43, 0x36, 0x6e, 0x45, 0x77, 0x4d, 0x44, 0x49, 0x59, 0x5a, 0x4f, 0x6a, 0x2f, + 0x69, 0x50, 0x57, 0x73, 0x43, 0x7a, 0x61, 0x45, 0x4b, 0x69, 0x37, 0x0a, 0x31, 0x4f, + 0x69, 0x4f, 0x53, 0x4c, 0x52, 0x46, 0x68, 0x57, 0x47, 0x6a, 0x62, 0x6e, 0x42, 0x56, + 0x4a, 0x66, 0x56, 0x6e, 0x6b, 0x59, 0x34, 0x75, 0x33, 0x49, 0x6a, 0x6b, 0x44, 0x59, + 0x59, 0x4c, 0x30, 0x4d, 0x78, 0x4f, 0x34, 0x6d, 0x71, 0x73, 0x79, 0x59, 0x6a, 0x6c, + 0x42, 0x61, 0x6c, 0x54, 0x56, 0x59, 0x78, 0x46, 0x50, 0x32, 0x73, 0x4a, 0x42, 0x4b, + 0x35, 0x7a, 0x6c, 0x4b, 0x4f, 0x42, 0x0a, 0x75, 0x7a, 0x43, 0x42, 0x75, 0x44, 0x41, + 0x66, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x53, 0x4d, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, + 0x42, 0x51, 0x69, 0x5a, 0x51, 0x7a, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4f, + 0x44, 0x74, 0x4a, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4f, 0x53, 0x63, 0x47, 0x72, + 0x44, 0x42, 0x53, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7a, 0x42, + 0x4a, 0x0a, 0x4d, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, 0x68, 0x6b, 0x46, 0x6f, + 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, 0x4c, 0x32, 0x4e, 0x6c, 0x63, 0x6e, + 0x52, 0x70, 0x5a, 0x6d, 0x6c, 0x6a, 0x59, 0x58, 0x52, 0x6c, 0x63, 0x79, 0x35, 0x30, + 0x63, 0x6e, 0x56, 0x7a, 0x64, 0x47, 0x56, 0x6b, 0x63, 0x32, 0x56, 0x79, 0x64, 0x6d, + 0x6c, 0x6a, 0x5a, 0x58, 0x4d, 0x75, 0x61, 0x57, 0x35, 0x30, 0x0a, 0x5a, 0x57, 0x77, + 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x30, 0x6c, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, + 0x30, 0x64, 0x59, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x45, 0x4e, 0x42, 0x4c, 0x6d, 0x52, + 0x6c, 0x63, 0x6a, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, + 0x67, 0x51, 0x55, 0x49, 0x6d, 0x55, 0x4d, 0x31, 0x6c, 0x71, 0x64, 0x4e, 0x49, 0x6e, + 0x7a, 0x67, 0x37, 0x53, 0x56, 0x0a, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7a, 0x6b, 0x6e, + 0x42, 0x71, 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, + 0x48, 0x2f, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4d, 0x42, 0x49, 0x47, + 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2f, 0x77, 0x51, 0x49, 0x4d, 0x41, + 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x0a, 0x4b, 0x6f, 0x5a, 0x49, 0x7a, 0x6a, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, + 0x51, 0x41, 0x77, 0x52, 0x67, 0x49, 0x68, 0x41, 0x4f, 0x57, 0x2f, 0x35, 0x51, 0x6b, + 0x52, 0x2b, 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4e, 0x6f, 0x6f, 0x77, 0x4c, + 0x75, 0x50, 0x52, 0x4c, 0x73, 0x57, 0x47, 0x66, 0x2f, 0x59, 0x69, 0x37, 0x47, 0x53, + 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, 0x67, 0x0a, 0x41, 0x69, 0x45, 0x41, + 0x34, 0x4a, 0x30, 0x6c, 0x72, 0x48, 0x6f, 0x4d, 0x73, 0x2b, 0x58, 0x6f, 0x35, 0x6f, + 0x2f, 0x73, 0x58, 0x36, 0x4f, 0x39, 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5a, + 0x55, 0x47, 0x4f, 0x64, 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, + 0x49, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x00, + ]; + check_quote("e, &[0, 96, 106, 0, 0, 0]).unwrap(); + } + + #[test] + fn tdx_1() { + let quote = [ + 4, 0, 2, 0, 129, 0, 0, 0, 0, 0, 0, 0, 147, 154, 114, 51, 247, 156, 76, 169, 148, 10, + 13, 179, 149, 127, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 112, 245, 193, 194, 193, 97, 11, 242, + 221, 173, 52, 138, 136, 235, 246, 165, 80, 37, 110, 148, 158, 82, 18, 44, 116, 61, 201, + 124, 222, 80, 204, 175, 173, 47, 197, 146, 125, 21, 15, 48, 127, 186, 59, 140, 162, 24, + 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 0, 231, 0, 6, 0, 0, 0, 0, 0, 93, 86, 8, 14, 185, 239, 140, 224, + 187, 175, 107, 220, 218, 222, 235, 6, 231, 197, 176, 164, 209, 236, 22, 190, 134, 138, + 133, 169, 83, 186, 190, 12, 94, 84, 208, 28, 142, 5, 10, 84, 254, 28, 160, 120, 55, 37, + 48, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 22, 233, 37, 247, 150, 244, 226, 130, 207, 166, 231, 45, 76, 119, 168, 5, 96, + 152, 122, 250, 41, 21, 90, 97, 253, 195, 58, 219, 128, 234, 176, 212, 17, 42, 189, 82, + 56, 126, 94, 37, 166, 13, 238, 251, 138, 82, 135, 117, 24, 106, 53, 86, 81, 215, 130, + 41, 226, 43, 72, 182, 103, 79, 0, 108, 212, 45, 139, 179, 112, 234, 46, 145, 248, 100, + 217, 148, 78, 7, 193, 13, 107, 37, 15, 181, 32, 72, 204, 252, 93, 110, 234, 107, 135, + 143, 164, 209, 17, 30, 60, 72, 25, 158, 84, 78, 146, 66, 178, 218, 227, 55, 134, 247, + 30, 251, 51, 88, 64, 181, 222, 201, 202, 60, 114, 188, 65, 7, 6, 17, 253, 30, 60, 185, + 130, 97, 201, 148, 120, 72, 216, 131, 106, 163, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 16, 0, 0, 173, 106, 32, 109, 219, 200, 128, + 152, 193, 7, 155, 122, 125, 105, 155, 215, 213, 138, 170, 109, 16, 164, 39, 124, 85, + 23, 81, 166, 93, 155, 21, 180, 75, 204, 181, 76, 63, 176, 74, 232, 187, 105, 12, 6, + 252, 251, 184, 232, 245, 64, 217, 15, 193, 251, 48, 39, 120, 210, 6, 165, 79, 192, 101, + 165, 146, 8, 244, 170, 53, 52, 105, 26, 27, 111, 7, 237, 192, 22, 93, 124, 74, 31, 173, + 205, 119, 80, 167, 54, 215, 90, 6, 0, 234, 108, 253, 234, 29, 165, 67, 157, 15, 111, + 174, 243, 248, 207, 172, 69, 217, 220, 251, 85, 85, 184, 153, 126, 192, 253, 143, 167, + 105, 23, 73, 81, 110, 247, 117, 96, 6, 0, 69, 16, 0, 0, 7, 7, 255, 27, 3, 255, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, 0, 229, + 163, 167, 181, 216, 48, 194, 149, 59, 152, 83, 76, 108, 89, 163, 163, 79, 220, 52, 233, + 51, 247, 245, 137, 143, 10, 133, 207, 8, 132, 107, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 158, 42, 124, + 111, 148, 143, 23, 71, 78, 52, 167, 252, 67, 237, 3, 15, 124, 21, 99, 241, 186, 189, + 223, 99, 64, 200, 46, 14, 84, 168, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, + 251, 252, 166, 90, 191, 174, 148, 208, 101, 246, 199, 76, 45, 11, 45, 3, 58, 105, 166, + 8, 54, 250, 244, 81, 54, 176, 166, 141, 248, 70, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 2, 162, 97, 38, 185, + 42, 4, 183, 177, 168, 67, 207, 162, 237, 225, 183, 89, 224, 19, 4, 86, 108, 202, 197, + 108, 151, 18, 255, 142, 246, 86, 143, 11, 27, 240, 45, 133, 132, 182, 25, 177, 123, + 186, 98, 132, 113, 180, 86, 63, 3, 10, 8, 152, 75, 152, 211, 9, 149, 235, 147, 51, 209, + 203, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 93, 14, 0, 0, 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, + 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 69, 56, + 68, 67, 67, 66, 74, 97, 103, 65, 119, 73, 66, 65, 103, 73, 85, 89, 71, 99, 82, 76, 89, + 53, 43, 114, 99, 119, 67, 65, 88, 84, 72, 48, 71, 109, 83, 71, 98, 119, 115, 104, 113, + 119, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 99, + 68, 69, 105, 77, 67, 65, 71, 65, 49, 85, 69, 65, 119, 119, 90, 83, 87, 53, 48, 90, 87, + 119, 103, 85, 48, 100, 89, 73, 70, 66, 68, 83, 121, 66, 81, 98, 71, 70, 48, 90, 109, + 57, 121, 98, 83, 66, 68, 81, 84, 69, 97, 77, 66, 103, 71, 65, 49, 85, 69, 67, 103, 119, + 82, 10, 83, 87, 53, 48, 90, 87, 119, 103, 81, 50, 57, 121, 99, 71, 57, 121, 89, 88, 82, + 112, 98, 50, 52, 120, 70, 68, 65, 83, 66, 103, 78, 86, 66, 65, 99, 77, 67, 49, 78, 104, + 98, 110, 82, 104, 73, 69, 78, 115, 89, 88, 74, 104, 77, 81, 115, 119, 67, 81, 89, 68, + 86, 81, 81, 73, 10, 68, 65, 74, 68, 81, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, + 66, 104, 77, 67, 86, 86, 77, 119, 72, 104, 99, 78, 77, 106, 81, 120, 77, 106, 65, 49, + 77, 68, 65, 120, 79, 68, 85, 122, 87, 104, 99, 78, 77, 122, 69, 120, 77, 106, 65, 49, + 77, 68, 65, 120, 79, 68, 85, 122, 10, 87, 106, 66, 119, 77, 83, 73, 119, 73, 65, 89, + 68, 86, 81, 81, 68, 68, 66, 108, 74, 98, 110, 82, 108, 98, 67, 66, 84, 82, 49, 103, + 103, 85, 69, 78, 76, 73, 69, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, + 108, 77, 82, 111, 119, 71, 65, 89, 68, 86, 81, 81, 75, 10, 68, 66, 70, 74, 98, 110, 82, + 108, 98, 67, 66, 68, 98, 51, 74, 119, 98, 51, 74, 104, 100, 71, 108, 118, 98, 106, 69, + 85, 77, 66, 73, 71, 65, 49, 85, 69, 66, 119, 119, 76, 85, 50, 70, 117, 100, 71, 69, + 103, 81, 50, 120, 104, 99, 109, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 10, 66, 65, + 103, 77, 65, 107, 78, 66, 77, 81, 115, 119, 67, 81, 89, 68, 86, 81, 81, 71, 69, 119, + 74, 86, 85, 122, 66, 90, 77, 66, 77, 71, 66, 121, 113, 71, 83, 77, 52, 57, 65, 103, 69, + 71, 67, 67, 113, 71, 83, 77, 52, 57, 65, 119, 69, 72, 65, 48, 73, 65, 66, 79, 122, 117, + 10, 122, 56, 66, 78, 78, 88, 71, 102, 48, 48, 85, 66, 100, 53, 52, 90, 97, 120, 85, + 113, 79, 110, 79, 81, 103, 53, 81, 109, 51, 54, 115, 105, 49, 74, 111, 112, 97, 110, + 88, 114, 80, 112, 117, 78, 86, 102, 73, 98, 50, 77, 114, 54, 104, 75, 68, 106, 107, 75, + 107, 100, 114, 82, 81, 103, 10, 55, 101, 117, 57, 84, 73, 76, 86, 57, 53, 82, 43, 55, + 80, 118, 90, 102, 120, 101, 106, 103, 103, 77, 77, 77, 73, 73, 68, 67, 68, 65, 102, 66, + 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, 103, 66, 83, 86, 98, 49, 51, 78, 118, 82, + 118, 104, 54, 85, 66, 74, 121, 100, 84, 48, 10, 77, 56, 52, 66, 86, 119, 118, 101, 86, + 68, 66, 114, 66, 103, 78, 86, 72, 82, 56, 69, 90, 68, 66, 105, 77, 71, 67, 103, 88, + 113, 66, 99, 104, 108, 112, 111, 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 70, 119, + 97, 83, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, 86, 121, 10, 100, 109, 108, + 106, 90, 88, 77, 117, 97, 87, 53, 48, 90, 87, 119, 117, 89, 50, 57, 116, 76, 51, 78, + 110, 101, 67, 57, 106, 90, 88, 74, 48, 97, 87, 90, 112, 89, 50, 70, 48, 97, 87, 57, + 117, 76, 51, 89, 48, 76, 51, 66, 106, 97, 50, 78, 121, 98, 68, 57, 106, 89, 84, 49, + 119, 10, 98, 71, 70, 48, 90, 109, 57, 121, 98, 83, 90, 108, 98, 109, 78, 118, 90, 71, + 108, 117, 90, 122, 49, 107, 90, 88, 73, 119, 72, 81, 89, 68, 86, 82, 48, 79, 66, 66, + 89, 69, 70, 74, 97, 84, 121, 67, 67, 48, 49, 71, 76, 117, 52, 105, 65, 104, 100, 119, + 98, 111, 115, 84, 105, 112, 10, 75, 119, 90, 87, 77, 65, 52, 71, 65, 49, 85, 100, 68, + 119, 69, 66, 47, 119, 81, 69, 65, 119, 73, 71, 119, 68, 65, 77, 66, 103, 78, 86, 72, + 82, 77, 66, 65, 102, 56, 69, 65, 106, 65, 65, 77, 73, 73, 67, 79, 81, 89, 74, 75, 111, + 90, 73, 104, 118, 104, 78, 65, 81, 48, 66, 10, 66, 73, 73, 67, 75, 106, 67, 67, 65, + 105, 89, 119, 72, 103, 89, 75, 75, 111, 90, 73, 104, 118, 104, 78, 65, 81, 48, 66, 65, + 81, 81, 81, 78, 107, 122, 43, 57, 54, 111, 121, 99, 107, 71, 79, 47, 106, 70, 47, 56, + 72, 90, 56, 101, 84, 67, 67, 65, 87, 77, 71, 67, 105, 113, 71, 10, 83, 73, 98, 52, 84, + 81, 69, 78, 65, 81, 73, 119, 103, 103, 70, 84, 77, 66, 65, 71, 67, 121, 113, 71, 83, + 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 66, 65, 103, 69, 72, 77, 66, 65, 71, 67, 121, + 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 67, 65, 103, 69, 72, 10, 77, 66, + 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 68, 65, 103, 69, + 67, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 69, + 65, 103, 69, 67, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 10, + 65, 81, 73, 70, 65, 103, 69, 68, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, + 81, 69, 78, 65, 81, 73, 71, 65, 103, 69, 66, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, + 98, 52, 84, 81, 69, 78, 65, 81, 73, 72, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, + 71, 10, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 73, 65, 103, 69, 68, 77, 66, 65, + 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 74, 65, 103, 69, 65, + 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 75, 65, + 103, 69, 65, 10, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, + 81, 73, 76, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, + 69, 78, 65, 81, 73, 77, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, + 52, 84, 81, 69, 78, 10, 65, 81, 73, 78, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, + 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 79, 65, 103, 69, 65, 77, 66, 65, 71, + 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 80, 65, 103, 69, 65, 77, + 66, 65, 71, 67, 121, 113, 71, 10, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 81, 65, + 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, + 73, 82, 65, 103, 69, 76, 77, 66, 56, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, + 78, 65, 81, 73, 83, 66, 66, 65, 72, 10, 66, 119, 73, 67, 65, 119, 69, 65, 65, 119, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 77, 66, 65, 71, 67, 105, 113, 71, 83, 73, 98, 52, + 84, 81, 69, 78, 65, 81, 77, 69, 65, 103, 65, 65, 77, 66, 81, 71, 67, 105, 113, 71, 83, + 73, 98, 52, 84, 81, 69, 78, 65, 81, 81, 69, 10, 66, 103, 67, 65, 98, 119, 85, 65, 65, + 68, 65, 80, 66, 103, 111, 113, 104, 107, 105, 71, 43, 69, 48, 66, 68, 81, 69, 70, 67, + 103, 69, 66, 77, 66, 52, 71, 67, 105, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, + 89, 69, 69, 73, 89, 81, 78, 89, 69, 43, 108, 116, 89, 109, 10, 76, 85, 69, 101, 65, 74, + 83, 109, 67, 116, 65, 119, 82, 65, 89, 75, 75, 111, 90, 73, 104, 118, 104, 78, 65, 81, + 48, 66, 66, 122, 65, 50, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, + 78, 65, 81, 99, 66, 65, 81, 72, 47, 77, 66, 65, 71, 67, 121, 113, 71, 10, 83, 73, 98, + 52, 84, 81, 69, 78, 65, 81, 99, 67, 65, 81, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, + 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 99, 68, 65, 81, 72, 47, 77, 65, 111, 71, 67, + 67, 113, 71, 83, 77, 52, 57, 66, 65, 77, 67, 65, 48, 103, 65, 77, 69, 85, 67, 10, 73, + 65, 74, 88, 103, 116, 73, 109, 88, 71, 51, 97, 104, 113, 81, 82, 122, 48, 90, 66, 43, + 66, 83, 107, 97, 70, 43, 88, 87, 110, 110, 119, 90, 49, 88, 102, 110, 106, 67, 79, 102, + 54, 115, 87, 65, 105, 69, 65, 115, 76, 57, 52, 110, 57, 53, 55, 86, 89, 65, 47, 118, + 109, 77, 119, 10, 103, 65, 56, 104, 78, 115, 112, 97, 66, 71, 80, 106, 99, 105, 50, 43, + 107, 104, 98, 120, 80, 112, 100, 43, 86, 100, 89, 61, 10, 45, 45, 45, 45, 45, 69, 78, + 68, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, 45, 45, + 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, + 45, 45, 10, 77, 73, 73, 67, 108, 106, 67, 67, 65, 106, 50, 103, 65, 119, 73, 66, 65, + 103, 73, 86, 65, 74, 86, 118, 88, 99, 50, 57, 71, 43, 72, 112, 81, 69, 110, 74, 49, 80, + 81, 122, 122, 103, 70, 88, 67, 57, 53, 85, 77, 65, 111, 71, 67, 67, 113, 71, 83, 77, + 52, 57, 66, 65, 77, 67, 10, 77, 71, 103, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, + 77, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 70, 78, 72, 87, 67, 66, 83, 98, 50, 57, + 48, 73, 69, 78, 66, 77, 82, 111, 119, 71, 65, 89, 68, 86, 81, 81, 75, 68, 66, 70, 74, + 98, 110, 82, 108, 98, 67, 66, 68, 10, 98, 51, 74, 119, 98, 51, 74, 104, 100, 71, 108, + 118, 98, 106, 69, 85, 77, 66, 73, 71, 65, 49, 85, 69, 66, 119, 119, 76, 85, 50, 70, + 117, 100, 71, 69, 103, 81, 50, 120, 104, 99, 109, 69, 120, 67, 122, 65, 74, 66, 103, + 78, 86, 66, 65, 103, 77, 65, 107, 78, 66, 77, 81, 115, 119, 10, 67, 81, 89, 68, 86, 81, + 81, 71, 69, 119, 74, 86, 85, 122, 65, 101, 70, 119, 48, 120, 79, 68, 65, 49, 77, 106, + 69, 120, 77, 68, 85, 119, 77, 84, 66, 97, 70, 119, 48, 122, 77, 122, 65, 49, 77, 106, + 69, 120, 77, 68, 85, 119, 77, 84, 66, 97, 77, 72, 65, 120, 73, 106, 65, 103, 10, 66, + 103, 78, 86, 66, 65, 77, 77, 71, 85, 108, 117, 100, 71, 86, 115, 73, 70, 78, 72, 87, + 67, 66, 81, 81, 48, 115, 103, 85, 71, 120, 104, 100, 71, 90, 118, 99, 109, 48, 103, 81, + 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, + 71, 86, 115, 10, 73, 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, + 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, + 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, + 119, 67, 81, 48, 69, 120, 10, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 89, 84, 65, + 108, 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, 122, 106, 48, 67, 65, + 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, 68, 81, 103, 65, 69, 78, 83, + 66, 47, 55, 116, 50, 49, 108, 88, 83, 79, 10, 50, 67, 117, 122, 112, 120, 119, 55, 52, + 101, 74, 66, 55, 50, 69, 121, 68, 71, 103, 87, 53, 114, 88, 67, 116, 120, 50, 116, 86, + 84, 76, 113, 54, 104, 75, 107, 54, 122, 43, 85, 105, 82, 90, 67, 110, 113, 82, 55, 112, + 115, 79, 118, 103, 113, 70, 101, 83, 120, 108, 109, 84, 108, 74, 108, 10, 101, 84, 109, + 105, 50, 87, 89, 122, 51, 113, 79, 66, 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, + 86, 72, 83, 77, 69, 71, 68, 65, 87, 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, + 105, 102, 79, 68, 116, 74, 86, 83, 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, + 10, 66, 103, 78, 86, 72, 82, 56, 69, 83, 122, 66, 74, 77, 69, 101, 103, 82, 97, 66, 68, + 104, 107, 70, 111, 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, + 112, 90, 109, 108, 106, 89, 88, 82, 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, + 86, 107, 99, 50, 86, 121, 10, 100, 109, 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 90, + 87, 119, 117, 89, 50, 57, 116, 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, + 109, 57, 118, 100, 69, 78, 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, + 81, 52, 69, 70, 103, 81, 85, 108, 87, 57, 100, 10, 122, 98, 48, 98, 52, 101, 108, 65, + 83, 99, 110, 85, 57, 68, 80, 79, 65, 86, 99, 76, 51, 108, 81, 119, 68, 103, 89, 68, 86, + 82, 48, 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, 65, 49, + 85, 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 10, 65, 102, 56, 67, 65, 81, + 65, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 68, 82, 119, + 65, 119, 82, 65, 73, 103, 88, 115, 86, 107, 105, 48, 119, 43, 105, 54, 86, 89, 71, 87, + 51, 85, 70, 47, 50, 50, 117, 97, 88, 101, 48, 89, 74, 68, 106, 49, 85, 101, 10, 110, + 65, 43, 84, 106, 68, 49, 97, 105, 53, 99, 67, 73, 67, 89, 98, 49, 83, 65, 109, 68, 53, + 120, 107, 102, 84, 86, 112, 118, 111, 52, 85, 111, 121, 105, 83, 89, 120, 114, 68, 87, + 76, 109, 85, 82, 52, 67, 73, 57, 78, 75, 121, 102, 80, 78, 43, 10, 45, 45, 45, 45, 45, + 69, 78, 68, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, + 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, + 45, 45, 45, 45, 10, 77, 73, 73, 67, 106, 122, 67, 67, 65, 106, 83, 103, 65, 119, 73, + 66, 65, 103, 73, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, + 86, 85, 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 67, 103, 89, 73, 75, 111, + 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, 103, 71, 65, + 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, + 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, + 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 10, 99, 110, 66, 118, + 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, + 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, + 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, 74, 10, 66, + 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, 77, 66, 52, 88, 68, 84, 69, 52, 77, 68, + 85, 121, 77, 84, 69, 119, 78, 68, 85, 120, 77, 70, 111, 88, 68, 84, 81, 53, 77, 84, 73, + 122, 77, 84, 73, 122, 78, 84, 107, 49, 79, 86, 111, 119, 97, 68, 69, 97, 77, 66, 103, + 71, 10, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, + 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, + 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 99, 110, + 66, 118, 99, 109, 70, 48, 10, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, + 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, + 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, + 74, 66, 103, 78, 86, 66, 65, 89, 84, 10, 65, 108, 86, 84, 77, 70, 107, 119, 69, 119, + 89, 72, 75, 111, 90, 73, 122, 106, 48, 67, 65, 81, 89, 73, 75, 111, 90, 73, 122, 106, + 48, 68, 65, 81, 99, 68, 81, 103, 65, 69, 67, 54, 110, 69, 119, 77, 68, 73, 89, 90, 79, + 106, 47, 105, 80, 87, 115, 67, 122, 97, 69, 75, 105, 55, 10, 49, 79, 105, 79, 83, 76, + 82, 70, 104, 87, 71, 106, 98, 110, 66, 86, 74, 102, 86, 110, 107, 89, 52, 117, 51, 73, + 106, 107, 68, 89, 89, 76, 48, 77, 120, 79, 52, 109, 113, 115, 121, 89, 106, 108, 66, + 97, 108, 84, 86, 89, 120, 70, 80, 50, 115, 74, 66, 75, 53, 122, 108, 75, 79, 66, 10, + 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, + 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, 86, 83, + 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, 66, 103, 78, 86, 72, 82, 56, 69, 83, + 122, 66, 74, 10, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, 100, 72, 82, 119, + 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, + 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, 86, 121, 100, 109, + 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 10, 90, 87, 119, 117, 89, 50, 57, 116, 76, + 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, 57, 118, 100, 69, 78, 66, 76, + 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, 81, 52, 69, 70, 103, 81, 85, 73, + 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 10, 85, 114, 57, 81, + 71, 122, 107, 110, 66, 113, 119, 119, 68, 103, 89, 68, 86, 82, 48, 80, 65, 81, 72, 47, + 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, 65, 49, 85, 100, 69, 119, 69, 66, 47, + 119, 81, 73, 77, 65, 89, 66, 65, 102, 56, 67, 65, 81, 69, 119, 67, 103, 89, 73, 10, 75, + 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 68, 83, 81, 65, 119, 82, 103, 73, 104, 65, + 79, 87, 47, 53, 81, 107, 82, 43, 83, 57, 67, 105, 83, 68, 99, 78, 111, 111, 119, 76, + 117, 80, 82, 76, 115, 87, 71, 102, 47, 89, 105, 55, 71, 83, 88, 57, 52, 66, 103, 119, + 84, 119, 103, 10, 65, 105, 69, 65, 52, 74, 48, 108, 114, 72, 111, 77, 115, 43, 88, 111, + 53, 111, 47, 115, 88, 54, 79, 57, 81, 87, 120, 72, 82, 65, 118, 90, 85, 71, 79, 100, + 82, 81, 55, 99, 118, 113, 82, 88, 97, 113, 73, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, + 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + check_quote("e, &[0, 128, 111, 5, 0, 0]).unwrap(); + } +} diff --git a/crates/teepot/tests/sgx_quote_verification.rs b/crates/teepot/tests/sgx_quote_verification.rs index 4f04d2f..7d8c6b5 100644 --- a/crates/teepot/tests/sgx_quote_verification.rs +++ b/crates/teepot/tests/sgx_quote_verification.rs @@ -1,19 +1,26 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2024 Matter Labs +// Copyright (c) 2024-2025 Matter Labs +#[cfg(feature = "quote_op")] mod sgx { - use anyhow::Result; - use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral}; + use anyhow::{Context, Result}; use std::time::{Duration, UNIX_EPOCH}; - use teepot::sgx::{verify_quote_with_collateral, QuoteVerificationResult}; + use teepot::{ + prover::reportdata::{ReportData, ReportDataV1}, + quote::{ + tcblevel::TcbLevel, verify_quote_with_collateral, Collateral, Quote, + QuoteVerificationResult, Report, + }, + }; + use tracing_test::traced_test; fn check_quote( quote: &[u8], - collateral: &Collateral, + collateral: Option<&Collateral>, current_time: i64, expected_mrsigner: &[u8], expected_reportdata: &[u8], - expected_result: sgx_ql_qv_result_t, + expected_result: TcbLevel, ) -> Result<()> { let QuoteVerificationResult { collateral_expired, @@ -22,9 +29,9 @@ mod sgx { quote, advisories, tcb_level_date_tag, - } = verify_quote_with_collateral(quote, Some(collateral), current_time).unwrap(); + } = verify_quote_with_collateral(quote, collateral, current_time)?; - if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK { + if collateral_expired || result != TcbLevel::Ok { print!("Attestation failed: "); if collateral_expired { @@ -36,28 +43,7 @@ mod sgx { earliest_expiration_date ); } - - match result { - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => (), - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => println!("Config needed"), - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { - println!("Config and Software hardening needed") - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => println!("Out of date"), - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => { - println!("Out of Date and Config needed") - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => { - println!("Software hardening needed") - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_INVALID_SIGNATURE => { - println!("Invalid signature") - } - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_REVOKED => println!("Revoked"), - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED => println!("Unspecified"), - - _ => println!("Unknown state!"), - } + println!("{result:?}"); } for advisory in advisories { @@ -67,16 +53,22 @@ mod sgx { println!("tcb_level_date_tag: {:?}", tcb_level_date_tag); assert_eq!(expected_result, result); - let expected_reportdata = hex::encode(expected_reportdata); - let expected_mrsigner = hex::encode(expected_mrsigner); - let actual_reportdata = hex::encode("e.report_body.reportdata[..]); - let actual_mrsigner = hex::encode("e.report_body.mrsigner[..]); - assert_eq!(expected_mrsigner, actual_mrsigner); - assert_eq!(expected_reportdata, actual_reportdata); + + if let Report::SgxEnclave(report_body) = quote.report { + let expected_reportdata = hex::encode(expected_reportdata); + let expected_mrsigner = hex::encode(expected_mrsigner); + let actual_reportdata = hex::encode(&report_body.report_data[..]); + let actual_mrsigner = hex::encode(&report_body.mr_signer[..]); + assert_eq!(expected_mrsigner, actual_mrsigner); + assert_eq!(expected_reportdata, actual_reportdata); + } + Ok(()) } #[test] + // alternative quote verification cannot cope with old collateral data format + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] fn sw_hardening() { let quote = [ 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a, @@ -1132,16 +1124,18 @@ mod sgx { check_quote( "e, - &collateral, + Some(&collateral), current_time, &mrsigner, &report_data, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED, + TcbLevel::SwHardeningNeeded, ) .unwrap(); } #[test] + // alternative quote verification cannot cope with old collateral data format + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] fn out_of_date() { let quote = [ 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a, @@ -2203,11 +2197,11 @@ mod sgx { check_quote( "e, - &collateral, + Some(&collateral), current_time, &mrsigner, &report_data, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE, + TcbLevel::OutOfDate, ) .unwrap(); } @@ -2560,17 +2554,17 @@ mod sgx { ]; let collateral = Collateral { - major_version: 1, - minor_version: 0, - tee_type: 0, - pck_crl_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAg\nBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVs\nIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0Ex\nCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO\n2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJl\neTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBS\nBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2Vy\ndmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9d\nzb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1Ue\nnA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), - root_ca_crl: "-----BEGIN X509 CRL-----\nMIIBITCByAIBATAKBggqhkjOPQQDAjBoMRowGAYDVQQDDBFJbnRlbCBTR1ggUm9v\ndCBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRh\nIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDQwMzEwMjI1MVoX\nDTI0MDQwMjEwMjI1MVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFCJlDNZa\nnTSJ84O0lVK/UBs5JwasMAoGCCqGSM49BAMCA0gAMEUCIFFXfUfZ+6FXtl8etfRl\ne7xeVsyvc1oD8blj1wSAWrEYAiEAk5AV7BY25+r6X0JsHkAmR8ZzEytoUMq9aM72\nutdoKgM=\n-----END X509 CRL-----\n\0".as_bytes().into(), - pck_crl: "-----BEGIN X509 CRL-----\nMIIKYTCCCggCAQEwCgYIKoZIzj0EAwIwcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBD\nSyBQbGF0Zm9ybSBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNV\nBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDUx\nMzIxMDAwM1oXDTIzMDYxMjIxMDAwM1owggk0MDMCFG/DTlAj5yiSNDXWGqS4PGGB\nZq01Fw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAO+ubpcV/KE7h+Mz\n6CYe1tmQqSatFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAP1ghkhi\nnLpzB4tNSS9LPqdBrQjNFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIV\nAIr5JBhOHVr93XPD1joS9ei1c35WFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMK\nAQEwNAIVALEleXjPqczdB1mr+MXKcvrjp4qbFw0yMzA1MTMyMTAwMDNaMAwwCgYD\nVR0VBAMKAQEwMwIUdP6mFKlyvg4oQ/IFmDWBHthy+bMXDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATA0AhUA+cTvVrOrSNV34Qi67fS/iAFCFLkXDTIzMDUxMzIx\nMDAwM1owDDAKBgNVHRUEAwoBATAzAhQHHeB3j55fxPKHjzDWsHyaMOazCxcNMjMw\nNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQDN4kJPlyzqlP8jmTf02AwlAp3W\nCxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFGwzGeUQm2RQfTzxEyzg\nA0nvUnMZFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAN8I11a2anSX\n9DtbtYraBNP096k3Fw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUKK9I\nW2z2fkCaOdXLWu5FmPeo+nsXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATA0\nAhUA+4strsCSytqKqbxP8vHCDQNGZowXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUE\nAwoBATA0AhUAzUhQrFK9zGmmpvBYyLxXu9C1+GQXDTIzMDUxMzIxMDAwM1owDDAK\nBgNVHRUEAwoBATA0AhUAmU3TZm9SdfuAX5XdAr1QyyZ52K0XDTIzMDUxMzIxMDAw\nM1owDDAKBgNVHRUEAwoBATAzAhQHAhNpACUidNkDXu31RXRi+tDvTBcNMjMwNTEz\nMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFGHyv3Pjm04EqifYAb1z0kMZtb+AFw0y\nMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUOZK+hRuWkC7/OJWebC7/GwZR\npLUXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhRjnxOaUED9z/GR6KT7\nG/CG7WA5cRcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCVnVM/kknc\nHlE1RM3IML8Zt/HzARcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFA/a\nQ6ALaOp5t8LerqwLSYvfsq+QFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEw\nNAIVAJ1ndTuB5HCQrqdj++xMRUm825kzFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0V\nBAMKAQEwMwIUNL+7eh2cVoFH4Ri2FPe3btPvaN8XDTIzMDUxMzIxMDAwM1owDDAK\nBgNVHRUEAwoBATA0AhUAhdPJOBt3p+BNEZyeWtZ0n/P/q4cXDTIzMDUxMzIxMDAw\nM1owDDAKBgNVHRUEAwoBATA0AhUAk4h8pEEeepI70f7SgZspSfIBtbQXDTIzMDUx\nMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhQkmNxig5MJlv2L8jo3rL4mo77UVxcN\nMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCKZvGnSUiGZ2icw5A6xUxm\nK3EucxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCvwTYQvdNst5hd\nEGSBqIDToB/aBxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQDv4Ess\nM9A2qslspnO/HppHtk1cuxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQC\nFQCD2ayNi7UJ0cbICa1xLoQwVZ7X8xcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQD\nCgEBMDMCFHkx/VC1Bxwbv8W3tt7YtFudi4UpFw0yMzA1MTMyMTAwMDNaMAwwCgYD\nVR0VBAMKAQEwMwIUH6IOKXC95dV/e43fgzlITh8dCCMXDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATAzAhQeh7LDsy2NI+QRzvNBl7la8Mit9RcNMjMwNTEzMjEw\nMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCa/S7pCkc1UKFn2ZaRFDfHUC0fCRcNMjMw\nNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFESBsPEXKKE7aW0+qcdwoLFexY3a\nFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAKeFn1eYLvDmfTe8jvLv\nWsg1/xqpFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUeuN3SKn5EvTG\nO6erB8WTzh0dEYEXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhQTiEsz\nJpk4wZWqFw/KddoXdTjfCxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMC\nFCw8xv6SedsVFtXOOfKomM2loXXhFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMK\nAQEwMwIUcXlIaHUJI0vpeeS33ObzG+9ktowXDTIzMDUxMzIxMDAwM1owDDAKBgNV\nHRUEAwoBATA0AhUAnXbvLDnBNuhli25zlrHXRFonYx8XDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATA0AhUAw+Al/KmV829ZtIRnk54+NOY2Gm8XDTIzMDUxMzIx\nMDAwM1owDDAKBgNVHRUEAwoBATA0AhUAjF9rMlfaBbF0KeLmG6ll1nMwYGoXDTIz\nMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATA0AhUAoXxRci7B4MMnj+i98FIFnL7E\n5kgXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBAaAvMC0wCgYDVR0UBAMCAQEw\nHwYDVR0jBBgwFoAUlW9dzb0b4elAScnU9DPOAVcL3lQwCgYIKoZIzj0EAwIDRwAw\nRAIgFCcExhc9muZCJybEgaDtgH1ZF5s739IHTjXl/JZTDR4CIG5WeLcFCg2nZozP\n5NqC0+MecmYxPN0eubPbpzhsF28z\n-----END X509 CRL-----\n\0".as_bytes().into(), - tcb_info_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), - tcb_info: "{\"tcbInfo\":{\"id\":\"SGX\",\"version\":3,\"issueDate\":\"2023-05-15T14:08:11Z\",\"nextUpdate\":\"2023-06-14T14:08:11Z\",\"fmspc\":\"00606a000000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":15,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":11,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":11,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":1},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":11,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":11,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":9,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":1},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2022-08-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":9,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2022-08-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]}]},\"signature\":\"5fee75684718bcb9191312cd3bf47096a4068f450d2ec9e05b8cd5a3a7cb61b292612a708c4c5a304836c82c3a7191447e3ba691e59287cc9c8943e821734d41\"}\0".as_bytes().into(), - qe_identity_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), - qe_identity: "{\"enclaveIdentity\":{\"id\":\"QE\",\"version\":2,\"issueDate\":\"2023-05-15T14:48:40Z\",\"nextUpdate\":\"2023-06-14T14:48:40Z\",\"tcbEvaluationDataNumber\":15,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":1,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":8},\"tcbDate\":\"2023-02-15T00: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\":\"97ff8957975b40aa51a17765d734363b8cb7557e6657b04f63b67c05f2886be1b7cc14b9c1b889e03b85866e277a102eb9fb85029b7008b41754cdba014a5a8c\"}\0".as_bytes().into(), - }; + major_version: 1, + minor_version: 0, + tee_type: 0, + pck_crl_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMC\nMGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD\nb3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw\nCQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAg\nBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVs\nIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0Ex\nCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO\n2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJl\neTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBS\nBgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2Vy\ndmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9d\nzb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1Ue\nnA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), + root_ca_crl: "-----BEGIN X509 CRL-----\nMIIBITCByAIBATAKBggqhkjOPQQDAjBoMRowGAYDVQQDDBFJbnRlbCBTR1ggUm9v\ndCBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRh\nIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDQwMzEwMjI1MVoX\nDTI0MDQwMjEwMjI1MVqgLzAtMAoGA1UdFAQDAgEBMB8GA1UdIwQYMBaAFCJlDNZa\nnTSJ84O0lVK/UBs5JwasMAoGCCqGSM49BAMCA0gAMEUCIFFXfUfZ+6FXtl8etfRl\ne7xeVsyvc1oD8blj1wSAWrEYAiEAk5AV7BY25+r6X0JsHkAmR8ZzEytoUMq9aM72\nutdoKgM=\n-----END X509 CRL-----\n\0".as_bytes().into(), + pck_crl: "-----BEGIN X509 CRL-----\nMIIKYTCCCggCAQEwCgYIKoZIzj0EAwIwcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBD\nSyBQbGF0Zm9ybSBDQTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNV\nBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMXDTIzMDUx\nMzIxMDAwM1oXDTIzMDYxMjIxMDAwM1owggk0MDMCFG/DTlAj5yiSNDXWGqS4PGGB\nZq01Fw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAO+ubpcV/KE7h+Mz\n6CYe1tmQqSatFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAP1ghkhi\nnLpzB4tNSS9LPqdBrQjNFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIV\nAIr5JBhOHVr93XPD1joS9ei1c35WFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMK\nAQEwNAIVALEleXjPqczdB1mr+MXKcvrjp4qbFw0yMzA1MTMyMTAwMDNaMAwwCgYD\nVR0VBAMKAQEwMwIUdP6mFKlyvg4oQ/IFmDWBHthy+bMXDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATA0AhUA+cTvVrOrSNV34Qi67fS/iAFCFLkXDTIzMDUxMzIx\nMDAwM1owDDAKBgNVHRUEAwoBATAzAhQHHeB3j55fxPKHjzDWsHyaMOazCxcNMjMw\nNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQDN4kJPlyzqlP8jmTf02AwlAp3W\nCxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFGwzGeUQm2RQfTzxEyzg\nA0nvUnMZFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAN8I11a2anSX\n9DtbtYraBNP096k3Fw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUKK9I\nW2z2fkCaOdXLWu5FmPeo+nsXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATA0\nAhUA+4strsCSytqKqbxP8vHCDQNGZowXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUE\nAwoBATA0AhUAzUhQrFK9zGmmpvBYyLxXu9C1+GQXDTIzMDUxMzIxMDAwM1owDDAK\nBgNVHRUEAwoBATA0AhUAmU3TZm9SdfuAX5XdAr1QyyZ52K0XDTIzMDUxMzIxMDAw\nM1owDDAKBgNVHRUEAwoBATAzAhQHAhNpACUidNkDXu31RXRi+tDvTBcNMjMwNTEz\nMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFGHyv3Pjm04EqifYAb1z0kMZtb+AFw0y\nMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUOZK+hRuWkC7/OJWebC7/GwZR\npLUXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhRjnxOaUED9z/GR6KT7\nG/CG7WA5cRcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCVnVM/kknc\nHlE1RM3IML8Zt/HzARcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFA/a\nQ6ALaOp5t8LerqwLSYvfsq+QFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEw\nNAIVAJ1ndTuB5HCQrqdj++xMRUm825kzFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0V\nBAMKAQEwMwIUNL+7eh2cVoFH4Ri2FPe3btPvaN8XDTIzMDUxMzIxMDAwM1owDDAK\nBgNVHRUEAwoBATA0AhUAhdPJOBt3p+BNEZyeWtZ0n/P/q4cXDTIzMDUxMzIxMDAw\nM1owDDAKBgNVHRUEAwoBATA0AhUAk4h8pEEeepI70f7SgZspSfIBtbQXDTIzMDUx\nMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhQkmNxig5MJlv2L8jo3rL4mo77UVxcN\nMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCKZvGnSUiGZ2icw5A6xUxm\nK3EucxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCvwTYQvdNst5hd\nEGSBqIDToB/aBxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQDv4Ess\nM9A2qslspnO/HppHtk1cuxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDQC\nFQCD2ayNi7UJ0cbICa1xLoQwVZ7X8xcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQD\nCgEBMDMCFHkx/VC1Bxwbv8W3tt7YtFudi4UpFw0yMzA1MTMyMTAwMDNaMAwwCgYD\nVR0VBAMKAQEwMwIUH6IOKXC95dV/e43fgzlITh8dCCMXDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATAzAhQeh7LDsy2NI+QRzvNBl7la8Mit9RcNMjMwNTEzMjEw\nMDAzWjAMMAoGA1UdFQQDCgEBMDQCFQCa/S7pCkc1UKFn2ZaRFDfHUC0fCRcNMjMw\nNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMCFESBsPEXKKE7aW0+qcdwoLFexY3a\nFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwNAIVAKeFn1eYLvDmfTe8jvLv\nWsg1/xqpFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMKAQEwMwIUeuN3SKn5EvTG\nO6erB8WTzh0dEYEXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATAzAhQTiEsz\nJpk4wZWqFw/KddoXdTjfCxcNMjMwNTEzMjEwMDAzWjAMMAoGA1UdFQQDCgEBMDMC\nFCw8xv6SedsVFtXOOfKomM2loXXhFw0yMzA1MTMyMTAwMDNaMAwwCgYDVR0VBAMK\nAQEwMwIUcXlIaHUJI0vpeeS33ObzG+9ktowXDTIzMDUxMzIxMDAwM1owDDAKBgNV\nHRUEAwoBATA0AhUAnXbvLDnBNuhli25zlrHXRFonYx8XDTIzMDUxMzIxMDAwM1ow\nDDAKBgNVHRUEAwoBATA0AhUAw+Al/KmV829ZtIRnk54+NOY2Gm8XDTIzMDUxMzIx\nMDAwM1owDDAKBgNVHRUEAwoBATA0AhUAjF9rMlfaBbF0KeLmG6ll1nMwYGoXDTIz\nMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBATA0AhUAoXxRci7B4MMnj+i98FIFnL7E\n5kgXDTIzMDUxMzIxMDAwM1owDDAKBgNVHRUEAwoBAaAvMC0wCgYDVR0UBAMCAQEw\nHwYDVR0jBBgwFoAUlW9dzb0b4elAScnU9DPOAVcL3lQwCgYIKoZIzj0EAwIDRwAw\nRAIgFCcExhc9muZCJybEgaDtgH1ZF5s739IHTjXl/JZTDR4CIG5WeLcFCg2nZozP\n5NqC0+MecmYxPN0eubPbpzhsF28z\n-----END X509 CRL-----\n\0".as_bytes().into(), + tcb_info_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), + tcb_info: "{\"tcbInfo\":{\"id\":\"SGX\",\"version\":3,\"issueDate\":\"2023-05-15T14:08:11Z\",\"nextUpdate\":\"2023-06-14T14:08:11Z\",\"fmspc\":\"00606a000000\",\"pceId\":\"0000\",\"tcbType\":0,\"tcbEvaluationDataNumber\":15,\"tcbLevels\":[{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":11,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":11,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":1},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"SWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":11,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":11,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2023-02-15T00:00:00Z\",\"tcbStatus\":\"ConfigurationAndSWHardeningNeeded\",\"advisoryIDs\":[\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":9,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":1},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2022-08-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":7,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":9,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":13},\"tcbDate\":\"2022-08-10T00:00:00Z\",\"tcbStatus\":\"OutOfDateConfigurationNeeded\",\"advisoryIDs\":[\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\",\"INTEL-SA-00615\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":11},\"tcbDate\":\"2021-11-10T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":10},\"tcbDate\":\"2020-11-11T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00477\",\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]},{\"tcb\":{\"sgxtcbcomponents\":[{\"svn\":4,\"category\":\"BIOS\",\"type\":\"Early Microcode Update\"},{\"svn\":4,\"category\":\"OS/VMM\",\"type\":\"SGX Late Microcode Update\"},{\"svn\":3,\"category\":\"OS/VMM\",\"type\":\"TXT SINIT\"},{\"svn\":3,\"category\":\"BIOS\"},{\"svn\":255},{\"svn\":255},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0},{\"svn\":0}],\"pcesvn\":5},\"tcbDate\":\"2018-01-04T00:00:00Z\",\"tcbStatus\":\"OutOfDate\",\"advisoryIDs\":[\"INTEL-SA-00106\",\"INTEL-SA-00115\",\"INTEL-SA-00135\",\"INTEL-SA-00203\",\"INTEL-SA-00220\",\"INTEL-SA-00233\",\"INTEL-SA-00270\",\"INTEL-SA-00293\",\"INTEL-SA-00320\",\"INTEL-SA-00329\",\"INTEL-SA-00381\",\"INTEL-SA-00389\",\"INTEL-SA-00477\",\"INTEL-SA-00586\",\"INTEL-SA-00614\",\"INTEL-SA-00615\",\"INTEL-SA-00657\",\"INTEL-SA-00730\",\"INTEL-SA-00738\",\"INTEL-SA-00767\"]}]},\"signature\":\"5fee75684718bcb9191312cd3bf47096a4068f450d2ec9e05b8cd5a3a7cb61b292612a708c4c5a304836c82c3a7191447e3ba691e59287cc9c8943e821734d41\"}\0".as_bytes().into(), + qe_identity_issuer_chain: "-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG\nA1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw\nb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD\nVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv\nP+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju\nypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f\nBEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz\nLmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK\nQEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG\nSM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj\nftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n\0".as_bytes().into(), + qe_identity: "{\"enclaveIdentity\":{\"id\":\"QE\",\"version\":2,\"issueDate\":\"2023-05-15T14:48:40Z\",\"nextUpdate\":\"2023-06-14T14:48:40Z\",\"tcbEvaluationDataNumber\":15,\"miscselect\":\"00000000\",\"miscselectMask\":\"FFFFFFFF\",\"attributes\":\"11000000000000000000000000000000\",\"attributesMask\":\"FBFFFFFFFFFFFFFF0000000000000000\",\"mrsigner\":\"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF\",\"isvprodid\":1,\"tcbLevels\":[{\"tcb\":{\"isvsvn\":8},\"tcbDate\":\"2023-02-15T00: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\":\"97ff8957975b40aa51a17765d734363b8cb7557e6657b04f63b67c05f2886be1b7cc14b9c1b889e03b85866e277a102eb9fb85029b7008b41754cdba014a5a8c\"}\0".as_bytes().into(), + }; let current_time = 1684165303; @@ -2586,16 +2580,18 @@ mod sgx { check_quote( "e, - &collateral, + Some(&collateral), current_time, &mrsigner, &report_data, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED, + TcbLevel::SwHardeningNeeded, ) .unwrap(); } #[test] + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + // alternative quote verification errors on debug flag fn vault_proxy() { let quote = [ 3, 0, 2, 0, 0, 0, 0, 0, 9, 0, 14, 0, 147, 154, 114, 51, 247, 156, 76, 169, 148, 10, 13, @@ -3669,12 +3665,1520 @@ mod sgx { check_quote( "e, - &collateral, + Some(&collateral), current_time, &mrsigner, &report_data, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED, + TcbLevel::SwHardeningNeeded, ) .unwrap(); } + + #[tokio::test] + #[traced_test] + async fn tdx_1() -> Result<()> { + let quote = [ + 4, 0, 2, 0, 129, 0, 0, 0, 0, 0, 0, 0, 147, 154, 114, 51, 247, 156, 76, 169, 148, 10, + 13, 179, 149, 127, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 112, 245, 193, 194, 193, 97, 11, 242, + 221, 173, 52, 138, 136, 235, 246, 165, 80, 37, 110, 148, 158, 82, 18, 44, 116, 61, 201, + 124, 222, 80, 204, 175, 173, 47, 197, 146, 125, 21, 15, 48, 127, 186, 59, 140, 162, 24, + 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 0, 231, 0, 6, 0, 0, 0, 0, 0, 93, 86, 8, 14, 185, 239, 140, 224, + 187, 175, 107, 220, 218, 222, 235, 6, 231, 197, 176, 164, 209, 236, 22, 190, 134, 138, + 133, 169, 83, 186, 190, 12, 94, 84, 208, 28, 142, 5, 10, 84, 254, 28, 160, 120, 55, 37, + 48, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 22, 233, 37, 247, 150, 244, 226, 130, 207, 166, 231, 45, 76, 119, 168, 5, 96, + 152, 122, 250, 41, 21, 90, 97, 253, 195, 58, 219, 128, 234, 176, 212, 17, 42, 189, 82, + 56, 126, 94, 37, 166, 13, 238, 251, 138, 82, 135, 117, 24, 106, 53, 86, 81, 215, 130, + 41, 226, 43, 72, 182, 103, 79, 0, 108, 212, 45, 139, 179, 112, 234, 46, 145, 248, 100, + 217, 148, 78, 7, 193, 13, 107, 37, 15, 181, 32, 72, 204, 252, 93, 110, 234, 107, 135, + 143, 164, 209, 17, 30, 60, 72, 25, 158, 84, 78, 146, 66, 178, 218, 227, 55, 134, 247, + 30, 251, 51, 88, 64, 181, 222, 201, 202, 60, 114, 188, 65, 7, 6, 17, 253, 30, 60, 185, + 130, 97, 201, 148, 120, 72, 216, 131, 106, 163, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 16, 0, 0, 173, 106, 32, 109, 219, 200, 128, + 152, 193, 7, 155, 122, 125, 105, 155, 215, 213, 138, 170, 109, 16, 164, 39, 124, 85, + 23, 81, 166, 93, 155, 21, 180, 75, 204, 181, 76, 63, 176, 74, 232, 187, 105, 12, 6, + 252, 251, 184, 232, 245, 64, 217, 15, 193, 251, 48, 39, 120, 210, 6, 165, 79, 192, 101, + 165, 146, 8, 244, 170, 53, 52, 105, 26, 27, 111, 7, 237, 192, 22, 93, 124, 74, 31, 173, + 205, 119, 80, 167, 54, 215, 90, 6, 0, 234, 108, 253, 234, 29, 165, 67, 157, 15, 111, + 174, 243, 248, 207, 172, 69, 217, 220, 251, 85, 85, 184, 153, 126, 192, 253, 143, 167, + 105, 23, 73, 81, 110, 247, 117, 96, 6, 0, 69, 16, 0, 0, 7, 7, 255, 27, 3, 255, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, 0, 229, + 163, 167, 181, 216, 48, 194, 149, 59, 152, 83, 76, 108, 89, 163, 163, 79, 220, 52, 233, + 51, 247, 245, 137, 143, 10, 133, 207, 8, 132, 107, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 158, 42, 124, + 111, 148, 143, 23, 71, 78, 52, 167, 252, 67, 237, 3, 15, 124, 21, 99, 241, 186, 189, + 223, 99, 64, 200, 46, 14, 84, 168, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, + 251, 252, 166, 90, 191, 174, 148, 208, 101, 246, 199, 76, 45, 11, 45, 3, 58, 105, 166, + 8, 54, 250, 244, 81, 54, 176, 166, 141, 248, 70, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 2, 162, 97, 38, 185, + 42, 4, 183, 177, 168, 67, 207, 162, 237, 225, 183, 89, 224, 19, 4, 86, 108, 202, 197, + 108, 151, 18, 255, 142, 246, 86, 143, 11, 27, 240, 45, 133, 132, 182, 25, 177, 123, + 186, 98, 132, 113, 180, 86, 63, 3, 10, 8, 152, 75, 152, 211, 9, 149, 235, 147, 51, 209, + 203, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 93, 14, 0, 0, 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, + 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 69, 56, + 68, 67, 67, 66, 74, 97, 103, 65, 119, 73, 66, 65, 103, 73, 85, 89, 71, 99, 82, 76, 89, + 53, 43, 114, 99, 119, 67, 65, 88, 84, 72, 48, 71, 109, 83, 71, 98, 119, 115, 104, 113, + 119, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 99, + 68, 69, 105, 77, 67, 65, 71, 65, 49, 85, 69, 65, 119, 119, 90, 83, 87, 53, 48, 90, 87, + 119, 103, 85, 48, 100, 89, 73, 70, 66, 68, 83, 121, 66, 81, 98, 71, 70, 48, 90, 109, + 57, 121, 98, 83, 66, 68, 81, 84, 69, 97, 77, 66, 103, 71, 65, 49, 85, 69, 67, 103, 119, + 82, 10, 83, 87, 53, 48, 90, 87, 119, 103, 81, 50, 57, 121, 99, 71, 57, 121, 89, 88, 82, + 112, 98, 50, 52, 120, 70, 68, 65, 83, 66, 103, 78, 86, 66, 65, 99, 77, 67, 49, 78, 104, + 98, 110, 82, 104, 73, 69, 78, 115, 89, 88, 74, 104, 77, 81, 115, 119, 67, 81, 89, 68, + 86, 81, 81, 73, 10, 68, 65, 74, 68, 81, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, + 66, 104, 77, 67, 86, 86, 77, 119, 72, 104, 99, 78, 77, 106, 81, 120, 77, 106, 65, 49, + 77, 68, 65, 120, 79, 68, 85, 122, 87, 104, 99, 78, 77, 122, 69, 120, 77, 106, 65, 49, + 77, 68, 65, 120, 79, 68, 85, 122, 10, 87, 106, 66, 119, 77, 83, 73, 119, 73, 65, 89, + 68, 86, 81, 81, 68, 68, 66, 108, 74, 98, 110, 82, 108, 98, 67, 66, 84, 82, 49, 103, + 103, 85, 69, 78, 76, 73, 69, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, + 108, 77, 82, 111, 119, 71, 65, 89, 68, 86, 81, 81, 75, 10, 68, 66, 70, 74, 98, 110, 82, + 108, 98, 67, 66, 68, 98, 51, 74, 119, 98, 51, 74, 104, 100, 71, 108, 118, 98, 106, 69, + 85, 77, 66, 73, 71, 65, 49, 85, 69, 66, 119, 119, 76, 85, 50, 70, 117, 100, 71, 69, + 103, 81, 50, 120, 104, 99, 109, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 10, 66, 65, + 103, 77, 65, 107, 78, 66, 77, 81, 115, 119, 67, 81, 89, 68, 86, 81, 81, 71, 69, 119, + 74, 86, 85, 122, 66, 90, 77, 66, 77, 71, 66, 121, 113, 71, 83, 77, 52, 57, 65, 103, 69, + 71, 67, 67, 113, 71, 83, 77, 52, 57, 65, 119, 69, 72, 65, 48, 73, 65, 66, 79, 122, 117, + 10, 122, 56, 66, 78, 78, 88, 71, 102, 48, 48, 85, 66, 100, 53, 52, 90, 97, 120, 85, + 113, 79, 110, 79, 81, 103, 53, 81, 109, 51, 54, 115, 105, 49, 74, 111, 112, 97, 110, + 88, 114, 80, 112, 117, 78, 86, 102, 73, 98, 50, 77, 114, 54, 104, 75, 68, 106, 107, 75, + 107, 100, 114, 82, 81, 103, 10, 55, 101, 117, 57, 84, 73, 76, 86, 57, 53, 82, 43, 55, + 80, 118, 90, 102, 120, 101, 106, 103, 103, 77, 77, 77, 73, 73, 68, 67, 68, 65, 102, 66, + 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, 103, 66, 83, 86, 98, 49, 51, 78, 118, 82, + 118, 104, 54, 85, 66, 74, 121, 100, 84, 48, 10, 77, 56, 52, 66, 86, 119, 118, 101, 86, + 68, 66, 114, 66, 103, 78, 86, 72, 82, 56, 69, 90, 68, 66, 105, 77, 71, 67, 103, 88, + 113, 66, 99, 104, 108, 112, 111, 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 70, 119, + 97, 83, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, 86, 121, 10, 100, 109, 108, + 106, 90, 88, 77, 117, 97, 87, 53, 48, 90, 87, 119, 117, 89, 50, 57, 116, 76, 51, 78, + 110, 101, 67, 57, 106, 90, 88, 74, 48, 97, 87, 90, 112, 89, 50, 70, 48, 97, 87, 57, + 117, 76, 51, 89, 48, 76, 51, 66, 106, 97, 50, 78, 121, 98, 68, 57, 106, 89, 84, 49, + 119, 10, 98, 71, 70, 48, 90, 109, 57, 121, 98, 83, 90, 108, 98, 109, 78, 118, 90, 71, + 108, 117, 90, 122, 49, 107, 90, 88, 73, 119, 72, 81, 89, 68, 86, 82, 48, 79, 66, 66, + 89, 69, 70, 74, 97, 84, 121, 67, 67, 48, 49, 71, 76, 117, 52, 105, 65, 104, 100, 119, + 98, 111, 115, 84, 105, 112, 10, 75, 119, 90, 87, 77, 65, 52, 71, 65, 49, 85, 100, 68, + 119, 69, 66, 47, 119, 81, 69, 65, 119, 73, 71, 119, 68, 65, 77, 66, 103, 78, 86, 72, + 82, 77, 66, 65, 102, 56, 69, 65, 106, 65, 65, 77, 73, 73, 67, 79, 81, 89, 74, 75, 111, + 90, 73, 104, 118, 104, 78, 65, 81, 48, 66, 10, 66, 73, 73, 67, 75, 106, 67, 67, 65, + 105, 89, 119, 72, 103, 89, 75, 75, 111, 90, 73, 104, 118, 104, 78, 65, 81, 48, 66, 65, + 81, 81, 81, 78, 107, 122, 43, 57, 54, 111, 121, 99, 107, 71, 79, 47, 106, 70, 47, 56, + 72, 90, 56, 101, 84, 67, 67, 65, 87, 77, 71, 67, 105, 113, 71, 10, 83, 73, 98, 52, 84, + 81, 69, 78, 65, 81, 73, 119, 103, 103, 70, 84, 77, 66, 65, 71, 67, 121, 113, 71, 83, + 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 66, 65, 103, 69, 72, 77, 66, 65, 71, 67, 121, + 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 67, 65, 103, 69, 72, 10, 77, 66, + 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 68, 65, 103, 69, + 67, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 69, + 65, 103, 69, 67, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 10, + 65, 81, 73, 70, 65, 103, 69, 68, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, + 81, 69, 78, 65, 81, 73, 71, 65, 103, 69, 66, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, + 98, 52, 84, 81, 69, 78, 65, 81, 73, 72, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, + 71, 10, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 73, 65, 103, 69, 68, 77, 66, 65, + 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 74, 65, 103, 69, 65, + 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 75, 65, + 103, 69, 65, 10, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, + 81, 73, 76, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, + 69, 78, 65, 81, 73, 77, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, + 52, 84, 81, 69, 78, 10, 65, 81, 73, 78, 65, 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, + 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 79, 65, 103, 69, 65, 77, 66, 65, 71, + 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 80, 65, 103, 69, 65, 77, + 66, 65, 71, 67, 121, 113, 71, 10, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 73, 81, 65, + 103, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, + 73, 82, 65, 103, 69, 76, 77, 66, 56, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, + 78, 65, 81, 73, 83, 66, 66, 65, 72, 10, 66, 119, 73, 67, 65, 119, 69, 65, 65, 119, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 77, 66, 65, 71, 67, 105, 113, 71, 83, 73, 98, 52, + 84, 81, 69, 78, 65, 81, 77, 69, 65, 103, 65, 65, 77, 66, 81, 71, 67, 105, 113, 71, 83, + 73, 98, 52, 84, 81, 69, 78, 65, 81, 81, 69, 10, 66, 103, 67, 65, 98, 119, 85, 65, 65, + 68, 65, 80, 66, 103, 111, 113, 104, 107, 105, 71, 43, 69, 48, 66, 68, 81, 69, 70, 67, + 103, 69, 66, 77, 66, 52, 71, 67, 105, 113, 71, 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, + 89, 69, 69, 73, 89, 81, 78, 89, 69, 43, 108, 116, 89, 109, 10, 76, 85, 69, 101, 65, 74, + 83, 109, 67, 116, 65, 119, 82, 65, 89, 75, 75, 111, 90, 73, 104, 118, 104, 78, 65, 81, + 48, 66, 66, 122, 65, 50, 77, 66, 65, 71, 67, 121, 113, 71, 83, 73, 98, 52, 84, 81, 69, + 78, 65, 81, 99, 66, 65, 81, 72, 47, 77, 66, 65, 71, 67, 121, 113, 71, 10, 83, 73, 98, + 52, 84, 81, 69, 78, 65, 81, 99, 67, 65, 81, 69, 65, 77, 66, 65, 71, 67, 121, 113, 71, + 83, 73, 98, 52, 84, 81, 69, 78, 65, 81, 99, 68, 65, 81, 72, 47, 77, 65, 111, 71, 67, + 67, 113, 71, 83, 77, 52, 57, 66, 65, 77, 67, 65, 48, 103, 65, 77, 69, 85, 67, 10, 73, + 65, 74, 88, 103, 116, 73, 109, 88, 71, 51, 97, 104, 113, 81, 82, 122, 48, 90, 66, 43, + 66, 83, 107, 97, 70, 43, 88, 87, 110, 110, 119, 90, 49, 88, 102, 110, 106, 67, 79, 102, + 54, 115, 87, 65, 105, 69, 65, 115, 76, 57, 52, 110, 57, 53, 55, 86, 89, 65, 47, 118, + 109, 77, 119, 10, 103, 65, 56, 104, 78, 115, 112, 97, 66, 71, 80, 106, 99, 105, 50, 43, + 107, 104, 98, 120, 80, 112, 100, 43, 86, 100, 89, 61, 10, 45, 45, 45, 45, 45, 69, 78, + 68, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, 45, 45, + 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, + 45, 45, 10, 77, 73, 73, 67, 108, 106, 67, 67, 65, 106, 50, 103, 65, 119, 73, 66, 65, + 103, 73, 86, 65, 74, 86, 118, 88, 99, 50, 57, 71, 43, 72, 112, 81, 69, 110, 74, 49, 80, + 81, 122, 122, 103, 70, 88, 67, 57, 53, 85, 77, 65, 111, 71, 67, 67, 113, 71, 83, 77, + 52, 57, 66, 65, 77, 67, 10, 77, 71, 103, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, + 77, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 70, 78, 72, 87, 67, 66, 83, 98, 50, 57, + 48, 73, 69, 78, 66, 77, 82, 111, 119, 71, 65, 89, 68, 86, 81, 81, 75, 68, 66, 70, 74, + 98, 110, 82, 108, 98, 67, 66, 68, 10, 98, 51, 74, 119, 98, 51, 74, 104, 100, 71, 108, + 118, 98, 106, 69, 85, 77, 66, 73, 71, 65, 49, 85, 69, 66, 119, 119, 76, 85, 50, 70, + 117, 100, 71, 69, 103, 81, 50, 120, 104, 99, 109, 69, 120, 67, 122, 65, 74, 66, 103, + 78, 86, 66, 65, 103, 77, 65, 107, 78, 66, 77, 81, 115, 119, 10, 67, 81, 89, 68, 86, 81, + 81, 71, 69, 119, 74, 86, 85, 122, 65, 101, 70, 119, 48, 120, 79, 68, 65, 49, 77, 106, + 69, 120, 77, 68, 85, 119, 77, 84, 66, 97, 70, 119, 48, 122, 77, 122, 65, 49, 77, 106, + 69, 120, 77, 68, 85, 119, 77, 84, 66, 97, 77, 72, 65, 120, 73, 106, 65, 103, 10, 66, + 103, 78, 86, 66, 65, 77, 77, 71, 85, 108, 117, 100, 71, 86, 115, 73, 70, 78, 72, 87, + 67, 66, 81, 81, 48, 115, 103, 85, 71, 120, 104, 100, 71, 90, 118, 99, 109, 48, 103, 81, + 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, + 71, 86, 115, 10, 73, 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, + 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, + 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, + 119, 67, 81, 48, 69, 120, 10, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 89, 84, 65, + 108, 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, 122, 106, 48, 67, 65, + 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, 68, 81, 103, 65, 69, 78, 83, + 66, 47, 55, 116, 50, 49, 108, 88, 83, 79, 10, 50, 67, 117, 122, 112, 120, 119, 55, 52, + 101, 74, 66, 55, 50, 69, 121, 68, 71, 103, 87, 53, 114, 88, 67, 116, 120, 50, 116, 86, + 84, 76, 113, 54, 104, 75, 107, 54, 122, 43, 85, 105, 82, 90, 67, 110, 113, 82, 55, 112, + 115, 79, 118, 103, 113, 70, 101, 83, 120, 108, 109, 84, 108, 74, 108, 10, 101, 84, 109, + 105, 50, 87, 89, 122, 51, 113, 79, 66, 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, + 86, 72, 83, 77, 69, 71, 68, 65, 87, 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, + 105, 102, 79, 68, 116, 74, 86, 83, 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, + 10, 66, 103, 78, 86, 72, 82, 56, 69, 83, 122, 66, 74, 77, 69, 101, 103, 82, 97, 66, 68, + 104, 107, 70, 111, 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, + 112, 90, 109, 108, 106, 89, 88, 82, 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, + 86, 107, 99, 50, 86, 121, 10, 100, 109, 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 90, + 87, 119, 117, 89, 50, 57, 116, 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, + 109, 57, 118, 100, 69, 78, 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, + 81, 52, 69, 70, 103, 81, 85, 108, 87, 57, 100, 10, 122, 98, 48, 98, 52, 101, 108, 65, + 83, 99, 110, 85, 57, 68, 80, 79, 65, 86, 99, 76, 51, 108, 81, 119, 68, 103, 89, 68, 86, + 82, 48, 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, 65, 49, + 85, 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 10, 65, 102, 56, 67, 65, 81, + 65, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 68, 82, 119, + 65, 119, 82, 65, 73, 103, 88, 115, 86, 107, 105, 48, 119, 43, 105, 54, 86, 89, 71, 87, + 51, 85, 70, 47, 50, 50, 117, 97, 88, 101, 48, 89, 74, 68, 106, 49, 85, 101, 10, 110, + 65, 43, 84, 106, 68, 49, 97, 105, 53, 99, 67, 73, 67, 89, 98, 49, 83, 65, 109, 68, 53, + 120, 107, 102, 84, 86, 112, 118, 111, 52, 85, 111, 121, 105, 83, 89, 120, 114, 68, 87, + 76, 109, 85, 82, 52, 67, 73, 57, 78, 75, 121, 102, 80, 78, 43, 10, 45, 45, 45, 45, 45, + 69, 78, 68, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, + 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, + 45, 45, 45, 45, 10, 77, 73, 73, 67, 106, 122, 67, 67, 65, 106, 83, 103, 65, 119, 73, + 66, 65, 103, 73, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, + 86, 85, 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 67, 103, 89, 73, 75, 111, + 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, 103, 71, 65, + 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, + 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, + 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 10, 99, 110, 66, 118, + 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, + 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, + 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, 74, 10, 66, + 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, 77, 66, 52, 88, 68, 84, 69, 52, 77, 68, + 85, 121, 77, 84, 69, 119, 78, 68, 85, 120, 77, 70, 111, 88, 68, 84, 81, 53, 77, 84, 73, + 122, 77, 84, 73, 122, 78, 84, 107, 49, 79, 86, 111, 119, 97, 68, 69, 97, 77, 66, 103, + 71, 10, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, + 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, + 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 99, 110, + 66, 118, 99, 109, 70, 48, 10, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, + 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, + 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, + 74, 66, 103, 78, 86, 66, 65, 89, 84, 10, 65, 108, 86, 84, 77, 70, 107, 119, 69, 119, + 89, 72, 75, 111, 90, 73, 122, 106, 48, 67, 65, 81, 89, 73, 75, 111, 90, 73, 122, 106, + 48, 68, 65, 81, 99, 68, 81, 103, 65, 69, 67, 54, 110, 69, 119, 77, 68, 73, 89, 90, 79, + 106, 47, 105, 80, 87, 115, 67, 122, 97, 69, 75, 105, 55, 10, 49, 79, 105, 79, 83, 76, + 82, 70, 104, 87, 71, 106, 98, 110, 66, 86, 74, 102, 86, 110, 107, 89, 52, 117, 51, 73, + 106, 107, 68, 89, 89, 76, 48, 77, 120, 79, 52, 109, 113, 115, 121, 89, 106, 108, 66, + 97, 108, 84, 86, 89, 120, 70, 80, 50, 115, 74, 66, 75, 53, 122, 108, 75, 79, 66, 10, + 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, + 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, 86, 83, + 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, 66, 103, 78, 86, 72, 82, 56, 69, 83, + 122, 66, 74, 10, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, 100, 72, 82, 119, + 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, + 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, 86, 121, 100, 109, + 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 10, 90, 87, 119, 117, 89, 50, 57, 116, 76, + 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, 57, 118, 100, 69, 78, 66, 76, + 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, 81, 52, 69, 70, 103, 81, 85, 73, + 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 10, 85, 114, 57, 81, + 71, 122, 107, 110, 66, 113, 119, 119, 68, 103, 89, 68, 86, 82, 48, 80, 65, 81, 72, 47, + 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, 65, 49, 85, 100, 69, 119, 69, 66, 47, + 119, 81, 73, 77, 65, 89, 66, 65, 102, 56, 67, 65, 81, 69, 119, 67, 103, 89, 73, 10, 75, + 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 68, 83, 81, 65, 119, 82, 103, 73, 104, 65, + 79, 87, 47, 53, 81, 107, 82, 43, 83, 57, 67, 105, 83, 68, 99, 78, 111, 111, 119, 76, + 117, 80, 82, 76, 115, 87, 71, 102, 47, 89, 105, 55, 71, 83, 88, 57, 52, 66, 103, 119, + 84, 119, 103, 10, 65, 105, 69, 65, 52, 74, 48, 108, 114, 72, 111, 77, 115, 43, 88, 111, + 53, 111, 47, 115, 88, 54, 79, 57, 81, 87, 120, 72, 82, 65, 118, 90, 85, 71, 79, 100, + 82, 81, 55, 99, 118, 113, 82, 88, 97, 113, 73, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, + 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + let current_time = 1733406935; + + let collateral = Collateral { + major_version: 3, + minor_version: 0, + tee_type: 129, + pck_crl_issuer_chain: [ + 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, + 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 67, 108, 106, 67, 67, 65, 106, 50, 103, 65, + 119, 73, 66, 65, 103, 73, 86, 65, 74, 86, 118, 88, 99, 50, 57, 71, 43, 72, 112, 81, + 69, 110, 74, 49, 80, 81, 122, 122, 103, 70, 88, 67, 57, 53, 85, 77, 65, 111, 71, + 67, 67, 113, 71, 83, 77, 52, 57, 66, 65, 77, 67, 10, 77, 71, 103, 120, 71, 106, 65, + 89, 66, 103, 78, 86, 66, 65, 77, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 70, + 78, 72, 87, 67, 66, 83, 98, 50, 57, 48, 73, 69, 78, 66, 77, 82, 111, 119, 71, 65, + 89, 68, 86, 81, 81, 75, 68, 66, 70, 74, 98, 110, 82, 108, 98, 67, 66, 68, 10, 98, + 51, 74, 119, 98, 51, 74, 104, 100, 71, 108, 118, 98, 106, 69, 85, 77, 66, 73, 71, + 65, 49, 85, 69, 66, 119, 119, 76, 85, 50, 70, 117, 100, 71, 69, 103, 81, 50, 120, + 104, 99, 109, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 103, 77, 65, 107, + 78, 66, 77, 81, 115, 119, 10, 67, 81, 89, 68, 86, 81, 81, 71, 69, 119, 74, 86, 85, + 122, 65, 101, 70, 119, 48, 120, 79, 68, 65, 49, 77, 106, 69, 120, 77, 68, 85, 119, + 77, 84, 66, 97, 70, 119, 48, 122, 77, 122, 65, 49, 77, 106, 69, 120, 77, 68, 85, + 119, 77, 84, 66, 97, 77, 72, 65, 120, 73, 106, 65, 103, 10, 66, 103, 78, 86, 66, + 65, 77, 77, 71, 85, 108, 117, 100, 71, 86, 115, 73, 70, 78, 72, 87, 67, 66, 81, 81, + 48, 115, 103, 85, 71, 120, 104, 100, 71, 90, 118, 99, 109, 48, 103, 81, 48, 69, + 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, + 86, 115, 10, 73, 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, + 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, + 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, + 67, 65, 119, 67, 81, 48, 69, 120, 10, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 89, + 84, 65, 108, 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, 122, 106, + 48, 67, 65, 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, 68, 81, 103, + 65, 69, 78, 83, 66, 47, 55, 116, 50, 49, 108, 88, 83, 79, 10, 50, 67, 117, 122, + 112, 120, 119, 55, 52, 101, 74, 66, 55, 50, 69, 121, 68, 71, 103, 87, 53, 114, 88, + 67, 116, 120, 50, 116, 86, 84, 76, 113, 54, 104, 75, 107, 54, 122, 43, 85, 105, 82, + 90, 67, 110, 113, 82, 55, 112, 115, 79, 118, 103, 113, 70, 101, 83, 120, 108, 109, + 84, 108, 74, 108, 10, 101, 84, 109, 105, 50, 87, 89, 122, 51, 113, 79, 66, 117, + 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, + 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, 86, + 83, 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, 10, 66, 103, 78, 86, 72, 82, + 56, 69, 83, 122, 66, 74, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, 100, + 72, 82, 119, 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, 108, + 106, 89, 88, 82, 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, + 86, 121, 10, 100, 109, 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 90, 87, 119, 117, + 89, 50, 57, 116, 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, 57, + 118, 100, 69, 78, 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, 81, + 52, 69, 70, 103, 81, 85, 108, 87, 57, 100, 10, 122, 98, 48, 98, 52, 101, 108, 65, + 83, 99, 110, 85, 57, 68, 80, 79, 65, 86, 99, 76, 51, 108, 81, 119, 68, 103, 89, 68, + 86, 82, 48, 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, + 65, 49, 85, 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 10, 65, 102, 56, + 67, 65, 81, 65, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, + 73, 68, 82, 119, 65, 119, 82, 65, 73, 103, 88, 115, 86, 107, 105, 48, 119, 43, 105, + 54, 86, 89, 71, 87, 51, 85, 70, 47, 50, 50, 117, 97, 88, 101, 48, 89, 74, 68, 106, + 49, 85, 101, 10, 110, 65, 43, 84, 106, 68, 49, 97, 105, 53, 99, 67, 73, 67, 89, 98, + 49, 83, 65, 109, 68, 53, 120, 107, 102, 84, 86, 112, 118, 111, 52, 85, 111, 121, + 105, 83, 89, 120, 114, 68, 87, 76, 109, 85, 82, 52, 67, 73, 57, 78, 75, 121, 102, + 80, 78, 43, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 67, 69, 82, 84, 73, 70, 73, 67, + 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, + 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 67, + 106, 122, 67, 67, 65, 106, 83, 103, 65, 119, 73, 66, 65, 103, 73, 85, 73, 109, 85, + 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 85, 114, 57, 81, 71, 122, + 107, 110, 66, 113, 119, 119, 67, 103, 89, 73, 75, 111, 90, 73, 122, 106, 48, 69, + 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, 103, 71, 65, 49, 85, 69, 65, 119, + 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, 70, 74, 118, 98, + 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, + 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 10, 99, 110, 66, 118, 99, 109, + 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, + 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, + 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, 74, 10, 66, + 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, 77, 66, 52, 88, 68, 84, 69, 52, 77, + 68, 85, 121, 77, 84, 69, 119, 78, 68, 85, 120, 77, 70, 111, 88, 68, 84, 81, 53, 77, + 84, 73, 122, 77, 84, 73, 122, 78, 84, 107, 49, 79, 86, 111, 119, 97, 68, 69, 97, + 77, 66, 103, 71, 10, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, + 103, 85, 48, 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, + 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, + 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 10, 97, 87, 57, 117, 77, 82, 81, + 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, + 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, + 67, 81, 48, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 89, 84, 10, 65, 108, + 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, 122, 106, 48, 67, 65, + 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, 68, 81, 103, 65, 69, 67, + 54, 110, 69, 119, 77, 68, 73, 89, 90, 79, 106, 47, 105, 80, 87, 115, 67, 122, 97, + 69, 75, 105, 55, 10, 49, 79, 105, 79, 83, 76, 82, 70, 104, 87, 71, 106, 98, 110, + 66, 86, 74, 102, 86, 110, 107, 89, 52, 117, 51, 73, 106, 107, 68, 89, 89, 76, 48, + 77, 120, 79, 52, 109, 113, 115, 121, 89, 106, 108, 66, 97, 108, 84, 86, 89, 120, + 70, 80, 50, 115, 74, 66, 75, 53, 122, 108, 75, 79, 66, 10, 117, 122, 67, 66, 117, + 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, 87, 103, 66, 81, 105, 90, + 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, 86, 83, 118, 49, 65, 98, + 79, 83, 99, 71, 114, 68, 66, 83, 66, 103, 78, 86, 72, 82, 56, 69, 83, 122, 66, 74, + 10, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, 100, 72, 82, 119, 99, 122, + 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, 108, + 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, 50, 86, 121, 100, 109, + 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 10, 90, 87, 119, 117, 89, 50, 57, 116, + 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, 57, 118, 100, 69, 78, + 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, 81, 52, 69, 70, 103, + 81, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 10, + 85, 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 68, 103, 89, 68, 86, 82, 48, + 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, 66, 73, 71, 65, 49, 85, + 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 65, 102, 56, 67, 65, 81, 69, + 119, 67, 103, 89, 73, 10, 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 68, 83, + 81, 65, 119, 82, 103, 73, 104, 65, 79, 87, 47, 53, 81, 107, 82, 43, 83, 57, 67, + 105, 83, 68, 99, 78, 111, 111, 119, 76, 117, 80, 82, 76, 115, 87, 71, 102, 47, 89, + 105, 55, 71, 83, 88, 57, 52, 66, 103, 119, 84, 119, 103, 10, 65, 105, 69, 65, 52, + 74, 48, 108, 114, 72, 111, 77, 115, 43, 88, 111, 53, 111, 47, 115, 88, 54, 79, 57, + 81, 87, 120, 72, 82, 65, 118, 90, 85, 71, 79, 100, 82, 81, 55, 99, 118, 113, 82, + 88, 97, 113, 73, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 67, 69, 82, 84, 73, + 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 0, + ] + .into(), + root_ca_crl: [ + 48, 130, 1, 34, 48, 129, 200, 2, 1, 1, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, + 48, 104, 49, 26, 48, 24, 6, 3, 85, 4, 3, 12, 17, 73, 110, 116, 101, 108, 32, 83, + 71, 88, 32, 82, 111, 111, 116, 32, 67, 65, 49, 26, 48, 24, 6, 3, 85, 4, 10, 12, 17, + 73, 110, 116, 101, 108, 32, 67, 111, 114, 112, 111, 114, 97, 116, 105, 111, 110, + 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 83, 97, 110, 116, 97, 32, 67, 108, 97, 114, + 97, 49, 11, 48, 9, 6, 3, 85, 4, 8, 12, 2, 67, 65, 49, 11, 48, 9, 6, 3, 85, 4, 6, + 19, 2, 85, 83, 23, 13, 50, 52, 48, 51, 50, 48, 49, 57, 49, 57, 51, 48, 90, 23, 13, + 50, 53, 48, 52, 48, 51, 49, 57, 49, 57, 51, 48, 90, 160, 47, 48, 45, 48, 10, 6, 3, + 85, 29, 20, 4, 3, 2, 1, 1, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, 128, 20, 34, + 101, 12, 214, 90, 157, 52, 137, 243, 131, 180, 149, 82, 191, 80, 27, 57, 39, 6, + 172, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 3, 73, 0, 48, 70, 2, 33, 0, 231, + 96, 111, 239, 45, 166, 135, 133, 160, 195, 155, 195, 74, 195, 68, 201, 226, 214, + 237, 75, 2, 35, 231, 154, 108, 98, 151, 212, 33, 183, 55, 132, 2, 33, 0, 252, 21, + 135, 174, 206, 66, 150, 213, 233, 55, 15, 214, 164, 68, 167, 45, 3, 197, 152, 203, + 33, 220, 129, 4, 197, 91, 18, 123, 118, 110, 168, 43, 0, + ] + .into(), + pck_crl: [ + 48, 130, 10, 98, 48, 130, 10, 8, 2, 1, 1, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, + 2, 48, 112, 49, 34, 48, 32, 6, 3, 85, 4, 3, 12, 25, 73, 110, 116, 101, 108, 32, 83, + 71, 88, 32, 80, 67, 75, 32, 80, 108, 97, 116, 102, 111, 114, 109, 32, 67, 65, 49, + 26, 48, 24, 6, 3, 85, 4, 10, 12, 17, 73, 110, 116, 101, 108, 32, 67, 111, 114, 112, + 111, 114, 97, 116, 105, 111, 110, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 83, 97, + 110, 116, 97, 32, 67, 108, 97, 114, 97, 49, 11, 48, 9, 6, 3, 85, 4, 8, 12, 2, 67, + 65, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 23, 13, 50, 52, 49, 49, 50, 56, + 49, 51, 50, 51, 48, 57, 90, 23, 13, 50, 52, 49, 50, 50, 56, 49, 51, 50, 51, 48, 57, + 90, 48, 130, 9, 52, 48, 51, 2, 20, 111, 195, 78, 80, 35, 231, 40, 146, 52, 53, 214, + 26, 164, 184, 60, 97, 129, 102, 173, 53, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, + 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, + 21, 0, 239, 174, 110, 151, 21, 252, 161, 59, 135, 227, 51, 232, 38, 30, 214, 217, + 144, 169, 38, 173, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, + 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 253, 96, 134, 72, + 98, 156, 186, 115, 7, 139, 77, 73, 47, 75, 62, 167, 65, 173, 8, 205, 23, 13, 50, + 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, + 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 138, 249, 36, 24, 78, 29, 90, 253, 221, 115, 195, + 214, 58, 18, 245, 232, 181, 115, 126, 86, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, + 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, + 21, 0, 177, 37, 121, 120, 207, 169, 204, 221, 7, 89, 171, 248, 197, 202, 114, 250, + 227, 167, 138, 155, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, + 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 116, 254, 166, 20, + 169, 114, 190, 14, 40, 67, 242, 5, 152, 53, 129, 30, 216, 114, 249, 179, 23, 13, + 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, + 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 249, 196, 239, 86, 179, 171, 72, 213, 119, + 225, 8, 186, 237, 244, 191, 136, 1, 66, 20, 185, 23, 13, 50, 52, 49, 49, 50, 56, + 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, + 51, 2, 20, 7, 29, 224, 119, 143, 158, 95, 196, 242, 135, 143, 48, 214, 176, 124, + 154, 48, 230, 179, 11, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, + 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 205, 226, 66, + 79, 151, 44, 234, 148, 255, 35, 153, 55, 244, 216, 12, 37, 2, 157, 214, 11, 23, 13, + 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, + 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 108, 51, 25, 229, 16, 155, 100, 80, 125, 60, + 241, 19, 44, 224, 3, 73, 239, 82, 115, 25, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, + 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, + 21, 0, 223, 8, 215, 86, 182, 106, 116, 151, 244, 59, 91, 181, 138, 218, 4, 211, + 244, 247, 169, 55, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, + 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 40, 175, 72, 91, 108, + 246, 126, 64, 154, 57, 213, 203, 90, 238, 69, 152, 247, 168, 250, 123, 23, 13, 50, + 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, + 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 251, 139, 45, 174, 192, 146, 202, 218, 138, 169, + 188, 79, 242, 241, 194, 13, 3, 70, 102, 140, 23, 13, 50, 52, 49, 49, 50, 56, 49, + 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, + 2, 21, 0, 205, 72, 80, 172, 82, 189, 204, 105, 166, 166, 240, 88, 200, 188, 87, + 187, 208, 181, 248, 100, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, + 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 153, 77, + 211, 102, 111, 82, 117, 251, 128, 95, 149, 221, 2, 189, 80, 203, 38, 121, 216, 173, + 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, + 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 7, 2, 19, 105, 0, 37, 34, 116, 217, 3, + 94, 237, 245, 69, 116, 98, 250, 208, 239, 76, 23, 13, 50, 52, 49, 49, 50, 56, 49, + 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, + 2, 20, 97, 242, 191, 115, 227, 155, 78, 4, 170, 39, 216, 1, 189, 115, 210, 67, 25, + 181, 191, 128, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, + 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 57, 146, 190, 133, 27, + 150, 144, 46, 255, 56, 149, 158, 108, 46, 255, 27, 6, 81, 164, 181, 23, 13, 50, 52, + 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, + 10, 1, 1, 48, 51, 2, 20, 99, 159, 19, 154, 80, 64, 253, 207, 241, 145, 232, 164, + 251, 27, 240, 134, 237, 96, 57, 113, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, + 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, + 149, 157, 83, 63, 146, 73, 220, 30, 81, 53, 68, 205, 200, 48, 191, 25, 183, 241, + 243, 1, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, + 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 15, 218, 67, 160, 11, 104, 234, + 121, 183, 194, 222, 174, 172, 11, 73, 139, 223, 178, 175, 144, 23, 13, 50, 52, 49, + 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, + 1, 1, 48, 52, 2, 21, 0, 157, 103, 117, 59, 129, 228, 112, 144, 174, 167, 99, 251, + 236, 76, 69, 73, 188, 219, 153, 51, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, + 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 52, + 191, 187, 122, 29, 156, 86, 129, 71, 225, 24, 182, 20, 247, 183, 110, 211, 239, + 104, 223, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, + 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 133, 211, 201, 56, 27, 119, + 167, 224, 77, 17, 156, 158, 90, 214, 116, 159, 243, 255, 171, 135, 23, 13, 50, 52, + 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, + 10, 1, 1, 48, 52, 2, 21, 0, 147, 136, 124, 164, 65, 30, 122, 146, 59, 209, 254, + 210, 129, 155, 41, 73, 242, 1, 181, 180, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, + 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, + 20, 36, 152, 220, 98, 131, 147, 9, 150, 253, 139, 242, 58, 55, 172, 190, 38, 163, + 190, 212, 87, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, + 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 138, 102, 241, 167, 73, + 72, 134, 103, 104, 156, 195, 144, 58, 197, 76, 102, 43, 113, 46, 115, 23, 13, 50, + 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, + 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 175, 193, 54, 16, 189, 211, 108, 183, 152, 93, + 16, 100, 129, 168, 128, 211, 160, 31, 218, 7, 23, 13, 50, 52, 49, 49, 50, 56, 49, + 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, + 2, 21, 0, 239, 224, 75, 44, 51, 208, 54, 170, 201, 108, 166, 115, 191, 30, 154, 71, + 182, 77, 92, 187, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, + 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 131, 217, 172, 141, + 139, 181, 9, 209, 198, 200, 9, 173, 113, 46, 132, 48, 85, 158, 215, 243, 23, 13, + 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, + 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 121, 49, 253, 80, 181, 7, 28, 27, 191, 197, 183, + 182, 222, 216, 180, 91, 157, 139, 133, 41, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, + 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, + 20, 31, 162, 14, 41, 112, 189, 229, 213, 127, 123, 141, 223, 131, 57, 72, 78, 31, + 29, 8, 35, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, + 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 30, 135, 178, 195, 179, 45, + 141, 35, 228, 17, 206, 243, 65, 151, 185, 90, 240, 200, 173, 245, 23, 13, 50, 52, + 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, + 10, 1, 1, 48, 52, 2, 21, 0, 154, 253, 46, 233, 10, 71, 53, 80, 161, 103, 217, 150, + 145, 20, 55, 199, 80, 45, 31, 9, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, + 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 68, + 129, 176, 241, 23, 40, 161, 59, 105, 109, 62, 169, 199, 112, 160, 177, 94, 197, + 141, 218, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, + 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 167, 133, 159, 87, 152, 46, + 240, 230, 125, 55, 188, 142, 242, 239, 90, 200, 53, 255, 26, 169, 23, 13, 50, 52, + 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, + 10, 1, 1, 48, 51, 2, 20, 122, 227, 119, 72, 169, 249, 18, 244, 198, 59, 167, 171, + 7, 197, 147, 206, 29, 29, 17, 129, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, + 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 19, + 136, 75, 51, 38, 153, 56, 193, 149, 170, 23, 15, 202, 117, 218, 23, 117, 56, 223, + 11, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, + 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 51, 2, 20, 44, 60, 198, 254, 146, 121, 219, 21, + 22, 213, 206, 57, 242, 168, 152, 205, 165, 161, 117, 225, 23, 13, 50, 52, 49, 49, + 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, + 1, 48, 51, 2, 20, 113, 121, 72, 104, 117, 9, 35, 75, 233, 121, 228, 183, 220, 230, + 243, 27, 239, 100, 182, 140, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, + 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 157, + 118, 239, 44, 57, 193, 54, 232, 101, 139, 110, 115, 150, 177, 215, 68, 90, 39, 99, + 31, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, + 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 195, 224, 37, 252, 169, 149, 243, + 111, 89, 180, 132, 103, 147, 158, 62, 52, 230, 54, 26, 111, 23, 13, 50, 52, 49, 49, + 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, + 1, 48, 52, 2, 21, 0, 140, 95, 107, 50, 87, 218, 5, 177, 116, 41, 226, 230, 27, 169, + 101, 214, 115, 48, 96, 106, 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, + 90, 48, 12, 48, 10, 6, 3, 85, 29, 21, 4, 3, 10, 1, 1, 48, 52, 2, 21, 0, 161, 124, + 81, 114, 46, 193, 224, 195, 39, 143, 232, 189, 240, 82, 5, 156, 190, 196, 230, 72, + 23, 13, 50, 52, 49, 49, 50, 56, 49, 51, 50, 51, 48, 57, 90, 48, 12, 48, 10, 6, 3, + 85, 29, 21, 4, 3, 10, 1, 1, 160, 47, 48, 45, 48, 10, 6, 3, 85, 29, 20, 4, 3, 2, 1, + 1, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, 128, 20, 149, 111, 93, 205, 189, 27, + 225, 233, 64, 73, 201, 212, 244, 51, 206, 1, 87, 11, 222, 84, 48, 10, 6, 8, 42, + 134, 72, 206, 61, 4, 3, 2, 3, 72, 0, 48, 69, 2, 32, 113, 129, 124, 253, 92, 251, + 24, 95, 180, 138, 106, 178, 57, 160, 79, 139, 90, 150, 233, 44, 243, 233, 219, 83, + 166, 120, 8, 167, 21, 168, 54, 112, 2, 33, 0, 241, 73, 163, 143, 77, 129, 88, 187, + 205, 46, 218, 183, 36, 140, 195, 164, 84, 101, 227, 217, 121, 192, 245, 25, 116, + 182, 208, 150, 186, 234, 175, 136, 0, + ] + .into(), + tcb_info_issuer_chain: [ + 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, + 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 67, 105, 122, 67, 67, 65, 106, 75, 103, 65, + 119, 73, 66, 65, 103, 73, 85, 102, 106, 105, 67, 49, 102, 116, 86, 75, 85, 112, 65, + 83, 89, 53, 70, 104, 65, 80, 112, 70, 74, 71, 57, 57, 70, 85, 119, 67, 103, 89, 73, + 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, + 103, 71, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, + 48, 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, + 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, + 118, 10, 99, 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, + 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, + 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, + 48, 69, 120, 67, 122, 65, 74, 10, 66, 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, + 77, 66, 52, 88, 68, 84, 69, 52, 77, 68, 85, 121, 77, 84, 69, 119, 78, 84, 65, 120, + 77, 70, 111, 88, 68, 84, 73, 49, 77, 68, 85, 121, 77, 84, 69, 119, 78, 84, 65, 120, + 77, 70, 111, 119, 98, 68, 69, 101, 77, 66, 119, 71, 10, 65, 49, 85, 69, 65, 119, + 119, 86, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, 70, 82, 68, 81, + 105, 66, 84, 97, 87, 100, 117, 97, 87, 53, 110, 77, 82, 111, 119, 71, 65, 89, 68, + 86, 81, 81, 75, 68, 66, 70, 74, 98, 110, 82, 108, 98, 67, 66, 68, 98, 51, 74, 119, + 10, 98, 51, 74, 104, 100, 71, 108, 118, 98, 106, 69, 85, 77, 66, 73, 71, 65, 49, + 85, 69, 66, 119, 119, 76, 85, 50, 70, 117, 100, 71, 69, 103, 81, 50, 120, 104, 99, + 109, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 103, 77, 65, 107, 78, 66, + 77, 81, 115, 119, 67, 81, 89, 68, 10, 86, 81, 81, 71, 69, 119, 74, 86, 85, 122, 66, + 90, 77, 66, 77, 71, 66, 121, 113, 71, 83, 77, 52, 57, 65, 103, 69, 71, 67, 67, 113, + 71, 83, 77, 52, 57, 65, 119, 69, 72, 65, 48, 73, 65, 66, 69, 78, 70, 71, 56, 120, + 122, 121, 100, 87, 82, 102, 75, 57, 50, 98, 109, 71, 118, 10, 80, 43, 109, 65, 104, + 57, 49, 80, 69, 121, 86, 55, 74, 104, 54, 70, 71, 74, 100, 53, 110, 100, 69, 57, + 97, 66, 72, 55, 82, 51, 69, 52, 65, 55, 117, 98, 114, 108, 104, 47, 122, 78, 51, + 67, 52, 120, 118, 112, 111, 111, 117, 71, 108, 105, 114, 77, 98, 97, 43, 87, 50, + 108, 106, 117, 10, 121, 112, 97, 106, 103, 98, 85, 119, 103, 98, 73, 119, 72, 119, + 89, 68, 86, 82, 48, 106, 66, 66, 103, 119, 70, 111, 65, 85, 73, 109, 85, 77, 49, + 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 85, 114, 57, 81, 71, 122, 107, + 110, 66, 113, 119, 119, 85, 103, 89, 68, 86, 82, 48, 102, 10, 66, 69, 115, 119, 83, + 84, 66, 72, 111, 69, 87, 103, 81, 52, 90, 66, 97, 72, 82, 48, 99, 72, 77, 54, 76, + 121, 57, 106, 90, 88, 74, 48, 97, 87, 90, 112, 89, 50, 70, 48, 90, 88, 77, 117, + 100, 72, 74, 49, 99, 51, 82, 108, 90, 72, 78, 108, 99, 110, 90, 112, 89, 50, 86, + 122, 10, 76, 109, 108, 117, 100, 71, 86, 115, 76, 109, 78, 118, 98, 83, 57, 74, 98, + 110, 82, 108, 98, 70, 78, 72, 87, 70, 74, 118, 98, 51, 82, 68, 81, 83, 53, 107, 90, + 88, 73, 119, 72, 81, 89, 68, 86, 82, 48, 79, 66, 66, 89, 69, 70, 72, 52, 52, 103, + 116, 88, 55, 86, 83, 108, 75, 10, 81, 69, 109, 79, 82, 89, 81, 68, 54, 82, 83, 82, + 118, 102, 82, 86, 77, 65, 52, 71, 65, 49, 85, 100, 68, 119, 69, 66, 47, 119, 81, + 69, 65, 119, 73, 71, 119, 68, 65, 77, 66, 103, 78, 86, 72, 82, 77, 66, 65, 102, 56, + 69, 65, 106, 65, 65, 77, 65, 111, 71, 67, 67, 113, 71, 10, 83, 77, 52, 57, 66, 65, + 77, 67, 65, 48, 99, 65, 77, 69, 81, 67, 73, 66, 57, 67, 56, 119, 79, 65, 78, 47, + 73, 109, 120, 68, 116, 71, 65, 67, 86, 50, 52, 54, 75, 99, 113, 106, 97, 103, 90, + 79, 82, 48, 107, 121, 99, 116, 121, 66, 114, 115, 71, 71, 74, 86, 65, 105, 65, 106, + 10, 102, 116, 98, 114, 78, 71, 115, 71, 85, 56, 89, 72, 50, 49, 49, 100, 82, 105, + 89, 78, 111, 80, 80, 117, 49, 57, 90, 112, 47, 122, 101, 56, 74, 109, 104, 117, + 106, 66, 48, 111, 66, 119, 61, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 67, 69, + 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, 45, 45, 45, 45, 66, + 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, + 10, 77, 73, 73, 67, 106, 122, 67, 67, 65, 106, 83, 103, 65, 119, 73, 66, 65, 103, + 73, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 85, + 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 67, 103, 89, 73, 75, 111, 90, + 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, 103, 71, 65, + 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, + 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, + 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 10, 99, + 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, + 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, + 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, + 67, 122, 65, 74, 10, 66, 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, 77, 66, 52, + 88, 68, 84, 69, 52, 77, 68, 85, 121, 77, 84, 69, 119, 78, 68, 85, 120, 77, 70, 111, + 88, 68, 84, 81, 53, 77, 84, 73, 122, 77, 84, 73, 122, 78, 84, 107, 49, 79, 86, 111, + 119, 97, 68, 69, 97, 77, 66, 103, 71, 10, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, + 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, + 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, + 100, 71, 86, 115, 73, 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 10, 97, 87, + 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, + 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, + 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, + 89, 84, 10, 65, 108, 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, + 122, 106, 48, 67, 65, 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, + 68, 81, 103, 65, 69, 67, 54, 110, 69, 119, 77, 68, 73, 89, 90, 79, 106, 47, 105, + 80, 87, 115, 67, 122, 97, 69, 75, 105, 55, 10, 49, 79, 105, 79, 83, 76, 82, 70, + 104, 87, 71, 106, 98, 110, 66, 86, 74, 102, 86, 110, 107, 89, 52, 117, 51, 73, 106, + 107, 68, 89, 89, 76, 48, 77, 120, 79, 52, 109, 113, 115, 121, 89, 106, 108, 66, 97, + 108, 84, 86, 89, 120, 70, 80, 50, 115, 74, 66, 75, 53, 122, 108, 75, 79, 66, 10, + 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, + 87, 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, + 86, 83, 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, 66, 103, 78, 86, 72, 82, + 56, 69, 83, 122, 66, 74, 10, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, + 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, + 108, 106, 89, 88, 82, 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, + 50, 86, 121, 100, 109, 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 10, 90, 87, 119, + 117, 89, 50, 57, 116, 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, + 57, 118, 100, 69, 78, 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, + 81, 52, 69, 70, 103, 81, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, + 103, 55, 83, 86, 10, 85, 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 68, + 103, 89, 68, 86, 82, 48, 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, + 66, 73, 71, 65, 49, 85, 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 65, + 102, 56, 67, 65, 81, 69, 119, 67, 103, 89, 73, 10, 75, 111, 90, 73, 122, 106, 48, + 69, 65, 119, 73, 68, 83, 81, 65, 119, 82, 103, 73, 104, 65, 79, 87, 47, 53, 81, + 107, 82, 43, 83, 57, 67, 105, 83, 68, 99, 78, 111, 111, 119, 76, 117, 80, 82, 76, + 115, 87, 71, 102, 47, 89, 105, 55, 71, 83, 88, 57, 52, 66, 103, 119, 84, 119, 103, + 10, 65, 105, 69, 65, 52, 74, 48, 108, 114, 72, 111, 77, 115, 43, 88, 111, 53, 111, + 47, 115, 88, 54, 79, 57, 81, 87, 120, 72, 82, 65, 118, 90, 85, 71, 79, 100, 82, 81, + 55, 99, 118, 113, 82, 88, 97, 113, 73, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, + 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 0, + ] + .into(), + tcb_info: [ + 123, 34, 116, 99, 98, 73, 110, 102, 111, 34, 58, 123, 34, 105, 100, 34, 58, 34, 84, + 68, 88, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 51, 44, 34, 105, + 115, 115, 117, 101, 68, 97, 116, 101, 34, 58, 34, 50, 48, 50, 52, 45, 49, 50, 45, + 48, 53, 84, 49, 50, 58, 49, 54, 58, 53, 50, 90, 34, 44, 34, 110, 101, 120, 116, 85, + 112, 100, 97, 116, 101, 34, 58, 34, 50, 48, 50, 53, 45, 48, 49, 45, 48, 52, 84, 49, + 50, 58, 49, 54, 58, 53, 50, 90, 34, 44, 34, 102, 109, 115, 112, 99, 34, 58, 34, 48, + 48, 56, 48, 54, 70, 48, 53, 48, 48, 48, 48, 34, 44, 34, 112, 99, 101, 73, 100, 34, + 58, 34, 48, 48, 48, 48, 34, 44, 34, 116, 99, 98, 84, 121, 112, 101, 34, 58, 48, 44, + 34, 116, 99, 98, 69, 118, 97, 108, 117, 97, 116, 105, 111, 110, 68, 97, 116, 97, + 78, 117, 109, 98, 101, 114, 34, 58, 49, 55, 44, 34, 116, 100, 120, 77, 111, 100, + 117, 108, 101, 34, 58, 123, 34, 109, 114, 115, 105, 103, 110, 101, 114, 34, 58, 34, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 97, 116, 116, 114, 105, + 98, 117, 116, 101, 115, 34, 58, 34, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 34, 44, 34, 97, 116, 116, 114, 105, 98, 117, 116, 101, 115, 77, 97, + 115, 107, 34, 58, 34, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 34, 125, 44, 34, 116, 100, 120, 77, 111, 100, 117, 108, 101, 73, 100, 101, 110, + 116, 105, 116, 105, 101, 115, 34, 58, 91, 123, 34, 105, 100, 34, 58, 34, 84, 68, + 88, 95, 48, 51, 34, 44, 34, 109, 114, 115, 105, 103, 110, 101, 114, 34, 58, 34, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 97, 116, 116, 114, 105, 98, + 117, 116, 101, 115, 34, 58, 34, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 34, 44, 34, 97, 116, 116, 114, 105, 98, 117, 116, 101, 115, 77, 97, + 115, 107, 34, 58, 34, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 34, 44, 34, 116, 99, 98, 76, 101, 118, 101, 108, 115, 34, 58, 91, 123, 34, 116, + 99, 98, 34, 58, 123, 34, 105, 115, 118, 115, 118, 110, 34, 58, 51, 125, 44, 34, + 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, 48, 50, 52, 45, 48, 51, 45, 49, 51, + 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, 34, 116, 99, 98, 83, 116, 97, 116, + 117, 115, 34, 58, 34, 85, 112, 84, 111, 68, 97, 116, 101, 34, 125, 93, 125, 44, + 123, 34, 105, 100, 34, 58, 34, 84, 68, 88, 95, 48, 49, 34, 44, 34, 109, 114, 115, + 105, 103, 110, 101, 114, 34, 58, 34, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 34, 44, 34, 97, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 58, 34, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 97, 116, 116, + 114, 105, 98, 117, 116, 101, 115, 77, 97, 115, 107, 34, 58, 34, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 34, 44, 34, 116, 99, 98, 76, 101, 118, + 101, 108, 115, 34, 58, 91, 123, 34, 116, 99, 98, 34, 58, 123, 34, 105, 115, 118, + 115, 118, 110, 34, 58, 52, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, + 50, 48, 50, 52, 45, 48, 51, 45, 49, 51, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, + 44, 34, 116, 99, 98, 83, 116, 97, 116, 117, 115, 34, 58, 34, 85, 112, 84, 111, 68, + 97, 116, 101, 34, 125, 44, 123, 34, 116, 99, 98, 34, 58, 123, 34, 105, 115, 118, + 115, 118, 110, 34, 58, 50, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, + 50, 48, 50, 51, 45, 48, 56, 45, 48, 57, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, + 44, 34, 116, 99, 98, 83, 116, 97, 116, 117, 115, 34, 58, 34, 79, 117, 116, 79, 102, + 68, 97, 116, 101, 34, 125, 93, 125, 93, 44, 34, 116, 99, 98, 76, 101, 118, 101, + 108, 115, 34, 58, 91, 123, 34, 116, 99, 98, 34, 58, 123, 34, 115, 103, 120, 116, + 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, 58, 91, 123, 34, 115, + 118, 110, 34, 58, 55, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, + 73, 79, 83, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 69, 97, 114, 108, 121, 32, + 77, 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 55, 44, 34, 99, 97, 116, 101, 103, 111, 114, + 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, + 34, 83, 71, 88, 32, 76, 97, 116, 101, 32, 77, 105, 99, 114, 111, 99, 111, 100, 101, + 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 50, + 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, + 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 88, 84, 32, 83, 73, 78, 73, 84, 34, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 50, 44, 34, 99, 97, 116, 101, 103, 111, + 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 51, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 49, 44, 34, 99, 97, 116, 101, 103, 111, + 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, + 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, + 34, 58, 34, 83, 69, 65, 77, 76, 68, 82, 32, 65, 67, 77, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 93, 44, 34, 112, 99, 101, 115, 118, 110, 34, 58, 49, 49, 44, 34, + 116, 100, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, + 58, 91, 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, 99, 97, 116, 101, 103, 111, + 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, + 58, 34, 84, 68, 88, 32, 77, 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, + 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 77, + 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 55, 44, 34, + 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, + 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 76, 97, 116, 101, 32, 77, 105, + 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 93, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, 48, 50, + 52, 45, 48, 51, 45, 49, 51, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, 34, + 116, 99, 98, 83, 116, 97, 116, 117, 115, 34, 58, 34, 85, 112, 84, 111, 68, 97, 116, + 101, 34, 125, 44, 123, 34, 116, 99, 98, 34, 58, 123, 34, 115, 103, 120, 116, 99, + 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, 58, 91, 123, 34, 115, 118, + 110, 34, 58, 54, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, + 79, 83, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 69, 97, 114, 108, 121, 32, 77, + 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, + 123, 34, 115, 118, 110, 34, 58, 54, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, + 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 83, + 71, 88, 32, 76, 97, 116, 101, 32, 77, 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, + 112, 100, 97, 116, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 50, 44, 34, + 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, + 34, 116, 121, 112, 101, 34, 58, 34, 84, 88, 84, 32, 83, 73, 78, 73, 84, 34, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 50, 44, 34, 99, 97, 116, 101, 103, 111, 114, + 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 51, + 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 49, 44, 34, 99, 97, 116, 101, 103, 111, 114, + 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, 111, + 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, + 58, 34, 83, 69, 65, 77, 76, 68, 82, 32, 65, 67, 77, 34, 125, 44, 123, 34, 115, 118, + 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, + 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, + 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, + 58, 48, 125, 93, 44, 34, 112, 99, 101, 115, 118, 110, 34, 58, 49, 49, 44, 34, 116, + 100, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, 58, 91, + 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, + 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, + 68, 88, 32, 77, 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, + 58, 48, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, + 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 77, 111, 100, + 117, 108, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 54, 44, 34, 99, 97, + 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, + 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 76, 97, 116, 101, 32, 77, 105, 99, 114, + 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 93, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, 48, 50, 51, 45, 48, + 56, 45, 48, 57, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, 34, 116, 99, 98, + 83, 116, 97, 116, 117, 115, 34, 58, 34, 79, 117, 116, 79, 102, 68, 97, 116, 101, + 34, 44, 34, 97, 100, 118, 105, 115, 111, 114, 121, 73, 68, 115, 34, 58, 91, 34, 73, + 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 57, 54, 48, 34, 44, 34, 73, 78, 84, 69, 76, + 45, 83, 65, 45, 48, 48, 57, 56, 50, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, + 48, 48, 57, 56, 54, 34, 93, 125, 44, 123, 34, 116, 99, 98, 34, 58, 123, 34, 115, + 103, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, 58, 91, + 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, + 34, 58, 34, 66, 73, 79, 83, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 69, 97, + 114, 108, 121, 32, 77, 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, + 116, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, + 112, 101, 34, 58, 34, 83, 71, 88, 32, 76, 97, 116, 101, 32, 77, 105, 99, 114, 111, + 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, 34, 115, 118, + 110, 34, 58, 50, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, + 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 88, 84, 32, 83, 73, + 78, 73, 84, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 50, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, + 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 49, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, + 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, + 121, 112, 101, 34, 58, 34, 83, 69, 65, 77, 76, 68, 82, 32, 65, 67, 77, 34, 125, 44, + 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, + 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, + 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, + 115, 118, 110, 34, 58, 48, 125, 93, 44, 34, 112, 99, 101, 115, 118, 110, 34, 58, + 49, 49, 44, 34, 116, 100, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, + 116, 115, 34, 58, 91, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, + 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, + 101, 34, 58, 34, 84, 68, 88, 32, 77, 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, + 115, 118, 110, 34, 58, 48, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, + 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, + 32, 77, 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 53, + 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, + 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 76, 97, 116, 101, 32, + 77, 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 93, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, + 48, 50, 51, 45, 48, 50, 45, 49, 53, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, + 34, 116, 99, 98, 83, 116, 97, 116, 117, 115, 34, 58, 34, 79, 117, 116, 79, 102, 68, + 97, 116, 101, 34, 44, 34, 97, 100, 118, 105, 115, 111, 114, 121, 73, 68, 115, 34, + 58, 91, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 56, 51, 55, 34, 44, 34, 73, + 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 57, 54, 48, 34, 44, 34, 73, 78, 84, 69, 76, + 45, 83, 65, 45, 48, 48, 57, 56, 50, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, + 48, 48, 57, 56, 54, 34, 93, 125, 44, 123, 34, 116, 99, 98, 34, 58, 123, 34, 115, + 103, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 34, 58, 91, + 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, + 34, 58, 34, 66, 73, 79, 83, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 69, 97, + 114, 108, 121, 32, 77, 105, 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, + 116, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, + 112, 101, 34, 58, 34, 83, 71, 88, 32, 76, 97, 116, 101, 32, 77, 105, 99, 114, 111, + 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, 34, 115, 118, + 110, 34, 58, 50, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, + 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 88, 84, 32, 83, 73, + 78, 73, 84, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 50, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 66, + 73, 79, 83, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 49, 44, 34, 99, 97, 116, + 101, 103, 111, 114, 121, 34, 58, 34, 66, 73, 79, 83, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, + 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, + 121, 112, 101, 34, 58, 34, 83, 69, 65, 77, 76, 68, 82, 32, 65, 67, 77, 34, 125, 44, + 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, + 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, + 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, + 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, + 115, 118, 110, 34, 58, 48, 125, 93, 44, 34, 112, 99, 101, 115, 118, 110, 34, 58, + 53, 44, 34, 116, 100, 120, 116, 99, 98, 99, 111, 109, 112, 111, 110, 101, 110, 116, + 115, 34, 58, 91, 123, 34, 115, 118, 110, 34, 58, 51, 44, 34, 99, 97, 116, 101, 103, + 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, + 34, 58, 34, 84, 68, 88, 32, 77, 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 44, 34, 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, + 83, 47, 86, 77, 77, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 77, + 111, 100, 117, 108, 101, 34, 125, 44, 123, 34, 115, 118, 110, 34, 58, 53, 44, 34, + 99, 97, 116, 101, 103, 111, 114, 121, 34, 58, 34, 79, 83, 47, 86, 77, 77, 34, 44, + 34, 116, 121, 112, 101, 34, 58, 34, 84, 68, 88, 32, 76, 97, 116, 101, 32, 77, 105, + 99, 114, 111, 99, 111, 100, 101, 32, 85, 112, 100, 97, 116, 101, 34, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, + 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, + 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, + 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, + 44, 123, 34, 115, 118, 110, 34, 58, 48, 125, 44, 123, 34, 115, 118, 110, 34, 58, + 48, 125, 93, 125, 44, 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, 48, 49, + 56, 45, 48, 49, 45, 48, 52, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, 34, + 116, 99, 98, 83, 116, 97, 116, 117, 115, 34, 58, 34, 79, 117, 116, 79, 102, 68, 97, + 116, 101, 34, 44, 34, 97, 100, 118, 105, 115, 111, 114, 121, 73, 68, 115, 34, 58, + 91, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 49, 48, 54, 34, 44, 34, 73, 78, + 84, 69, 76, 45, 83, 65, 45, 48, 48, 49, 49, 53, 34, 44, 34, 73, 78, 84, 69, 76, 45, + 83, 65, 45, 48, 48, 49, 51, 53, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, + 48, 50, 48, 51, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 50, 50, 48, + 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 50, 51, 51, 34, 44, 34, 73, + 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 50, 55, 48, 34, 44, 34, 73, 78, 84, 69, 76, + 45, 83, 65, 45, 48, 48, 50, 57, 51, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, + 48, 48, 51, 50, 48, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 51, 50, + 57, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 51, 56, 49, 34, 44, 34, + 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 51, 56, 57, 34, 44, 34, 73, 78, 84, 69, + 76, 45, 83, 65, 45, 48, 48, 52, 55, 55, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, + 45, 48, 48, 56, 51, 55, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 57, + 54, 48, 34, 44, 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 57, 56, 50, 34, 44, + 34, 73, 78, 84, 69, 76, 45, 83, 65, 45, 48, 48, 57, 56, 54, 34, 93, 125, 93, 125, + 44, 34, 115, 105, 103, 110, 97, 116, 117, 114, 101, 34, 58, 34, 48, 54, 99, 52, 54, + 48, 56, 54, 55, 56, 56, 50, 100, 99, 99, 100, 100, 97, 49, 55, 48, 48, 52, 98, 97, + 54, 48, 102, 53, 52, 99, 50, 50, 101, 102, 56, 53, 52, 100, 53, 57, 98, 53, 50, 52, + 100, 50, 54, 49, 98, 55, 97, 98, 97, 50, 50, 57, 101, 101, 97, 101, 50, 99, 56, 48, + 48, 51, 56, 53, 101, 55, 51, 48, 56, 51, 50, 53, 98, 56, 97, 48, 49, 48, 53, 48, + 54, 49, 54, 48, 48, 98, 50, 50, 55, 98, 56, 55, 50, 56, 100, 55, 102, 49, 101, 53, + 52, 53, 57, 52, 100, 99, 101, 98, 50, 100, 102, 48, 102, 53, 54, 102, 51, 97, 54, + 100, 102, 102, 99, 34, 125, 0, + ] + .into(), + qe_identity_issuer_chain: [ + 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, + 69, 45, 45, 45, 45, 45, 10, 77, 73, 73, 67, 105, 122, 67, 67, 65, 106, 75, 103, 65, + 119, 73, 66, 65, 103, 73, 85, 102, 106, 105, 67, 49, 102, 116, 86, 75, 85, 112, 65, + 83, 89, 53, 70, 104, 65, 80, 112, 70, 74, 71, 57, 57, 70, 85, 119, 67, 103, 89, 73, + 75, 111, 90, 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, + 103, 71, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, + 48, 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, + 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, + 118, 10, 99, 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, + 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, + 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, + 48, 69, 120, 67, 122, 65, 74, 10, 66, 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, + 77, 66, 52, 88, 68, 84, 69, 52, 77, 68, 85, 121, 77, 84, 69, 119, 78, 84, 65, 120, + 77, 70, 111, 88, 68, 84, 73, 49, 77, 68, 85, 121, 77, 84, 69, 119, 78, 84, 65, 120, + 77, 70, 111, 119, 98, 68, 69, 101, 77, 66, 119, 71, 10, 65, 49, 85, 69, 65, 119, + 119, 86, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, 70, 82, 68, 81, + 105, 66, 84, 97, 87, 100, 117, 97, 87, 53, 110, 77, 82, 111, 119, 71, 65, 89, 68, + 86, 81, 81, 75, 68, 66, 70, 74, 98, 110, 82, 108, 98, 67, 66, 68, 98, 51, 74, 119, + 10, 98, 51, 74, 104, 100, 71, 108, 118, 98, 106, 69, 85, 77, 66, 73, 71, 65, 49, + 85, 69, 66, 119, 119, 76, 85, 50, 70, 117, 100, 71, 69, 103, 81, 50, 120, 104, 99, + 109, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, 103, 77, 65, 107, 78, 66, + 77, 81, 115, 119, 67, 81, 89, 68, 10, 86, 81, 81, 71, 69, 119, 74, 86, 85, 122, 66, + 90, 77, 66, 77, 71, 66, 121, 113, 71, 83, 77, 52, 57, 65, 103, 69, 71, 67, 67, 113, + 71, 83, 77, 52, 57, 65, 119, 69, 72, 65, 48, 73, 65, 66, 69, 78, 70, 71, 56, 120, + 122, 121, 100, 87, 82, 102, 75, 57, 50, 98, 109, 71, 118, 10, 80, 43, 109, 65, 104, + 57, 49, 80, 69, 121, 86, 55, 74, 104, 54, 70, 71, 74, 100, 53, 110, 100, 69, 57, + 97, 66, 72, 55, 82, 51, 69, 52, 65, 55, 117, 98, 114, 108, 104, 47, 122, 78, 51, + 67, 52, 120, 118, 112, 111, 111, 117, 71, 108, 105, 114, 77, 98, 97, 43, 87, 50, + 108, 106, 117, 10, 121, 112, 97, 106, 103, 98, 85, 119, 103, 98, 73, 119, 72, 119, + 89, 68, 86, 82, 48, 106, 66, 66, 103, 119, 70, 111, 65, 85, 73, 109, 85, 77, 49, + 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 85, 114, 57, 81, 71, 122, 107, + 110, 66, 113, 119, 119, 85, 103, 89, 68, 86, 82, 48, 102, 10, 66, 69, 115, 119, 83, + 84, 66, 72, 111, 69, 87, 103, 81, 52, 90, 66, 97, 72, 82, 48, 99, 72, 77, 54, 76, + 121, 57, 106, 90, 88, 74, 48, 97, 87, 90, 112, 89, 50, 70, 48, 90, 88, 77, 117, + 100, 72, 74, 49, 99, 51, 82, 108, 90, 72, 78, 108, 99, 110, 90, 112, 89, 50, 86, + 122, 10, 76, 109, 108, 117, 100, 71, 86, 115, 76, 109, 78, 118, 98, 83, 57, 74, 98, + 110, 82, 108, 98, 70, 78, 72, 87, 70, 74, 118, 98, 51, 82, 68, 81, 83, 53, 107, 90, + 88, 73, 119, 72, 81, 89, 68, 86, 82, 48, 79, 66, 66, 89, 69, 70, 72, 52, 52, 103, + 116, 88, 55, 86, 83, 108, 75, 10, 81, 69, 109, 79, 82, 89, 81, 68, 54, 82, 83, 82, + 118, 102, 82, 86, 77, 65, 52, 71, 65, 49, 85, 100, 68, 119, 69, 66, 47, 119, 81, + 69, 65, 119, 73, 71, 119, 68, 65, 77, 66, 103, 78, 86, 72, 82, 77, 66, 65, 102, 56, + 69, 65, 106, 65, 65, 77, 65, 111, 71, 67, 67, 113, 71, 10, 83, 77, 52, 57, 66, 65, + 77, 67, 65, 48, 99, 65, 77, 69, 81, 67, 73, 66, 57, 67, 56, 119, 79, 65, 78, 47, + 73, 109, 120, 68, 116, 71, 65, 67, 86, 50, 52, 54, 75, 99, 113, 106, 97, 103, 90, + 79, 82, 48, 107, 121, 99, 116, 121, 66, 114, 115, 71, 71, 74, 86, 65, 105, 65, 106, + 10, 102, 116, 98, 114, 78, 71, 115, 71, 85, 56, 89, 72, 50, 49, 49, 100, 82, 105, + 89, 78, 111, 80, 80, 117, 49, 57, 90, 112, 47, 122, 101, 56, 74, 109, 104, 117, + 106, 66, 48, 111, 66, 119, 61, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 67, 69, + 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 45, 45, 45, 45, 45, 66, + 69, 71, 73, 78, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, + 10, 77, 73, 73, 67, 106, 122, 67, 67, 65, 106, 83, 103, 65, 119, 73, 66, 65, 103, + 73, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, 103, 55, 83, 86, 85, + 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 67, 103, 89, 73, 75, 111, 90, + 73, 122, 106, 48, 69, 65, 119, 73, 119, 10, 97, 68, 69, 97, 77, 66, 103, 71, 65, + 49, 85, 69, 65, 119, 119, 82, 83, 87, 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, + 73, 70, 74, 118, 98, 51, 81, 103, 81, 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, + 86, 66, 65, 111, 77, 69, 85, 108, 117, 100, 71, 86, 115, 73, 69, 78, 118, 10, 99, + 110, 66, 118, 99, 109, 70, 48, 97, 87, 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, + 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, + 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, + 67, 122, 65, 74, 10, 66, 103, 78, 86, 66, 65, 89, 84, 65, 108, 86, 84, 77, 66, 52, + 88, 68, 84, 69, 52, 77, 68, 85, 121, 77, 84, 69, 119, 78, 68, 85, 120, 77, 70, 111, + 88, 68, 84, 81, 53, 77, 84, 73, 122, 77, 84, 73, 122, 78, 84, 107, 49, 79, 86, 111, + 119, 97, 68, 69, 97, 77, 66, 103, 71, 10, 65, 49, 85, 69, 65, 119, 119, 82, 83, 87, + 53, 48, 90, 87, 119, 103, 85, 48, 100, 89, 73, 70, 74, 118, 98, 51, 81, 103, 81, + 48, 69, 120, 71, 106, 65, 89, 66, 103, 78, 86, 66, 65, 111, 77, 69, 85, 108, 117, + 100, 71, 86, 115, 73, 69, 78, 118, 99, 110, 66, 118, 99, 109, 70, 48, 10, 97, 87, + 57, 117, 77, 82, 81, 119, 69, 103, 89, 68, 86, 81, 81, 72, 68, 65, 116, 84, 89, 87, + 53, 48, 89, 83, 66, 68, 98, 71, 70, 121, 89, 84, 69, 76, 77, 65, 107, 71, 65, 49, + 85, 69, 67, 65, 119, 67, 81, 48, 69, 120, 67, 122, 65, 74, 66, 103, 78, 86, 66, 65, + 89, 84, 10, 65, 108, 86, 84, 77, 70, 107, 119, 69, 119, 89, 72, 75, 111, 90, 73, + 122, 106, 48, 67, 65, 81, 89, 73, 75, 111, 90, 73, 122, 106, 48, 68, 65, 81, 99, + 68, 81, 103, 65, 69, 67, 54, 110, 69, 119, 77, 68, 73, 89, 90, 79, 106, 47, 105, + 80, 87, 115, 67, 122, 97, 69, 75, 105, 55, 10, 49, 79, 105, 79, 83, 76, 82, 70, + 104, 87, 71, 106, 98, 110, 66, 86, 74, 102, 86, 110, 107, 89, 52, 117, 51, 73, 106, + 107, 68, 89, 89, 76, 48, 77, 120, 79, 52, 109, 113, 115, 121, 89, 106, 108, 66, 97, + 108, 84, 86, 89, 120, 70, 80, 50, 115, 74, 66, 75, 53, 122, 108, 75, 79, 66, 10, + 117, 122, 67, 66, 117, 68, 65, 102, 66, 103, 78, 86, 72, 83, 77, 69, 71, 68, 65, + 87, 103, 66, 81, 105, 90, 81, 122, 87, 87, 112, 48, 48, 105, 102, 79, 68, 116, 74, + 86, 83, 118, 49, 65, 98, 79, 83, 99, 71, 114, 68, 66, 83, 66, 103, 78, 86, 72, 82, + 56, 69, 83, 122, 66, 74, 10, 77, 69, 101, 103, 82, 97, 66, 68, 104, 107, 70, 111, + 100, 72, 82, 119, 99, 122, 111, 118, 76, 50, 78, 108, 99, 110, 82, 112, 90, 109, + 108, 106, 89, 88, 82, 108, 99, 121, 53, 48, 99, 110, 86, 122, 100, 71, 86, 107, 99, + 50, 86, 121, 100, 109, 108, 106, 90, 88, 77, 117, 97, 87, 53, 48, 10, 90, 87, 119, + 117, 89, 50, 57, 116, 76, 48, 108, 117, 100, 71, 86, 115, 85, 48, 100, 89, 85, 109, + 57, 118, 100, 69, 78, 66, 76, 109, 82, 108, 99, 106, 65, 100, 66, 103, 78, 86, 72, + 81, 52, 69, 70, 103, 81, 85, 73, 109, 85, 77, 49, 108, 113, 100, 78, 73, 110, 122, + 103, 55, 83, 86, 10, 85, 114, 57, 81, 71, 122, 107, 110, 66, 113, 119, 119, 68, + 103, 89, 68, 86, 82, 48, 80, 65, 81, 72, 47, 66, 65, 81, 68, 65, 103, 69, 71, 77, + 66, 73, 71, 65, 49, 85, 100, 69, 119, 69, 66, 47, 119, 81, 73, 77, 65, 89, 66, 65, + 102, 56, 67, 65, 81, 69, 119, 67, 103, 89, 73, 10, 75, 111, 90, 73, 122, 106, 48, + 69, 65, 119, 73, 68, 83, 81, 65, 119, 82, 103, 73, 104, 65, 79, 87, 47, 53, 81, + 107, 82, 43, 83, 57, 67, 105, 83, 68, 99, 78, 111, 111, 119, 76, 117, 80, 82, 76, + 115, 87, 71, 102, 47, 89, 105, 55, 71, 83, 88, 57, 52, 66, 103, 119, 84, 119, 103, + 10, 65, 105, 69, 65, 52, 74, 48, 108, 114, 72, 111, 77, 115, 43, 88, 111, 53, 111, + 47, 115, 88, 54, 79, 57, 81, 87, 120, 72, 82, 65, 118, 90, 85, 71, 79, 100, 82, 81, + 55, 99, 118, 113, 82, 88, 97, 113, 73, 61, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, + 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 10, 0, + ] + .into(), + qe_identity: [ + 123, 34, 101, 110, 99, 108, 97, 118, 101, 73, 100, 101, 110, 116, 105, 116, 121, + 34, 58, 123, 34, 105, 100, 34, 58, 34, 84, 68, 95, 81, 69, 34, 44, 34, 118, 101, + 114, 115, 105, 111, 110, 34, 58, 50, 44, 34, 105, 115, 115, 117, 101, 68, 97, 116, + 101, 34, 58, 34, 50, 48, 50, 52, 45, 49, 50, 45, 48, 53, 84, 49, 49, 58, 52, 54, + 58, 49, 50, 90, 34, 44, 34, 110, 101, 120, 116, 85, 112, 100, 97, 116, 101, 34, 58, + 34, 50, 48, 50, 53, 45, 48, 49, 45, 48, 52, 84, 49, 49, 58, 52, 54, 58, 49, 50, 90, + 34, 44, 34, 116, 99, 98, 69, 118, 97, 108, 117, 97, 116, 105, 111, 110, 68, 97, + 116, 97, 78, 117, 109, 98, 101, 114, 34, 58, 49, 55, 44, 34, 109, 105, 115, 99, + 115, 101, 108, 101, 99, 116, 34, 58, 34, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, + 34, 109, 105, 115, 99, 115, 101, 108, 101, 99, 116, 77, 97, 115, 107, 34, 58, 34, + 70, 70, 70, 70, 70, 70, 70, 70, 34, 44, 34, 97, 116, 116, 114, 105, 98, 117, 116, + 101, 115, 34, 58, 34, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 97, + 116, 116, 114, 105, 98, 117, 116, 101, 115, 77, 97, 115, 107, 34, 58, 34, 70, 66, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 109, 114, 115, 105, 103, 110, 101, + 114, 34, 58, 34, 68, 67, 57, 69, 50, 65, 55, 67, 54, 70, 57, 52, 56, 70, 49, 55, + 52, 55, 52, 69, 51, 52, 65, 55, 70, 67, 52, 51, 69, 68, 48, 51, 48, 70, 55, 67, 49, + 53, 54, 51, 70, 49, 66, 65, 66, 68, 68, 70, 54, 51, 52, 48, 67, 56, 50, 69, 48, 69, + 53, 52, 65, 56, 67, 53, 34, 44, 34, 105, 115, 118, 112, 114, 111, 100, 105, 100, + 34, 58, 50, 44, 34, 116, 99, 98, 76, 101, 118, 101, 108, 115, 34, 58, 91, 123, 34, + 116, 99, 98, 34, 58, 123, 34, 105, 115, 118, 115, 118, 110, 34, 58, 52, 125, 44, + 34, 116, 99, 98, 68, 97, 116, 101, 34, 58, 34, 50, 48, 50, 52, 45, 48, 51, 45, 49, + 51, 84, 48, 48, 58, 48, 48, 58, 48, 48, 90, 34, 44, 34, 116, 99, 98, 83, 116, 97, + 116, 117, 115, 34, 58, 34, 85, 112, 84, 111, 68, 97, 116, 101, 34, 125, 93, 125, + 44, 34, 115, 105, 103, 110, 97, 116, 117, 114, 101, 34, 58, 34, 55, 55, 53, 57, 54, + 101, 102, 102, 51, 101, 101, 101, 56, 99, 100, 51, 101, 52, 101, 54, 50, 48, 48, + 56, 102, 57, 97, 102, 97, 100, 100, 51, 51, 51, 101, 57, 102, 56, 100, 99, 56, 51, + 55, 102, 101, 54, 51, 50, 50, 53, 54, 56, 49, 52, 55, 100, 97, 57, 56, 99, 56, 99, + 57, 55, 99, 53, 99, 102, 54, 50, 54, 52, 102, 51, 98, 48, 100, 52, 55, 99, 99, 97, + 97, 54, 49, 98, 49, 98, 53, 97, 49, 101, 56, 99, 53, 53, 51, 97, 49, 54, 51, 50, + 50, 50, 51, 55, 99, 99, 57, 48, 48, 49, 102, 50, 100, 97, 100, 49, 56, 51, 52, 52, + 57, 54, 53, 52, 49, 49, 34, 125, 0, + ] + .into(), + }; + + let report_data = hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + + let mrsigner = + hex::decode("3b909bb3658ff42a1e877b8806fdab857f70dfc90244270c12ec2459c98d191a") + .unwrap(); + + check_quote( + "e, + Some(&collateral), + current_time as i64, + &mrsigner, + &report_data, + TcbLevel::Ok, + ) + .context("check_quote")?; + Ok(()) + } + + #[test] + fn get_ethereum_address_from_sgx_quote() { + let quote = [ + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x10, 0x00, 0x93, 0x9A, + 0x72, 0x33, 0xF7, 0x9C, 0x4C, 0xA9, 0x94, 0x0A, 0x0D, 0xB3, 0x95, 0x7F, 0x06, 0x07, + 0x63, 0xFB, 0x1C, 0xB9, 0x34, 0xDF, 0x64, 0xBC, 0xB4, 0x6D, 0x49, 0x47, 0x5F, 0x80, + 0xD8, 0x28, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x16, 0x18, 0x03, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC3, 0xAB, 0xFB, 0x19, 0x58, 0x23, 0xD9, 0x2F, 0x4D, 0xFD, 0x12, 0xD9, 0x2D, 0xA6, + 0x1C, 0x3F, 0xA7, 0x1D, 0xD6, 0x96, 0xE6, 0x49, 0x3A, 0xD7, 0x1C, 0x0A, 0x64, 0x25, + 0x55, 0x16, 0x6A, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x59, 0x1A, 0x72, 0xB8, 0xB8, + 0x6E, 0x0D, 0x88, 0x14, 0xD6, 0xE8, 0x75, 0x0E, 0x3E, 0xFE, 0x66, 0xAE, 0xA2, 0xD1, + 0x02, 0xB8, 0xBA, 0x24, 0x05, 0x36, 0x55, 0x59, 0xB8, 0x58, 0x69, 0x7D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x62, 0x73, 0x06, 0x09, 0x0A, 0xBA, 0xB3, 0xA6, 0xE1, 0x40, + 0x0E, 0x93, 0x45, 0xBC, 0x60, 0xC7, 0x8A, 0x8B, 0xEF, 0x57, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xCA, 0x10, + 0x00, 0x00, 0x9F, 0x22, 0x82, 0x4E, 0xFB, 0x91, 0x5E, 0x6F, 0x13, 0x99, 0xD9, 0xE2, + 0x90, 0x5D, 0xDC, 0x85, 0x9B, 0x06, 0x8F, 0x15, 0x98, 0x7D, 0xE7, 0xF8, 0xE7, 0xCB, + 0x71, 0x21, 0x4A, 0x69, 0xCC, 0xB4, 0x8A, 0x39, 0x47, 0x82, 0x1A, 0x34, 0x22, 0x7A, + 0xC1, 0x2C, 0x7D, 0x17, 0x33, 0x16, 0xF7, 0x24, 0x6B, 0x5F, 0x49, 0xE3, 0x60, 0x93, + 0xA7, 0xB8, 0x96, 0xBB, 0xDC, 0xDA, 0x69, 0xA3, 0x31, 0x2E, 0x14, 0xB6, 0xEC, 0x06, + 0x93, 0x48, 0x84, 0x61, 0x02, 0x92, 0x87, 0x4C, 0x0B, 0x4F, 0x94, 0xA3, 0x6B, 0xDA, + 0x56, 0x88, 0xD7, 0xF9, 0x30, 0xEC, 0x1B, 0x25, 0x5D, 0x79, 0x16, 0x82, 0x3C, 0x10, + 0xA1, 0x36, 0x99, 0xDD, 0x61, 0xD9, 0xB0, 0xAE, 0xD0, 0x9E, 0xAA, 0x8D, 0x0F, 0xAB, + 0x1F, 0x4C, 0x1F, 0x01, 0xA3, 0xF9, 0x9D, 0xFA, 0x96, 0xF1, 0xA3, 0x5D, 0x93, 0xBC, + 0x5A, 0x70, 0xB1, 0x7D, 0x06, 0x06, 0x16, 0x18, 0x03, 0xFF, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFE, + 0x8C, 0xFD, 0x01, 0x09, 0x5A, 0x0F, 0x10, 0x8A, 0xFF, 0x5C, 0x40, 0x62, 0x4B, 0x93, + 0x61, 0x2D, 0x6C, 0x28, 0xB7, 0x3E, 0x1A, 0x8D, 0x28, 0x17, 0x9C, 0x9D, 0xDF, 0x0E, + 0x06, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x4F, 0x57, 0x75, 0xD7, 0x96, 0x50, 0x3E, + 0x96, 0x13, 0x7F, 0x77, 0xC6, 0x8A, 0x82, 0x9A, 0x00, 0x56, 0xAC, 0x8D, 0xED, 0x70, + 0x14, 0x0B, 0x08, 0x1B, 0x09, 0x44, 0x90, 0xC5, 0x7B, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0xAA, 0x8C, 0x9D, 0xAE, 0x4E, 0xAC, 0x24, 0xE9, 0xAE, 0x7B, 0xD3, + 0x41, 0x7F, 0xE8, 0xB0, 0x63, 0xA8, 0x06, 0x2E, 0xE2, 0xE5, 0xCA, 0x7A, 0xEF, 0xBC, + 0x81, 0xFB, 0xFD, 0x13, 0x6A, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x72, 0xE4, 0xAF, + 0xAE, 0xEF, 0x52, 0x85, 0xF2, 0xA3, 0x1A, 0x9A, 0x90, 0x77, 0x36, 0xA9, 0xEF, 0x3C, + 0xB3, 0xA7, 0xE4, 0x5E, 0x9D, 0x70, 0xD8, 0xF6, 0x74, 0x6A, 0x67, 0x6B, 0x80, 0xD9, + 0x49, 0xCE, 0xD8, 0x84, 0x6C, 0x1F, 0x01, 0x79, 0xDF, 0xC6, 0xFF, 0xD6, 0xD1, 0x8F, + 0x15, 0xE0, 0xBB, 0xEE, 0x88, 0x81, 0x99, 0x2A, 0xDD, 0xA2, 0x41, 0xB4, 0xD2, 0x38, + 0xBD, 0x7B, 0x07, 0x42, 0x20, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x05, 0x00, 0x62, 0x0E, + 0x00, 0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, + 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x45, 0x38, 0x6A, 0x43, 0x43, 0x42, 0x4A, 0x69, 0x67, + 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4D, 0x44, 0x2F, 0x34, 0x41, + 0x31, 0x70, 0x30, 0x52, 0x4B, 0x2B, 0x4F, 0x56, 0x56, 0x31, 0x57, 0x69, 0x48, 0x54, + 0x48, 0x59, 0x44, 0x42, 0x4E, 0x38, 0x63, 0x66, 0x4D, 0x41, 0x6F, 0x47, 0x43, 0x43, + 0x71, 0x47, 0x53, 0x4D, 0x34, 0x39, 0x42, 0x41, 0x4D, 0x43, 0x0A, 0x4D, 0x48, 0x41, + 0x78, 0x49, 0x6A, 0x41, 0x67, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x4D, 0x4D, 0x47, + 0x55, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4E, 0x48, 0x57, 0x43, 0x42, + 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, 0x47, 0x5A, 0x76, 0x63, + 0x6D, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6A, 0x41, 0x59, 0x42, 0x67, 0x4E, + 0x56, 0x42, 0x41, 0x6F, 0x4D, 0x0A, 0x45, 0x55, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, + 0x49, 0x45, 0x4E, 0x76, 0x63, 0x6E, 0x42, 0x76, 0x63, 0x6D, 0x46, 0x30, 0x61, 0x57, + 0x39, 0x75, 0x4D, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, + 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, + 0x46, 0x79, 0x59, 0x54, 0x45, 0x4C, 0x4D, 0x41, 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, + 0x0A, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42, + 0x67, 0x4E, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6C, 0x56, 0x54, 0x4D, 0x42, 0x34, + 0x58, 0x44, 0x54, 0x49, 0x31, 0x4D, 0x44, 0x45, 0x78, 0x4E, 0x6A, 0x45, 0x35, 0x4D, + 0x54, 0x45, 0x79, 0x4D, 0x31, 0x6F, 0x58, 0x44, 0x54, 0x4D, 0x79, 0x4D, 0x44, 0x45, + 0x78, 0x4E, 0x6A, 0x45, 0x35, 0x4D, 0x54, 0x45, 0x79, 0x0A, 0x4D, 0x31, 0x6F, 0x77, + 0x63, 0x44, 0x45, 0x69, 0x4D, 0x43, 0x41, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, + 0x77, 0x5A, 0x53, 0x57, 0x35, 0x30, 0x5A, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, + 0x49, 0x46, 0x42, 0x44, 0x53, 0x79, 0x42, 0x44, 0x5A, 0x58, 0x4A, 0x30, 0x61, 0x57, + 0x5A, 0x70, 0x59, 0x32, 0x46, 0x30, 0x5A, 0x54, 0x45, 0x61, 0x4D, 0x42, 0x67, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x0A, 0x43, 0x67, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5A, + 0x57, 0x77, 0x67, 0x51, 0x32, 0x39, 0x79, 0x63, 0x47, 0x39, 0x79, 0x59, 0x58, 0x52, + 0x70, 0x62, 0x32, 0x34, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4E, 0x56, 0x42, + 0x41, 0x63, 0x4D, 0x43, 0x31, 0x4E, 0x68, 0x62, 0x6E, 0x52, 0x68, 0x49, 0x45, 0x4E, + 0x73, 0x59, 0x58, 0x4A, 0x68, 0x4D, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x0A, + 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, 0x4A, 0x44, 0x51, 0x54, 0x45, 0x4C, 0x4D, 0x41, + 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, 0x42, 0x68, 0x4D, 0x43, 0x56, 0x56, 0x4D, 0x77, + 0x57, 0x54, 0x41, 0x54, 0x42, 0x67, 0x63, 0x71, 0x68, 0x6B, 0x6A, 0x4F, 0x50, 0x51, + 0x49, 0x42, 0x42, 0x67, 0x67, 0x71, 0x68, 0x6B, 0x6A, 0x4F, 0x50, 0x51, 0x4D, 0x42, + 0x42, 0x77, 0x4E, 0x43, 0x41, 0x41, 0x54, 0x55, 0x0A, 0x46, 0x4E, 0x61, 0x4A, 0x4F, + 0x50, 0x2B, 0x36, 0x56, 0x46, 0x65, 0x39, 0x68, 0x49, 0x48, 0x6A, 0x47, 0x42, 0x59, + 0x73, 0x2F, 0x43, 0x42, 0x6C, 0x61, 0x31, 0x65, 0x47, 0x70, 0x4D, 0x4E, 0x39, 0x71, + 0x71, 0x6F, 0x6D, 0x72, 0x33, 0x31, 0x78, 0x57, 0x46, 0x68, 0x33, 0x4B, 0x4F, 0x2F, + 0x65, 0x34, 0x38, 0x6B, 0x4A, 0x53, 0x55, 0x64, 0x31, 0x45, 0x37, 0x7A, 0x47, 0x79, + 0x50, 0x61, 0x44, 0x0A, 0x58, 0x35, 0x4D, 0x48, 0x49, 0x72, 0x71, 0x5A, 0x51, 0x6A, + 0x41, 0x4C, 0x6D, 0x66, 0x54, 0x70, 0x6B, 0x34, 0x66, 0x51, 0x6F, 0x34, 0x49, 0x44, + 0x44, 0x54, 0x43, 0x43, 0x41, 0x77, 0x6B, 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, + 0x30, 0x6A, 0x42, 0x42, 0x67, 0x77, 0x46, 0x6F, 0x41, 0x55, 0x6C, 0x57, 0x39, 0x64, + 0x7A, 0x62, 0x30, 0x62, 0x34, 0x65, 0x6C, 0x41, 0x53, 0x63, 0x6E, 0x55, 0x0A, 0x39, + 0x44, 0x50, 0x4F, 0x41, 0x56, 0x63, 0x4C, 0x33, 0x6C, 0x51, 0x77, 0x61, 0x77, 0x59, + 0x44, 0x56, 0x52, 0x30, 0x66, 0x42, 0x47, 0x51, 0x77, 0x59, 0x6A, 0x42, 0x67, 0x6F, + 0x46, 0x36, 0x67, 0x58, 0x49, 0x5A, 0x61, 0x61, 0x48, 0x52, 0x30, 0x63, 0x48, 0x4D, + 0x36, 0x4C, 0x79, 0x39, 0x68, 0x63, 0x47, 0x6B, 0x75, 0x64, 0x48, 0x4A, 0x31, 0x63, + 0x33, 0x52, 0x6C, 0x5A, 0x48, 0x4E, 0x6C, 0x0A, 0x63, 0x6E, 0x5A, 0x70, 0x59, 0x32, + 0x56, 0x7A, 0x4C, 0x6D, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x4C, 0x6D, 0x4E, 0x76, + 0x62, 0x53, 0x39, 0x7A, 0x5A, 0x33, 0x67, 0x76, 0x59, 0x32, 0x56, 0x79, 0x64, 0x47, + 0x6C, 0x6D, 0x61, 0x57, 0x4E, 0x68, 0x64, 0x47, 0x6C, 0x76, 0x62, 0x69, 0x39, 0x32, + 0x4E, 0x43, 0x39, 0x77, 0x59, 0x32, 0x74, 0x6A, 0x63, 0x6D, 0x77, 0x2F, 0x59, 0x32, + 0x45, 0x39, 0x0A, 0x63, 0x47, 0x78, 0x68, 0x64, 0x47, 0x5A, 0x76, 0x63, 0x6D, 0x30, + 0x6D, 0x5A, 0x57, 0x35, 0x6A, 0x62, 0x32, 0x52, 0x70, 0x62, 0x6D, 0x63, 0x39, 0x5A, + 0x47, 0x56, 0x79, 0x4D, 0x42, 0x30, 0x47, 0x41, 0x31, 0x55, 0x64, 0x44, 0x67, 0x51, + 0x57, 0x42, 0x42, 0x51, 0x52, 0x49, 0x68, 0x6F, 0x4D, 0x41, 0x36, 0x7A, 0x4D, 0x48, + 0x4D, 0x43, 0x75, 0x39, 0x78, 0x6B, 0x53, 0x50, 0x31, 0x42, 0x46, 0x0A, 0x33, 0x35, + 0x54, 0x70, 0x69, 0x7A, 0x41, 0x4F, 0x42, 0x67, 0x4E, 0x56, 0x48, 0x51, 0x38, 0x42, + 0x41, 0x66, 0x38, 0x45, 0x42, 0x41, 0x4D, 0x43, 0x42, 0x73, 0x41, 0x77, 0x44, 0x41, + 0x59, 0x44, 0x56, 0x52, 0x30, 0x54, 0x41, 0x51, 0x48, 0x2F, 0x42, 0x41, 0x49, 0x77, + 0x41, 0x44, 0x43, 0x43, 0x41, 0x6A, 0x6F, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x34, 0x54, 0x51, 0x45, 0x4E, 0x0A, 0x41, 0x51, 0x53, 0x43, 0x41, 0x69, 0x73, + 0x77, 0x67, 0x67, 0x49, 0x6E, 0x4D, 0x42, 0x34, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, + 0x49, 0x62, 0x34, 0x54, 0x51, 0x45, 0x4E, 0x41, 0x51, 0x45, 0x45, 0x45, 0x4A, 0x6B, + 0x55, 0x39, 0x50, 0x62, 0x45, 0x49, 0x73, 0x2B, 0x68, 0x37, 0x6F, 0x35, 0x68, 0x4D, + 0x31, 0x35, 0x64, 0x64, 0x75, 0x51, 0x77, 0x67, 0x67, 0x46, 0x6B, 0x42, 0x67, 0x6F, + 0x71, 0x0A, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, + 0x4D, 0x49, 0x49, 0x42, 0x56, 0x44, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, + 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x41, 0x51, 0x49, 0x42, + 0x42, 0x6A, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x41, 0x67, 0x49, 0x42, 0x0A, 0x42, 0x6A, 0x41, + 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x43, 0x41, 0x77, 0x49, 0x42, 0x41, 0x6A, 0x41, 0x51, 0x42, 0x67, 0x73, + 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x42, + 0x41, 0x49, 0x42, 0x41, 0x6A, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, 0x69, + 0x47, 0x2B, 0x45, 0x30, 0x42, 0x0A, 0x44, 0x51, 0x45, 0x43, 0x42, 0x51, 0x49, 0x42, + 0x41, 0x7A, 0x41, 0x51, 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, + 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x42, 0x67, 0x49, 0x42, 0x41, 0x54, 0x41, 0x51, + 0x42, 0x67, 0x73, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, 0x51, + 0x45, 0x43, 0x42, 0x77, 0x49, 0x42, 0x41, 0x44, 0x41, 0x52, 0x42, 0x67, 0x73, 0x71, + 0x0A, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, 0x51, 0x45, 0x43, 0x43, + 0x41, 0x49, 0x43, 0x41, 0x50, 0x38, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, + 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6B, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, + 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x6F, 0x43, 0x0A, 0x41, 0x51, 0x41, 0x77, + 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, + 0x30, 0x42, 0x41, 0x67, 0x73, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, + 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, + 0x77, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, + 0x68, 0x76, 0x68, 0x4E, 0x0A, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x30, 0x43, 0x41, + 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, + 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x67, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, + 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, + 0x42, 0x41, 0x67, 0x38, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x0A, + 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, + 0x41, 0x43, 0x41, 0x51, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, + 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x45, 0x43, 0x41, 0x51, + 0x73, 0x77, 0x48, 0x77, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, + 0x41, 0x51, 0x30, 0x42, 0x41, 0x68, 0x49, 0x45, 0x0A, 0x45, 0x41, 0x59, 0x47, 0x41, + 0x67, 0x49, 0x44, 0x41, 0x51, 0x44, 0x2F, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x77, 0x45, 0x41, 0x59, 0x4B, 0x4B, 0x6F, 0x5A, 0x49, 0x68, + 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x41, 0x77, 0x51, 0x43, 0x41, 0x41, 0x41, + 0x77, 0x46, 0x41, 0x59, 0x4B, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, + 0x51, 0x30, 0x42, 0x0A, 0x42, 0x41, 0x51, 0x47, 0x6B, 0x49, 0x42, 0x76, 0x41, 0x41, + 0x41, 0x41, 0x4D, 0x41, 0x38, 0x47, 0x43, 0x69, 0x71, 0x47, 0x53, 0x49, 0x62, 0x34, + 0x54, 0x51, 0x45, 0x4E, 0x41, 0x51, 0x55, 0x4B, 0x41, 0x51, 0x45, 0x77, 0x48, 0x67, + 0x59, 0x4B, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, + 0x42, 0x67, 0x51, 0x51, 0x6A, 0x52, 0x32, 0x30, 0x65, 0x42, 0x69, 0x76, 0x0A, 0x6C, + 0x30, 0x42, 0x45, 0x79, 0x4F, 0x31, 0x71, 0x66, 0x7A, 0x57, 0x78, 0x53, 0x7A, 0x42, + 0x45, 0x42, 0x67, 0x6F, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x2B, 0x45, 0x30, 0x42, 0x44, + 0x51, 0x45, 0x48, 0x4D, 0x44, 0x59, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, + 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x45, 0x42, 0x41, + 0x66, 0x38, 0x77, 0x45, 0x41, 0x59, 0x4C, 0x0A, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, + 0x68, 0x4E, 0x41, 0x51, 0x30, 0x42, 0x42, 0x77, 0x49, 0x42, 0x41, 0x66, 0x38, 0x77, + 0x45, 0x41, 0x59, 0x4C, 0x4B, 0x6F, 0x5A, 0x49, 0x68, 0x76, 0x68, 0x4E, 0x41, 0x51, + 0x30, 0x42, 0x42, 0x77, 0x4D, 0x42, 0x41, 0x66, 0x38, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, 0x41, + 0x41, 0x77, 0x0A, 0x52, 0x51, 0x49, 0x67, 0x54, 0x30, 0x4B, 0x32, 0x44, 0x70, 0x64, + 0x77, 0x58, 0x58, 0x77, 0x78, 0x2B, 0x67, 0x6B, 0x38, 0x4D, 0x6E, 0x6F, 0x50, 0x56, + 0x49, 0x6A, 0x58, 0x46, 0x71, 0x4E, 0x58, 0x45, 0x2F, 0x32, 0x39, 0x4D, 0x58, 0x68, + 0x4D, 0x57, 0x72, 0x53, 0x4E, 0x6F, 0x72, 0x67, 0x43, 0x49, 0x51, 0x44, 0x61, 0x72, + 0x33, 0x34, 0x78, 0x58, 0x2F, 0x57, 0x6C, 0x75, 0x44, 0x6B, 0x6A, 0x0A, 0x58, 0x4A, + 0x51, 0x54, 0x61, 0x72, 0x4C, 0x59, 0x76, 0x78, 0x2B, 0x74, 0x65, 0x36, 0x44, 0x31, + 0x64, 0x74, 0x77, 0x52, 0x44, 0x63, 0x6D, 0x6A, 0x58, 0x53, 0x37, 0x4B, 0x72, 0x41, + 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x0A, 0x4D, 0x49, 0x49, 0x43, 0x6C, 0x6A, 0x43, 0x43, 0x41, 0x6A, 0x32, 0x67, 0x41, + 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x56, 0x41, 0x4A, 0x56, 0x76, 0x58, 0x63, 0x32, + 0x39, 0x47, 0x2B, 0x48, 0x70, 0x51, 0x45, 0x6E, 0x4A, 0x31, 0x50, 0x51, 0x7A, 0x7A, + 0x67, 0x46, 0x58, 0x43, 0x39, 0x35, 0x55, 0x4D, 0x41, 0x6F, 0x47, 0x43, 0x43, 0x71, + 0x47, 0x53, 0x4D, 0x34, 0x39, 0x42, 0x41, 0x4D, 0x43, 0x0A, 0x4D, 0x47, 0x67, 0x78, + 0x47, 0x6A, 0x41, 0x59, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x4D, 0x4D, 0x45, 0x55, + 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4E, 0x48, 0x57, 0x43, 0x42, 0x53, + 0x62, 0x32, 0x39, 0x30, 0x49, 0x45, 0x4E, 0x42, 0x4D, 0x52, 0x6F, 0x77, 0x47, 0x41, + 0x59, 0x44, 0x56, 0x51, 0x51, 0x4B, 0x44, 0x42, 0x46, 0x4A, 0x62, 0x6E, 0x52, 0x6C, + 0x62, 0x43, 0x42, 0x44, 0x0A, 0x62, 0x33, 0x4A, 0x77, 0x62, 0x33, 0x4A, 0x68, 0x64, + 0x47, 0x6C, 0x76, 0x62, 0x6A, 0x45, 0x55, 0x4D, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, + 0x45, 0x42, 0x77, 0x77, 0x4C, 0x55, 0x32, 0x46, 0x75, 0x64, 0x47, 0x45, 0x67, 0x51, + 0x32, 0x78, 0x68, 0x63, 0x6D, 0x45, 0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42, 0x67, 0x4E, + 0x56, 0x42, 0x41, 0x67, 0x4D, 0x41, 0x6B, 0x4E, 0x42, 0x4D, 0x51, 0x73, 0x77, 0x0A, + 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4A, 0x56, 0x55, 0x7A, + 0x41, 0x65, 0x46, 0x77, 0x30, 0x78, 0x4F, 0x44, 0x41, 0x31, 0x4D, 0x6A, 0x45, 0x78, + 0x4D, 0x44, 0x55, 0x77, 0x4D, 0x54, 0x42, 0x61, 0x46, 0x77, 0x30, 0x7A, 0x4D, 0x7A, + 0x41, 0x31, 0x4D, 0x6A, 0x45, 0x78, 0x4D, 0x44, 0x55, 0x77, 0x4D, 0x54, 0x42, 0x61, + 0x4D, 0x48, 0x41, 0x78, 0x49, 0x6A, 0x41, 0x67, 0x0A, 0x42, 0x67, 0x4E, 0x56, 0x42, + 0x41, 0x4D, 0x4D, 0x47, 0x55, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x46, 0x4E, + 0x48, 0x57, 0x43, 0x42, 0x51, 0x51, 0x30, 0x73, 0x67, 0x55, 0x47, 0x78, 0x68, 0x64, + 0x47, 0x5A, 0x76, 0x63, 0x6D, 0x30, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6A, 0x41, + 0x59, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x6F, 0x4D, 0x45, 0x55, 0x6C, 0x75, 0x64, + 0x47, 0x56, 0x73, 0x0A, 0x49, 0x45, 0x4E, 0x76, 0x63, 0x6E, 0x42, 0x76, 0x63, 0x6D, + 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4D, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, + 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, + 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4C, 0x4D, 0x41, 0x6B, 0x47, + 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x0A, 0x43, + 0x7A, 0x41, 0x4A, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6C, 0x56, + 0x54, 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, + 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, + 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x4E, 0x53, 0x42, 0x2F, 0x37, + 0x74, 0x32, 0x31, 0x6C, 0x58, 0x53, 0x4F, 0x0A, 0x32, 0x43, 0x75, 0x7A, 0x70, 0x78, + 0x77, 0x37, 0x34, 0x65, 0x4A, 0x42, 0x37, 0x32, 0x45, 0x79, 0x44, 0x47, 0x67, 0x57, + 0x35, 0x72, 0x58, 0x43, 0x74, 0x78, 0x32, 0x74, 0x56, 0x54, 0x4C, 0x71, 0x36, 0x68, + 0x4B, 0x6B, 0x36, 0x7A, 0x2B, 0x55, 0x69, 0x52, 0x5A, 0x43, 0x6E, 0x71, 0x52, 0x37, + 0x70, 0x73, 0x4F, 0x76, 0x67, 0x71, 0x46, 0x65, 0x53, 0x78, 0x6C, 0x6D, 0x54, 0x6C, + 0x4A, 0x6C, 0x0A, 0x65, 0x54, 0x6D, 0x69, 0x32, 0x57, 0x59, 0x7A, 0x33, 0x71, 0x4F, + 0x42, 0x75, 0x7A, 0x43, 0x42, 0x75, 0x44, 0x41, 0x66, 0x42, 0x67, 0x4E, 0x56, 0x48, + 0x53, 0x4D, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, 0x42, 0x51, 0x69, 0x5A, 0x51, 0x7A, + 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4F, 0x44, 0x74, 0x4A, 0x56, 0x53, 0x76, + 0x31, 0x41, 0x62, 0x4F, 0x53, 0x63, 0x47, 0x72, 0x44, 0x42, 0x53, 0x0A, 0x42, 0x67, + 0x4E, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7A, 0x42, 0x4A, 0x4D, 0x45, 0x65, 0x67, + 0x52, 0x61, 0x42, 0x44, 0x68, 0x6B, 0x46, 0x6F, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7A, + 0x6F, 0x76, 0x4C, 0x32, 0x4E, 0x6C, 0x63, 0x6E, 0x52, 0x70, 0x5A, 0x6D, 0x6C, 0x6A, + 0x59, 0x58, 0x52, 0x6C, 0x63, 0x79, 0x35, 0x30, 0x63, 0x6E, 0x56, 0x7A, 0x64, 0x47, + 0x56, 0x6B, 0x63, 0x32, 0x56, 0x79, 0x0A, 0x64, 0x6D, 0x6C, 0x6A, 0x5A, 0x58, 0x4D, + 0x75, 0x61, 0x57, 0x35, 0x30, 0x5A, 0x57, 0x77, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4C, + 0x30, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, 0x30, 0x64, 0x59, 0x55, 0x6D, 0x39, + 0x76, 0x64, 0x45, 0x4E, 0x42, 0x4C, 0x6D, 0x52, 0x6C, 0x63, 0x6A, 0x41, 0x64, 0x42, + 0x67, 0x4E, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, 0x67, 0x51, 0x55, 0x6C, 0x57, 0x39, + 0x64, 0x0A, 0x7A, 0x62, 0x30, 0x62, 0x34, 0x65, 0x6C, 0x41, 0x53, 0x63, 0x6E, 0x55, + 0x39, 0x44, 0x50, 0x4F, 0x41, 0x56, 0x63, 0x4C, 0x33, 0x6C, 0x51, 0x77, 0x44, 0x67, + 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, 0x48, 0x2F, 0x42, 0x41, 0x51, 0x44, + 0x41, 0x67, 0x45, 0x47, 0x4D, 0x42, 0x49, 0x47, 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, + 0x45, 0x42, 0x2F, 0x77, 0x51, 0x49, 0x4D, 0x41, 0x59, 0x42, 0x0A, 0x41, 0x66, 0x38, + 0x43, 0x41, 0x51, 0x41, 0x77, 0x43, 0x67, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, + 0x6A, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x52, 0x77, 0x41, 0x77, 0x52, 0x41, 0x49, + 0x67, 0x58, 0x73, 0x56, 0x6B, 0x69, 0x30, 0x77, 0x2B, 0x69, 0x36, 0x56, 0x59, 0x47, + 0x57, 0x33, 0x55, 0x46, 0x2F, 0x32, 0x32, 0x75, 0x61, 0x58, 0x65, 0x30, 0x59, 0x4A, + 0x44, 0x6A, 0x31, 0x55, 0x65, 0x0A, 0x6E, 0x41, 0x2B, 0x54, 0x6A, 0x44, 0x31, 0x61, + 0x69, 0x35, 0x63, 0x43, 0x49, 0x43, 0x59, 0x62, 0x31, 0x53, 0x41, 0x6D, 0x44, 0x35, + 0x78, 0x6B, 0x66, 0x54, 0x56, 0x70, 0x76, 0x6F, 0x34, 0x55, 0x6F, 0x79, 0x69, 0x53, + 0x59, 0x78, 0x72, 0x44, 0x57, 0x4C, 0x6D, 0x55, 0x52, 0x34, 0x43, 0x49, 0x39, 0x4E, + 0x4B, 0x79, 0x66, 0x50, 0x4E, 0x2B, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, + 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, + 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, + 0x4E, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, + 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x43, 0x6A, 0x7A, 0x43, 0x43, 0x41, + 0x6A, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55, 0x49, 0x6D, 0x55, + 0x4D, 0x31, 0x6C, 0x71, 0x64, 0x4E, 0x49, 0x6E, 0x7A, 0x67, 0x37, 0x53, 0x56, 0x55, + 0x72, 0x39, 0x51, 0x47, 0x7A, 0x6B, 0x6E, 0x42, 0x71, 0x77, 0x77, 0x43, 0x67, 0x59, + 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x45, 0x41, 0x77, 0x49, 0x77, 0x0A, + 0x61, 0x44, 0x45, 0x61, 0x4D, 0x42, 0x67, 0x47, 0x41, 0x31, 0x55, 0x45, 0x41, 0x77, + 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5A, 0x57, 0x77, 0x67, 0x55, 0x30, 0x64, 0x59, + 0x49, 0x46, 0x4A, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, 0x30, 0x45, 0x78, 0x47, 0x6A, + 0x41, 0x59, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x6F, 0x4D, 0x45, 0x55, 0x6C, 0x75, + 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4E, 0x76, 0x0A, 0x63, 0x6E, 0x42, 0x76, 0x63, + 0x6D, 0x46, 0x30, 0x61, 0x57, 0x39, 0x75, 0x4D, 0x52, 0x51, 0x77, 0x45, 0x67, 0x59, + 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, 0x59, 0x57, 0x35, 0x30, 0x59, + 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, 0x45, 0x4C, 0x4D, 0x41, 0x6B, + 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, 0x51, 0x30, 0x45, 0x78, 0x43, + 0x7A, 0x41, 0x4A, 0x0A, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6C, + 0x56, 0x54, 0x4D, 0x42, 0x34, 0x58, 0x44, 0x54, 0x45, 0x34, 0x4D, 0x44, 0x55, 0x79, + 0x4D, 0x54, 0x45, 0x77, 0x4E, 0x44, 0x55, 0x78, 0x4D, 0x46, 0x6F, 0x58, 0x44, 0x54, + 0x51, 0x35, 0x4D, 0x54, 0x49, 0x7A, 0x4D, 0x54, 0x49, 0x7A, 0x4E, 0x54, 0x6B, 0x31, + 0x4F, 0x56, 0x6F, 0x77, 0x61, 0x44, 0x45, 0x61, 0x4D, 0x42, 0x67, 0x47, 0x0A, 0x41, + 0x31, 0x55, 0x45, 0x41, 0x77, 0x77, 0x52, 0x53, 0x57, 0x35, 0x30, 0x5A, 0x57, 0x77, + 0x67, 0x55, 0x30, 0x64, 0x59, 0x49, 0x46, 0x4A, 0x76, 0x62, 0x33, 0x51, 0x67, 0x51, + 0x30, 0x45, 0x78, 0x47, 0x6A, 0x41, 0x59, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, 0x6F, + 0x4D, 0x45, 0x55, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x49, 0x45, 0x4E, 0x76, 0x63, + 0x6E, 0x42, 0x76, 0x63, 0x6D, 0x46, 0x30, 0x0A, 0x61, 0x57, 0x39, 0x75, 0x4D, 0x52, + 0x51, 0x77, 0x45, 0x67, 0x59, 0x44, 0x56, 0x51, 0x51, 0x48, 0x44, 0x41, 0x74, 0x54, + 0x59, 0x57, 0x35, 0x30, 0x59, 0x53, 0x42, 0x44, 0x62, 0x47, 0x46, 0x79, 0x59, 0x54, + 0x45, 0x4C, 0x4D, 0x41, 0x6B, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x43, + 0x51, 0x30, 0x45, 0x78, 0x43, 0x7A, 0x41, 0x4A, 0x42, 0x67, 0x4E, 0x56, 0x42, 0x41, + 0x59, 0x54, 0x0A, 0x41, 0x6C, 0x56, 0x54, 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, + 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, + 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, + 0x45, 0x43, 0x36, 0x6E, 0x45, 0x77, 0x4D, 0x44, 0x49, 0x59, 0x5A, 0x4F, 0x6A, 0x2F, + 0x69, 0x50, 0x57, 0x73, 0x43, 0x7A, 0x61, 0x45, 0x4B, 0x69, 0x37, 0x0A, 0x31, 0x4F, + 0x69, 0x4F, 0x53, 0x4C, 0x52, 0x46, 0x68, 0x57, 0x47, 0x6A, 0x62, 0x6E, 0x42, 0x56, + 0x4A, 0x66, 0x56, 0x6E, 0x6B, 0x59, 0x34, 0x75, 0x33, 0x49, 0x6A, 0x6B, 0x44, 0x59, + 0x59, 0x4C, 0x30, 0x4D, 0x78, 0x4F, 0x34, 0x6D, 0x71, 0x73, 0x79, 0x59, 0x6A, 0x6C, + 0x42, 0x61, 0x6C, 0x54, 0x56, 0x59, 0x78, 0x46, 0x50, 0x32, 0x73, 0x4A, 0x42, 0x4B, + 0x35, 0x7A, 0x6C, 0x4B, 0x4F, 0x42, 0x0A, 0x75, 0x7A, 0x43, 0x42, 0x75, 0x44, 0x41, + 0x66, 0x42, 0x67, 0x4E, 0x56, 0x48, 0x53, 0x4D, 0x45, 0x47, 0x44, 0x41, 0x57, 0x67, + 0x42, 0x51, 0x69, 0x5A, 0x51, 0x7A, 0x57, 0x57, 0x70, 0x30, 0x30, 0x69, 0x66, 0x4F, + 0x44, 0x74, 0x4A, 0x56, 0x53, 0x76, 0x31, 0x41, 0x62, 0x4F, 0x53, 0x63, 0x47, 0x72, + 0x44, 0x42, 0x53, 0x42, 0x67, 0x4E, 0x56, 0x48, 0x52, 0x38, 0x45, 0x53, 0x7A, 0x42, + 0x4A, 0x0A, 0x4D, 0x45, 0x65, 0x67, 0x52, 0x61, 0x42, 0x44, 0x68, 0x6B, 0x46, 0x6F, + 0x64, 0x48, 0x52, 0x77, 0x63, 0x7A, 0x6F, 0x76, 0x4C, 0x32, 0x4E, 0x6C, 0x63, 0x6E, + 0x52, 0x70, 0x5A, 0x6D, 0x6C, 0x6A, 0x59, 0x58, 0x52, 0x6C, 0x63, 0x79, 0x35, 0x30, + 0x63, 0x6E, 0x56, 0x7A, 0x64, 0x47, 0x56, 0x6B, 0x63, 0x32, 0x56, 0x79, 0x64, 0x6D, + 0x6C, 0x6A, 0x5A, 0x58, 0x4D, 0x75, 0x61, 0x57, 0x35, 0x30, 0x0A, 0x5A, 0x57, 0x77, + 0x75, 0x59, 0x32, 0x39, 0x74, 0x4C, 0x30, 0x6C, 0x75, 0x64, 0x47, 0x56, 0x73, 0x55, + 0x30, 0x64, 0x59, 0x55, 0x6D, 0x39, 0x76, 0x64, 0x45, 0x4E, 0x42, 0x4C, 0x6D, 0x52, + 0x6C, 0x63, 0x6A, 0x41, 0x64, 0x42, 0x67, 0x4E, 0x56, 0x48, 0x51, 0x34, 0x45, 0x46, + 0x67, 0x51, 0x55, 0x49, 0x6D, 0x55, 0x4D, 0x31, 0x6C, 0x71, 0x64, 0x4E, 0x49, 0x6E, + 0x7A, 0x67, 0x37, 0x53, 0x56, 0x0A, 0x55, 0x72, 0x39, 0x51, 0x47, 0x7A, 0x6B, 0x6E, + 0x42, 0x71, 0x77, 0x77, 0x44, 0x67, 0x59, 0x44, 0x56, 0x52, 0x30, 0x50, 0x41, 0x51, + 0x48, 0x2F, 0x42, 0x41, 0x51, 0x44, 0x41, 0x67, 0x45, 0x47, 0x4D, 0x42, 0x49, 0x47, + 0x41, 0x31, 0x55, 0x64, 0x45, 0x77, 0x45, 0x42, 0x2F, 0x77, 0x51, 0x49, 0x4D, 0x41, + 0x59, 0x42, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x45, 0x77, 0x43, 0x67, 0x59, 0x49, + 0x0A, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x45, 0x41, 0x77, 0x49, 0x44, 0x53, + 0x51, 0x41, 0x77, 0x52, 0x67, 0x49, 0x68, 0x41, 0x4F, 0x57, 0x2F, 0x35, 0x51, 0x6B, + 0x52, 0x2B, 0x53, 0x39, 0x43, 0x69, 0x53, 0x44, 0x63, 0x4E, 0x6F, 0x6F, 0x77, 0x4C, + 0x75, 0x50, 0x52, 0x4C, 0x73, 0x57, 0x47, 0x66, 0x2F, 0x59, 0x69, 0x37, 0x47, 0x53, + 0x58, 0x39, 0x34, 0x42, 0x67, 0x77, 0x54, 0x77, 0x67, 0x0A, 0x41, 0x69, 0x45, 0x41, + 0x34, 0x4A, 0x30, 0x6C, 0x72, 0x48, 0x6F, 0x4D, 0x73, 0x2B, 0x58, 0x6F, 0x35, 0x6F, + 0x2F, 0x73, 0x58, 0x36, 0x4F, 0x39, 0x51, 0x57, 0x78, 0x48, 0x52, 0x41, 0x76, 0x5A, + 0x55, 0x47, 0x4F, 0x64, 0x52, 0x51, 0x37, 0x63, 0x76, 0x71, 0x52, 0x58, 0x61, 0x71, + 0x49, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x43, 0x45, + 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x0A, 0x00, + ]; + let expected_ethereum_addr = [ + 0x62, 0x73, 0x06, 0x09, 0x0A, 0xBA, 0xB3, 0xA6, 0xE1, 0x40, 0x0E, 0x93, 0x45, 0xBC, + 0x60, 0xC7, 0x8A, 0x8B, 0xEF, 0x57, + ]; + let expected_reportdata = ReportData::V1(ReportDataV1 { + ethereum_address: expected_ethereum_addr, + }); + let expected_reportdata_bytes: [u8; 64] = expected_reportdata.into(); + assert_eq!( + expected_reportdata_bytes, + [ + 0x62, 0x73, 0x06, 0x09, 0x0A, 0xBA, 0xB3, 0xA6, 0xE1, 0x40, 0x0E, 0x93, 0x45, 0xBC, + 0x60, 0xC7, 0x8A, 0x8B, 0xEF, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ] + ); + + let quote = Quote::parse("e).unwrap(); + let expected_mrsigner = + hex::decode("c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d") + .unwrap(); + + if let Report::SgxEnclave(report_body) = quote.report { + let expected_reportdata = hex::encode(expected_reportdata_bytes); + let expected_mrsigner = hex::encode(expected_mrsigner); + let actual_reportdata = hex::encode(&report_body.report_data[..]); + let actual_mrsigner = hex::encode(&report_body.mr_signer[..]); + assert_eq!(expected_reportdata, actual_reportdata); + assert_eq!(expected_mrsigner, actual_mrsigner); + } else { + panic!("SGX quote expected"); + } + } } diff --git a/deny.toml b/deny.toml index 72b56af..bbe34d2 100644 --- a/deny.toml +++ b/deny.toml @@ -1,15 +1,15 @@ +[graph] targets = [] all-features = false no-default-features = false + +[output] feature-depth = 1 [advisories] db-path = "~/.cargo/advisory-db" db-urls = ["https://github.com/rustsec/advisory-db"] -vulnerability = "deny" -unmaintained = "warn" yanked = "warn" -notice = "warn" ignore = [ # Sidechannel attack to get the private key https://rustsec.org/advisories/RUSTSEC-2023-0071 # currently no rsa private key is used in the codebase, @@ -20,8 +20,6 @@ ignore = [ ] [licenses] -unlicensed = "deny" -copyleft = "deny" allow = [ "MIT", "Apache-2.0", @@ -29,14 +27,14 @@ allow = [ "Unlicense", "MPL-2.0", "Unicode-DFS-2016", + "Unicode-3.0", "BSD-2-Clause", "BSD-3-Clause", "OpenSSL", "CC0-1.0", + "Zlib", + "CDLA-Permissive-2.0", ] -deny = [] -allow-osi-fsf-free = "neither" -default = "deny" confidence-threshold = 0.8 exceptions = [] @@ -46,6 +44,18 @@ version = "*" expression = "MIT AND ISC AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] +[[licenses.clarify]] +name = "tdx-attest-sys" +version = "*" +expression = "BSD-3-Clause" +license-files = [] + +[[licenses.clarify]] +name = "tdx-attest-rs" +version = "*" +expression = "BSD-3-Clause" +license-files = [] + [licenses.private] ignore = false registries = [] diff --git a/examples/README.md b/examples/README.md index 10b9fc4..c4ad6c0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -11,13 +11,13 @@ $ docker compose up ```bash ❯ cd teepot -❯ gpg --export username@example.com | base64 > gpgkey.pub +❯ gpg --export username@example.com | base64 > assets/gpgkey.pub ❯ export GPG_TTY="$(tty)" ❯ gpg-connect-agent updatestartuptty /bye -❯ RUST_LOG=info cargo run -p vault-unseal -- --sgx-mrsigner c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d --sgx-allowed-tcb-levels SwHardeningNeeded --server https://20.172.154.218:8443 init --unseal-threshold 1 -u bin/tee-vault-admin/tests/data/gpgkey.pub --admin-threshold 1 -a bin/tee-vault-admin/tests/data/gpgkey.pub --admin-tee-mrenclave 21c8c1a4dbcce04798f5119eb47203084bc74e564a3c954d1a21172c656cb801 +❯ RUST_LOG=info cargo run -p vault-unseal -- --sgx-mrsigner c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d --sgx-allowed-tcb-levels SwHardeningNeeded --server https://20.172.154.218:8443 init --unseal-threshold 1 -u assets/gpgkey.pub --admin-threshold 1 -a assets/gpgkey.pub --admin-tee-mrenclave 21c8c1a4dbcce04798f5119eb47203084bc74e564a3c954d1a21172c656cb801 Finished dev [unoptimized + debuginfo] target(s) in 0.09s - Running `target/debug/vault-unseal --sgx-mrsigner c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d --sgx-allowed-tcb-levels SwHardeningNeeded --server 'https://20.172.154.218:8443' init --unseal-threshold 1 -u bin/tee-vault-admin/tests/data/gpgkey.pub --admin-threshold 1 -a bin/tee-vault-admin/tests/data/gpgkey.pub --admin-tee-mrenclave 21c8c1a4dbcce04798f5119eb47203084bc74e564a3c954d1a21172c656cb801` + Running `target/debug/vault-unseal --sgx-mrsigner c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d --sgx-allowed-tcb-levels SwHardeningNeeded --server 'https://20.172.154.218:8443' init --unseal-threshold 1 -u assets/gpgkey.pub --admin-threshold 1 -a assets/gpgkey.pub --admin-tee-mrenclave 21c8c1a4dbcce04798f5119eb47203084bc74e564a3c954d1a21172c656cb801` 2023-08-23T14:47:56.902422Z INFO tee_client: Getting attestation report 2023-08-23T14:47:57.340877Z INFO tee_client: Checked or set server certificate public key hash `b4bf52fdb37431c8531fb310be389c2d17ad9bd41d662e10308c9147c007d0d0` 2023-08-23T14:47:57.741599Z INFO tee_client: Verifying attestation report @@ -121,7 +121,7 @@ Passphrase: Find out the `mr_enclave` value of the teepot-vault-admin-sgx-azure enclave and extract the sigstruct file: ```bash -❯ docker run -v .:/mnt --pull always -it matterlabsrobot/teepot-vault-admin-sgx-azure:latest 'gramine-sgx-sigstruct-view teepot-vault-admin-sgx-azure.sig; cp teepot-vault-admin-sgx-azure.sig /mnt' +❯ docker run -v .:/mnt --pull always -it ghcr.io/matter-labs/teepot-vault-admin-sgx-azure:latest 'gramine-sgx-sigstruct-view teepot-vault-admin-sgx-azure.sig; cp teepot-vault-admin-sgx-azure.sig /mnt' [...] Attributes: mr_signer: c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d @@ -155,9 +155,9 @@ container. --server https://127.0.0.1:8443 \ init \ --unseal-threshold 1 \ - --unseal-pgp-key-file ./tests/data/gpgkey.pub \ + --unseal-pgp-key-file ./assets/gpgkey.pub \ --admin-threshold 1 \ - --admin-pgp-key-file ./tests/data/gpgkey.pub \ + --admin-pgp-key-file ./assets/gpgkey.pub \ --admin-tee-mrenclave 98a540dd7056584e2009c7cf7374f932fbb8e30a4c66cc815c9809620653f751 ``` @@ -226,18 +226,18 @@ Next is to sign the admin tee with the vault-admin tool: ``` Then replace `teepot-vault-admin-sgx-azure.sig` with `teepot-vault-admin-sgx-azure-new.sig` in the container -image `matterlabsrobot/teepot-vault-admin-sgx-azure:latest` with this Dockerfile: +image `ghcr.io/matter-labs/teepot-vault-admin-sgx-azure:latest` with this Dockerfile: ```Dockerfile -FROM matterlabsrobot/teepot-vault-admin-sgx-azure:latest +FROM ghcr.io/matter-labs/teepot-vault-admin-sgx-azure:latest COPY teepot-vault-admin-sgx-azure-new.sig /app/teepot-vault-admin-sgx-azure.sig ``` Build and push the new image: ```bash -❯ docker build -t matterlabsrobot/teepot-vault-admin-sgx-azure-signed:latest . -❯ docker push matterlabsrobot/teepot-vault-admin-sgx-azure-signed:latest +❯ docker build -t ghcr.io/matter-labs/teepot-vault-admin-sgx-azure-signed:latest . +❯ docker push ghcr.io/matter-labs/teepot-vault-admin-sgx-azure-signed:latest ``` Delete the old vault-admin pod and start the new one: diff --git a/examples/k8s/vault-1-pod.yaml b/examples/k8s/vault-1-pod.yaml index 2e3f181..8f36550 100644 --- a/examples/k8s/vault-1-pod.yaml +++ b/examples/k8s/vault-1-pod.yaml @@ -27,7 +27,7 @@ spec: imagePullSecrets: - name: docker-regcred containers: - - image: matterlabsrobot/teepot-vault-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-sgx-azure:latest name: vault imagePullPolicy: Always env: @@ -62,7 +62,7 @@ spec: volumeMounts: - mountPath: /opt/vault/data name: data-1 - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/examples/k8s/vault-2-pod.yaml b/examples/k8s/vault-2-pod.yaml index dd69d42..69b8d72 100644 --- a/examples/k8s/vault-2-pod.yaml +++ b/examples/k8s/vault-2-pod.yaml @@ -27,7 +27,7 @@ spec: imagePullSecrets: - name: docker-regcred containers: - - image: matterlabsrobot/teepot-vault-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-sgx-azure:latest name: vault imagePullPolicy: Always env: @@ -62,7 +62,7 @@ spec: volumeMounts: - mountPath: /opt/vault/data name: data-2 - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/examples/k8s/vault-3-pod.yaml b/examples/k8s/vault-3-pod.yaml index fb740ce..3854ef1 100644 --- a/examples/k8s/vault-3-pod.yaml +++ b/examples/k8s/vault-3-pod.yaml @@ -27,7 +27,7 @@ spec: imagePullSecrets: - name: docker-regcred containers: - - image: matterlabsrobot/teepot-vault-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-sgx-azure:latest name: vault imagePullPolicy: Always env: @@ -62,7 +62,7 @@ spec: volumeMounts: - mountPath: /opt/vault/data name: data-3 - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/examples/k8s/vault-unseal-pod-0.yaml b/examples/k8s/vault-unseal-pod-0.yaml index d358c11..0363bfd 100644 --- a/examples/k8s/vault-unseal-pod-0.yaml +++ b/examples/k8s/vault-unseal-pod-0.yaml @@ -11,7 +11,7 @@ spec: operator: Exists effect: NoSchedule containers: - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/examples/k8s/vault-unseal-pod-1.yaml b/examples/k8s/vault-unseal-pod-1.yaml index 8fdbafb..6c7d222 100644 --- a/examples/k8s/vault-unseal-pod-1.yaml +++ b/examples/k8s/vault-unseal-pod-1.yaml @@ -11,7 +11,7 @@ spec: operator: Exists effect: NoSchedule containers: - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/examples/k8s/vault-unseal-pod-2.yaml b/examples/k8s/vault-unseal-pod-2.yaml index 0b4150c..a0c9575 100644 --- a/examples/k8s/vault-unseal-pod-2.yaml +++ b/examples/k8s/vault-unseal-pod-2.yaml @@ -11,7 +11,7 @@ spec: operator: Exists effect: NoSchedule containers: - - image: matterlabsrobot/teepot-vault-unseal-sgx-azure:latest + - image: ghcr.io/matter-labs/teepot-vault-unseal-sgx-azure:latest name: vault-unseal imagePullPolicy: Always env: diff --git a/flake.lock b/flake.lock index 5432091..49ab19f 100644 --- a/flake.lock +++ b/flake.lock @@ -2,15 +2,16 @@ "nodes": { "crane": { "locked": { - "lastModified": 1725125250, - "narHash": "sha256-CB20rDD5eHikF6mMTTJdwPP1qvyoiyyw1RDUzwIaIF8=", + "lastModified": 1745454774, + "narHash": "sha256-oLvmxOnsEKGtwczxp/CwhrfmQUG2ym24OMWowcoRhH8=", "owner": "ipetkov", "repo": "crane", - "rev": "96fd12c7100e9e05fa1a0a5bd108525600ce282f", + "rev": "efd36682371678e2b6da3f108fdb5c613b3ec598", "type": "github" }, "original": { "owner": "ipetkov", + "ref": "efd36682371678e2b6da3f108fdb5c613b3ec598", "repo": "crane", "type": "github" } @@ -141,16 +142,32 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719707984, - "narHash": "sha256-RoxIr/fbndtuKqulGvNCcuzC6KdAib85Q8gXnjzA1dw=", + "lastModified": 1733550349, + "narHash": "sha256-NcGumB4Lr6KSDq+nIqXtNA8QwAQKDSZT7N9OTGWbTrs=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7dca15289a1c2990efbe4680f0923ce14139b042", + "rev": "e2605d0744c2417b09f8bf850dfca42fcf537d34", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-24.05", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-25-05": { + "locked": { + "lastModified": 1748437600, + "narHash": "sha256-hYKMs3ilp09anGO7xzfGs3JqEgUqFMnZ8GMAqI6/k04=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "7282cb574e0607e65224d33be8241eae7cfe0979", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } @@ -177,11 +194,11 @@ "snowfall-lib": "snowfall-lib" }, "locked": { - "lastModified": 1725271440, - "narHash": "sha256-CY79oPHWtDQdCDSJTMPZPYVgONAgsqCUZHr2idff53U=", + "lastModified": 1733824290, + "narHash": "sha256-8MKgW3pFW+IxsM/iGfHio91Gym89gh9uNQ3JO4+D8QY=", "owner": "matter-labs", "repo": "nixsgx", - "rev": "00bb72e3ef79c79030ab622cfe9ced39d21006cc", + "rev": "788ff5233053a52421c9f8fa626a936785dda511", "type": "github" }, "original": { @@ -216,6 +233,7 @@ "nixsgx-flake", "nixpkgs" ], + "nixpkgs-25-05": "nixpkgs-25-05", "nixsgx-flake": "nixsgx-flake", "rust-overlay": "rust-overlay", "snowfall-lib": [ @@ -233,11 +251,11 @@ ] }, "locked": { - "lastModified": 1725243956, - "narHash": "sha256-0A5ZP8uDCyBdYUzayZfy6JFdTefP79oZVAjyqA/yuSI=", + "lastModified": 1748572605, + "narHash": "sha256-k0nhPtkVDQkVJckRw6fGIeeDBktJf1BH0i8T48o7zkk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a10c8092d5f82622be79ed4dd12289f72011f850", + "rev": "405ef13a5b80a0a4d4fc87c83554423d80e5f929", "type": "github" }, "original": { @@ -265,6 +283,7 @@ }, "original": { "owner": "snowfallorg", + "ref": "c6238c83de101729c5de3a29586ba166a9a65622", "repo": "lib", "type": "github" } @@ -348,11 +367,11 @@ "nixsgx-flake": "nixsgx-flake_2" }, "locked": { - "lastModified": 1719832445, - "narHash": "sha256-Dnueq3A1sf8zT+bY6CcuaxPvX4AK7B6Sveqb8YfoY8o=", + "lastModified": 1747897304, + "narHash": "sha256-8O9ry5FaD1fkRqvHV5hPtsg5G+Z0RX6MRkazn5bmK50=", "owner": "matter-labs", "repo": "vault-auth-tee", - "rev": "2b53a4387fc8ecfb7826acd93d4895e7e810677d", + "rev": "dc802364964d9fe01b2e164e3fb3005bcdf91272", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0c6f684..f3ec3fe 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,7 @@ }; inputs = { + nixpkgs-25-05.url = "github:nixos/nixpkgs/nixos-25.05"; nixsgx-flake.url = "github:matter-labs/nixsgx"; nixpkgs.follows = "nixsgx-flake/nixpkgs"; snowfall-lib.follows = "nixsgx-flake/snowfall-lib"; @@ -21,17 +22,13 @@ inputs.nixpkgs.follows = "nixsgx-flake/nixpkgs"; }; - crane = { - url = "github:ipetkov/crane?tag=v0.17.3"; - inputs.nixpkgs.follows = "nixsgx-flake/nixpkgs"; - }; + crane.url = "github:ipetkov/crane?ref=efd36682371678e2b6da3f108fdb5c613b3ec598"; # v0.20.3 }; outputs = inputs: - let src = ./.; in inputs.snowfall-lib.mkFlake { inherit inputs; - inherit src; + src = ./.; snowfall.namespace = "teepot"; @@ -43,8 +40,11 @@ nixsgx-flake.overlays.default vault-auth-tee-flake.overlays.default rust-overlay.overlays.default - # somehow the original `src` is not available anymore - (final: prev: { teepotCrate = prev.pkgs.callPackage ./teepot-crate.nix { inherit inputs; inherit src; }; }) + (next: prev: { + # need recent cargo-deny understanding the 2024 edition + inherit (inputs.nixpkgs-25-05.legacyPackages.${prev.system}) + cargo-deny; + }) ]; alias = { @@ -60,16 +60,7 @@ }; outputs-builder = channels: { - formatter = channels.nixpkgs.nixpkgs-fmt; - - checks = { - inherit - (channels.nixpkgs.teepot) cargoFmt; - inherit - (channels.nixpkgs.teepot) cargoClippy; - inherit - (channels.nixpkgs.teepot) cargoDeny; - }; + formatter = channels.nixpkgs.nixfmt-rfc-style; }; }; } diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..6ebd543 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,3 @@ +{ ... }: { + nixosGenerate = import ./nixos-generate.nix; +} diff --git a/lib/nixos-generate.nix b/lib/nixos-generate.nix new file mode 100644 index 0000000..7b4806c --- /dev/null +++ b/lib/nixos-generate.nix @@ -0,0 +1,33 @@ +{ pkgs +, nixosSystem +, formatModule +, system +, specialArgs ? { } +, modules ? [ ] +}: +let + image = nixosSystem { + inherit pkgs specialArgs; + modules = + [ + formatModule + ( + { lib, ... }: { + options = { + fileExtension = lib.mkOption { + type = lib.types.str; + description = "Declare the path of the wanted file in the output directory"; + default = ""; + }; + formatAttr = lib.mkOption { + type = lib.types.str; + description = "Declare the default attribute to build"; + }; + }; + } + ) + ] + ++ modules; + }; +in +image.config.system.build.${image.config.formatAttr} diff --git a/packages/cargoClippy/default.nix b/packages/cargoClippy/default.nix deleted file mode 100644 index f356dce..0000000 --- a/packages/cargoClippy/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright (c) 2024 Matter Labs -{ teepotCrate }: teepotCrate.craneLib.cargoClippy ( - teepotCrate.commonArgs // { - pname = "teepot"; - inherit (teepotCrate) cargoArtifacts; - } -) diff --git a/packages/container-self-attestation-test-sgx-azure/default.nix b/packages/container-self-attestation-test-sgx-azure/default.nix index d6f6bcb..6f25ff8 100644 --- a/packages/container-self-attestation-test-sgx-azure/default.nix +++ b/packages/container-self-attestation-test-sgx-azure/default.nix @@ -2,10 +2,12 @@ # Copyright (c) 2024 Matter Labs { teepot , pkgs +, stdenv , container-name ? "teepot-self-attestation-test-sgx-azure" , tag ? null , isAzure ? true }: +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else pkgs.lib.tee.sgxGramineContainer { name = container-name; inherit tag; diff --git a/packages/container-tdx-test/default.nix b/packages/container-tdx-test/default.nix new file mode 100644 index 0000000..9563fff --- /dev/null +++ b/packages/container-tdx-test/default.nix @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Matter Labs +{ lib +, stdenv +, openssl +, curl +, dockerTools +, buildEnv +, teepot +, nixsgx +}: +if (stdenv.hostPlatform.isDarwin) then { + # FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin +} else + dockerTools.buildLayeredImage { + name = "tdx-test"; + + config.Entrypoint = [ "${teepot.teepot.tdx_test}/bin/tdx-test" ]; + config.Env = [ + "SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt" + ]; + + contents = buildEnv { + name = "image-root"; + + paths = with dockerTools; [ + teepot.teepot.tdx_test + openssl.out + curl.out + # nixsgx.sgx-dcap.quote_verify + # nixsgx.sgx-dcap.default_qpl + usrBinEnv + binSh + caCertificates + fakeNss + ]; + pathsToLink = [ "/bin" "/lib" "/etc" "/share" ]; + }; + } diff --git a/packages/container-tee-key-preexec-dcap/default.nix b/packages/container-tee-key-preexec-dcap/default.nix new file mode 100644 index 0000000..c2ebf94 --- /dev/null +++ b/packages/container-tee-key-preexec-dcap/default.nix @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Matter Labs +{ teepot +, pkgs +, stdenv +, bash +, coreutils +, container-name ? "teepot-key-preexec-dcap" +, tag ? null +}: let + entrypoint = "${bash}/bin/bash"; +in +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else +pkgs.lib.tee.sgxGramineContainer { + name = container-name; + inherit tag entrypoint; + + packages = [ teepot.teepot.tee_key_preexec coreutils bash ]; + + manifest = { + loader = { + argv = [ + entrypoint + "-c" + ("${teepot.teepot.tee_key_preexec}/bin/tee-key-preexec -- bash -c " + + "'echo \"SIGNING_KEY=$SIGNING_KEY\"; echo \"TEE_TYPE=$TEE_TYPE\";exec base64 \"$ATTESTATION_QUOTE_FILE_PATH\";'") + ]; + + log_level = "error"; + env = { + RUST_BACKTRACE = "1"; + RUST_LOG = "trace"; + }; + }; + sgx = { + edmm_enable = true; + max_threads = 2; + }; + }; +} diff --git a/packages/container-vault-admin-sgx-azure/default.nix b/packages/container-vault-admin-sgx-azure/default.nix index cb50463..dc661ef 100644 --- a/packages/container-vault-admin-sgx-azure/default.nix +++ b/packages/container-vault-admin-sgx-azure/default.nix @@ -2,10 +2,12 @@ # Copyright (c) 2024 Matter Labs { teepot , pkgs +, stdenv , container-name ? "teepot-vault-admin-sgx-azure" , tag ? null , isAzure ? null }: +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else pkgs.lib.tee.sgxGramineContainer { name = container-name; inherit tag; diff --git a/packages/container-vault-admin/default.nix b/packages/container-vault-admin/default.nix index ec70476..ee1549e 100644 --- a/packages/container-vault-admin/default.nix +++ b/packages/container-vault-admin/default.nix @@ -2,11 +2,13 @@ # Copyright (c) 2024 Matter Labs { dockerTools , buildEnv +, stdenv , teepot , openssl , curl , nixsgx }: +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else dockerTools.buildLayeredImage { name = "vault-admin"; diff --git a/packages/container-vault-sgx-azure/default.nix b/packages/container-vault-sgx-azure/default.nix index 9d94e9e..52d88a4 100644 --- a/packages/container-vault-sgx-azure/default.nix +++ b/packages/container-vault-sgx-azure/default.nix @@ -2,6 +2,7 @@ # Copyright (c) 2024 Matter Labs { teepot , pkgs +, stdenv , vat , vault , container-name ? "teepot-vault-sgx-azure" @@ -12,6 +13,7 @@ let entrypoint = "${teepot.teepot.tee_ratls_preexec}/bin/tee-ratls-preexec"; appDir = "/opt/vault"; in +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else pkgs.lib.tee.sgxGramineContainer { name = container-name; inherit tag; @@ -86,5 +88,3 @@ pkgs.lib.tee.sgxGramineContainer { sys.experimental__enable_flock = true; }; } - - diff --git a/packages/container-vault-unseal-sgx-azure/default.nix b/packages/container-vault-unseal-sgx-azure/default.nix index c1f0774..cd2ca12 100644 --- a/packages/container-vault-unseal-sgx-azure/default.nix +++ b/packages/container-vault-unseal-sgx-azure/default.nix @@ -2,11 +2,13 @@ # Copyright (c) 2024 Matter Labs { teepot , pkgs +, stdenv , vat , container-name ? "teepot-vault-unseal-sgx-azure" , tag ? null , isAzure ? true }: +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else pkgs.lib.tee.sgxGramineContainer { name = container-name; inherit tag isAzure; diff --git a/packages/container-vault-unseal/default.nix b/packages/container-vault-unseal/default.nix index 1345d09..22979b9 100644 --- a/packages/container-vault-unseal/default.nix +++ b/packages/container-vault-unseal/default.nix @@ -1,30 +1,43 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2024 Matter Labs { dockerTools +, lib +, stdenv , buildEnv , teepot , openssl , curl , nixsgx }: -dockerTools.buildLayeredImage { - name = "vault-unseal"; +if (stdenv.hostPlatform.isDarwin) then { + # FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin +} else + dockerTools.buildLayeredImage { + name = "vault-unseal"; - config.Entrypoint = [ "${teepot.teepot.vault_unseal}/bin/vault-unseal" ]; + config.Entrypoint = [ "${teepot.teepot.vault_unseal}/bin/vault-unseal" ]; - contents = buildEnv { - name = "image-root"; - paths = with dockerTools; with nixsgx;[ - openssl.out - curl.out - sgx-dcap.quote_verify - sgx-dcap.default_qpl - usrBinEnv - binSh - caCertificates - fakeNss - teepot.teepot.vault_unseal - ]; - pathsToLink = [ "/bin" "/lib" "/etc" ]; - }; -} + contents = buildEnv { + name = "image-root"; + paths = + with dockerTools; + with nixsgx; + [ + openssl.out + curl.out + usrBinEnv + binSh + caCertificates + fakeNss + teepot.teepot.vault_unseal + ] ++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [ + sgx-dcap.quote_verify + sgx-dcap.default_qpl + ]; + pathsToLink = [ + "/bin" + "/lib" + "/etc" + ]; + }; + } diff --git a/packages/container-verify-attestation-sgx/default.nix b/packages/container-verify-attestation-sgx/default.nix index d01e540..e871549 100644 --- a/packages/container-verify-attestation-sgx/default.nix +++ b/packages/container-verify-attestation-sgx/default.nix @@ -2,30 +2,44 @@ # Copyright (c) 2024 Matter Labs { dockerTools , buildEnv +, lib +, stdenv , teepot , openssl , curl , nixsgx }: -dockerTools.buildLayeredImage { - name = "verify-attestation-sgx"; +if (stdenv.hostPlatform.isDarwin) then { + # FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin +} else + dockerTools.buildLayeredImage { + name = "verify-attestation-sgx"; - config.Entrypoint = [ "${teepot.teepot.verify_attestation}/bin/verify-attestation" ]; - config.Env = [ "LD_LIBRARY_PATH=/lib" ]; - contents = buildEnv { - name = "image-root"; + config.Entrypoint = [ "${teepot.teepot.verify_attestation}/bin/verify-attestation" ]; + config.Env = [ "LD_LIBRARY_PATH=/lib" ]; + contents = buildEnv { + name = "image-root"; - paths = with dockerTools; with nixsgx;[ - openssl.out - curl.out - sgx-dcap.quote_verify - sgx-dcap.default_qpl - teepot.teepot.verify_attestation - usrBinEnv - binSh - caCertificates - fakeNss - ]; - pathsToLink = [ "/bin" "/lib" "/etc" "/share" ]; - }; -} + paths = + with dockerTools; + with nixsgx; + [ + openssl.out + curl.out + teepot.teepot.verify_attestation + usrBinEnv + binSh + caCertificates + fakeNss + ] ++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [ + sgx-dcap.quote_verify + sgx-dcap.default_qpl + ]; + pathsToLink = [ + "/bin" + "/lib" + "/etc" + "/share" + ]; + }; + } diff --git a/packages/container-verify-era-proof-attestation-sgx/default.nix b/packages/container-verify-era-proof-attestation-sgx/default.nix index f0918d1..d8be90c 100644 --- a/packages/container-verify-era-proof-attestation-sgx/default.nix +++ b/packages/container-verify-era-proof-attestation-sgx/default.nix @@ -3,31 +3,45 @@ { dockerTools , buildEnv , teepot +, stdenv , openssl , curl , nixsgx , pkg-config }: -dockerTools.buildLayeredImage { - name = "verify-era-proof-attestation"; +if (stdenv.hostPlatform.isDarwin) then { + # FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin +} else + dockerTools.buildLayeredImage { + name = "verify-era-proof-attestation"; - config.Entrypoint = [ "${teepot.teepot.verify_era_proof_attestation}/bin/verify-era-proof-attestation" ]; - config.Env = [ "LD_LIBRARY_PATH=/lib" ]; - contents = buildEnv { - name = "image-root"; - - paths = with dockerTools; with nixsgx;[ - pkg-config - openssl.out - curl.out - sgx-dcap.quote_verify - sgx-dcap.default_qpl - teepot.teepot.verify_era_proof_attestation - usrBinEnv - binSh - caCertificates - fakeNss + config.Entrypoint = [ + "${teepot.teepot.verify_era_proof_attestation}/bin/verify-era-proof-attestation" ]; - pathsToLink = [ "/bin" "/lib" "/etc" "/share" ]; - }; -} + config.Env = [ "LD_LIBRARY_PATH=/lib" ]; + contents = buildEnv { + name = "image-root"; + + paths = + with dockerTools; + with nixsgx; + [ + pkg-config + openssl.out + curl.out + sgx-dcap.quote_verify + sgx-dcap.default_qpl + teepot.teepot.verify_era_proof_attestation + usrBinEnv + binSh + caCertificates + fakeNss + ]; + pathsToLink = [ + "/bin" + "/lib" + "/etc" + "/share" + ]; + }; + } diff --git a/packages/tdx_google/configuration.nix b/packages/tdx_google/configuration.nix new file mode 100644 index 0000000..1298769 --- /dev/null +++ b/packages/tdx_google/configuration.nix @@ -0,0 +1,135 @@ +{ lib +, modulesPath +, pkgs +, ... +}: { + imports = [ + "${toString modulesPath}/profiles/minimal.nix" + "${toString modulesPath}/profiles/qemu-guest.nix" + ./metadata.nix + ./vector.nix + ./container.nix + ]; + + services.journald.console = "/dev/ttyS0"; + systemd.services."serial-getty@ttyS0".enable = lib.mkForce false; + + # the container might want to listen on ports + networking.firewall.enable = true; + networking.firewall.allowedTCPPortRanges = [{ from = 1024; to = 65535; }]; + networking.firewall.allowedUDPPortRanges = [{ from = 1024; to = 65535; }]; + + services.resolved.enable = true; + services.resolved.llmnr = "false"; + services.resolved.extraConfig = '' + [Resolve] + MulticastDNS=no + ''; + + networking.useNetworkd = lib.mkDefault true; + + # don't fill up the logs + networking.firewall.logRefusedConnections = false; + + services.prometheus.exporters.node = { + enable = true; + port = 9100; + enabledCollectors = [ + "logind" + "systemd" + ]; + disabledCollectors = [ + "textfile" + ]; + }; + + # /var is on tmpfs anyway + services.journald.storage = "volatile"; + + # we can't rely on/trust the hypervisor + services.timesyncd.enable = false; + services.chrony = { + enable = true; + enableNTS = true; + servers = [ + "time.cloudflare.com" + "ntppool1.time.nl" + "ntppool2.time.nl" + ]; + }; + systemd.services."chronyd".after = [ "network-online.target" ]; + + boot.kernelPackages = lib.mkForce pkgs.linuxPackages_6_12; + boot.kernelPatches = [ + { + name = "tdx-rtmr"; + patch = pkgs.fetchurl { + url = "https://github.com/haraldh/linux/commit/12d08008a5c94175e7a7dfcee40dff33431d9033.patch"; + hash = "sha256-sVDhvC3qnXpL5FRxWiQotH7Nl/oqRBQGjJGyhsKeBTA="; + }; + } + ]; + + boot.kernelParams = [ + "console=ttyS0,115200n8" + "random.trust_cpu=on" + ]; + + boot.consoleLogLevel = 7; + + boot.initrd.includeDefaultModules = false; + boot.initrd.availableKernelModules = [ + "tdx_guest" + "nvme" + "sd_mod" + "dm_mod" + "ata_piix" + ]; + + boot.initrd.systemd.enable = lib.mkDefault true; + + services.logind.extraConfig = '' + NAutoVTs=0 + ReserveVT=0 + ''; + + services.dbus.implementation = "broker"; + + boot.initrd.systemd.tpm2.enable = lib.mkForce false; + systemd.tpm2.enable = lib.mkForce false; + + nix.enable = false; # it's a read-only nix store anyway + + security.pam.services.su.forwardXAuth = lib.mkForce false; + + users.mutableUsers = false; + users.allowNoPasswordLogin = true; + + system.stateVersion = lib.version; + system.switch.enable = lib.mkForce false; + + documentation.info.enable = lib.mkForce false; + documentation.nixos.enable = lib.mkForce false; + documentation.man.enable = lib.mkForce false; + documentation.enable = lib.mkForce false; + + services.udisks2.enable = false; # udisks has become too bloated to have in a headless system + + # Get rid of the perl ecosystem to minimize the TCB and disk size + + # Remove perl from activation + system.etc.overlay.enable = lib.mkDefault true; + services.userborn.enable = lib.mkDefault true; + + # Random perl remnants + system.disableInstallerTools = lib.mkForce true; + programs.less.lessopen = lib.mkDefault null; + programs.command-not-found.enable = lib.mkDefault false; + boot.enableContainers = lib.mkForce false; + boot.loader.grub.enable = lib.mkDefault false; + environment.defaultPackages = lib.mkDefault [ ]; + + # Check that the system does not contain a Nix store path that contains the + # string "perl". + system.forbiddenDependenciesRegexes = [ "perl" ]; +} diff --git a/packages/tdx_google/container.nix b/packages/tdx_google/container.nix new file mode 100644 index 0000000..3e8d8a3 --- /dev/null +++ b/packages/tdx_google/container.nix @@ -0,0 +1,51 @@ +{ lib +, modulesPath +, pkgs +, ... +}: { + virtualisation.docker.enable = true; + + systemd.services.docker_start_container = { + description = "The main application container"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" "docker.service" "vector.service" "chronyd.service" "metadata.service" ]; + requires = [ "network-online.target" "docker.service" "vector.service" "metadata.service" ]; + serviceConfig = { + Type = "exec"; + User = "root"; + EnvironmentFile = "-/run/env/env"; + }; + path = [ pkgs.docker pkgs.teepot.teepot.tdx_extend pkgs.iproute2 ]; + script = '' + set -eu -o pipefail + + # wait for vector to initialize itself + for i in {1..30}; do + if [[ $(ss -H -t -l -n sport = 4318) ]]; then + break + fi + echo "Waiting for vector to initialize itself..." >&2 + sleep 1 + done + + DIGEST=''${CONTAINER_DIGEST#sha256:} + echo "Measuring $DIGEST" >&2 + test -c /dev/tdx_guest && tdx-extend --digest "$DIGEST" --rtmr 3 + + # /sys/kernel/config is needed for attestation + docker run -d --rm \ + --name tdx_container \ + --env "GOOGLE_METADATA=1" \ + --network=host \ + --init \ + --privileged \ + -v /sys/kernel/config:/sys/kernel/config \ + "sha256:$DIGEST" + exec docker wait tdx_container + ''; + + postStop = lib.mkDefault '' + shutdown --reboot +5 + ''; + }; +} diff --git a/packages/tdx_google/default.nix b/packages/tdx_google/default.nix new file mode 100644 index 0000000..98f16a6 --- /dev/null +++ b/packages/tdx_google/default.nix @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Matter Labs +{ lib +, pkgs +, stdenv +, system +, ... +}: +if (stdenv.hostPlatform.system != "x86_64-linux") then { } else +lib.teepot.nixosGenerate { + inherit (lib) nixosSystem; + inherit system pkgs; + modules = [ + ./configuration.nix + ./google.nix + ]; + formatModule = ./verity.nix; +} diff --git a/packages/tdx_google/google.nix b/packages/tdx_google/google.nix new file mode 100644 index 0000000..2684704 --- /dev/null +++ b/packages/tdx_google/google.nix @@ -0,0 +1,33 @@ +{ lib +, pkgs +, modulesPath +, ... +}: { + imports = [ + "${toString modulesPath}/profiles/headless.nix" + ]; + + system.image.id = "tdx_base"; + + boot.initrd.kernelModules = [ "virtio_scsi" ]; + boot.kernelModules = [ "virtio_pci" "virtio_net" ]; + + # Force getting the hostname from Google Compute. + networking.hostName = lib.mkForce ""; + + # Configure default metadata hostnames + networking.extraHosts = '' + 169.254.169.254 metadata.google.internal metadata + ''; + + networking.timeServers = [ "metadata.google.internal" ]; + + environment.etc."sysctl.d/60-gce-network-security.conf".source = "${pkgs.google-guest-configs}/etc/sysctl.d/60-gce-network-security.conf"; + + networking.usePredictableInterfaceNames = false; + + # GC has 1460 MTU + networking.interfaces.eth0.mtu = 1460; + + boot.extraModprobeConfig = lib.readFile "${pkgs.google-guest-configs}/etc/modprobe.d/gce-blacklist.conf"; +} diff --git a/packages/tdx_google/metadata.nix b/packages/tdx_google/metadata.nix new file mode 100644 index 0000000..1e7e182 --- /dev/null +++ b/packages/tdx_google/metadata.nix @@ -0,0 +1,56 @@ +{ lib +, modulesPath +, pkgs +, ... +}: { + systemd.services.metadata = { + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + enable = true; + path = [ pkgs.curl pkgs.docker pkgs.teepot.teepot.tdx_extend pkgs.coreutils ]; + wantedBy = [ "default.target" ]; + after = [ "network-online.target" "docker.service" ]; + requires = [ "network-online.target" "docker.service" ]; + script = '' + set -eu -o pipefail + : "''${CONTAINER_HUB:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_hub" -H "Metadata-Flavor: Google")}" + : "''${CONTAINER_IMAGE:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_image" -H "Metadata-Flavor: Google")}" + : "''${CONTAINER_TOKEN:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_token" -H "Metadata-Flavor: Google")}" + : "''${CONTAINER_USER:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/container_user" -H "Metadata-Flavor: Google")}" + : "''${HOST_ID:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/id" -H "Metadata-Flavor: Google")}" + : "''${HOST_IMAGE:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/image" -H "Metadata-Flavor: Google")}" + : "''${HOST_NAME:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/hostname" -H "Metadata-Flavor: Google")}" + : "''${KAFKA_TOPIC:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/kafka_topic" -H "Metadata-Flavor: Google")}" + : "''${KAFKA_URLS:=$(curl --silent --fail "http://metadata.google.internal/computeMetadata/v1/instance/attributes/kafka_urls" -H "Metadata-Flavor: Google")}" + + : "''${CONTAINER_IMAGE:?Error: Missing CONTAINER_IMAGE}" + : "''${CONTAINER_HUB:?Error: Missing CONTAINER_HUB}" + + if [[ $CONTAINER_USER ]] && [[ $CONTAINER_TOKEN ]]; then + docker login -u "$CONTAINER_USER" -p "$CONTAINER_TOKEN" "$CONTAINER_HUB" + fi + + docker pull "''${CONTAINER_HUB}/''${CONTAINER_IMAGE}" + CONTAINER_DIGEST=$(docker inspect --format '{{.Id}}' "''${CONTAINER_HUB}/''${CONTAINER_IMAGE}") + + mkdir -p /run/env + cat >/run/env/env <> $out/nix-support/propagated-user-env-packages + echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages binname=''${i//_/-} mv "$out/bin/$binname" "''${!i}/bin/" + '' + lib.optionalString (stdenv.hostPlatform.system == "x86_64-linux") '' + makeWrapper "''${!i}/bin/$binname" "''${!i}/bin/$binname-dcap" \ + --prefix LD_LIBRARY_PATH : "${ + lib.makeLibraryPath [ + pkgs.nixsgx.sgx-dcap.quote_verify + pkgs.nixsgx.sgx-dcap.default_qpl + pkgs.curl + ] + }" \ + --set-default QCNL_CONF_PATH "${pkgs.nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf" + '' + '' done + rmdir "$out/bin" ''; } ) - diff --git a/packages/teepotCrate/default.nix b/packages/teepotCrate/default.nix new file mode 100644 index 0000000..66006b5 --- /dev/null +++ b/packages/teepotCrate/default.nix @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Matter Labs +{ lib +, inputs +, stdenv +, makeRustPlatform +, nixsgx ? null +, pkg-config +, rust-bin +, pkgs +, openssl +, darwin +}: +let + rustVersion = rust-bin.fromRustupToolchainFile (inputs.src + "/rust-toolchain.toml"); + rustPlatform = makeRustPlatform { + cargo = rustVersion; + rustc = rustVersion; + }; + craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rustVersion; + commonArgs = { + nativeBuildInputs = [ + pkg-config + rustPlatform.bindgenHook + ]; + + buildInputs = [ + openssl.dev + ] + ++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [ + nixsgx.sgx-sdk + nixsgx.sgx-dcap + nixsgx.sgx-dcap.quote_verify + nixsgx.sgx-dcap.libtdx_attest + ] ++ lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.Security + ]; + + strictDeps = true; + + src = with lib.fileset; toSource { + root = inputs.src; + fileset = unions [ + # Default files from crane (Rust and cargo files) + (craneLib.fileset.commonCargoSources inputs.src) + (fileFilter (file: file.hasExt "hcl") (inputs.src + "/crates/teepot-vault/bin")) + # deny.toml and friends + (fileFilter (file: file.hasExt "toml") inputs.src) + # Custom test data files + (maybeMissing (inputs.src + "/crates/teepot/tests/data")) + (maybeMissing (inputs.src + "/crates/teepot-vault/tests/data")) + (maybeMissing (inputs.src + "/crates/intel-dcap-api/tests/test_data")) + # special files + (maybeMissing (inputs.src + "/crates/teepot-tdx-attest-sys/bindings.h")) + ]; + }; + + checkType = "debug"; + env = { + OPENSSL_NO_VENDOR = "1"; + NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa"; + }; + }; + + cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { + pname = "teepot-workspace"; + }); +in +{ + inherit rustPlatform + rustVersion + commonArgs + craneLib + cargoArtifacts; +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index bac2303..f259a5e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.78" +channel = "1.87" components = ["rustfmt", "clippy", "rust-src"] diff --git a/shells/teepot/default.nix b/shells/teepot/default.nix index faeb573..ac783f4 100644 --- a/shells/teepot/default.nix +++ b/shells/teepot/default.nix @@ -1,24 +1,64 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2024 Matter Labs { lib +, pkgs , mkShell , teepot -, dive -, taplo -, vault -, cargo-release +, nixsgx +, stdenv +, }: +let + toolchain_with_src = ( + teepot.teepot.passthru.rustVersion.override { + extensions = [ + "rustfmt" + "clippy" + "rust-src" + ]; + } + ); +in mkShell { inputsFrom = [ teepot.teepot ]; - shellHook = '' - export OPENSSL_NO_VENDOR="1"; - ''; - - packages = [ - dive - taplo - vault - cargo-release + nativeBuildInputs = with pkgs; [ + toolchain_with_src + pkg-config + teepot.teepot.passthru.rustPlatform.bindgenHook ]; + + packages = + with pkgs; + [ + dive + taplo + vault + cargo-release + azure-cli + kubectl + kubectx + k9s + google-cloud-sdk + ]; + + env = { + QCNL_CONF_PATH = + if (stdenv.hostPlatform.system != "x86_64-linux") then + "" + else + "${nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf"; + OPENSSL_NO_VENDOR = "1"; + RUST_SRC_PATH = "${toolchain_with_src}/lib/rustlib/src/rust/"; + }; + + shellHook = '' + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ + pkgs.lib.makeLibraryPath (lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [ + pkgs.curl + nixsgx.sgx-dcap + nixsgx.sgx-dcap.quote_verify + nixsgx.sgx-dcap.default_qpl + ])}" + ''; } diff --git a/systems/x86_64-linux/tdxtest/default.nix b/systems/x86_64-linux/tdxtest/default.nix new file mode 100644 index 0000000..f8c425e --- /dev/null +++ b/systems/x86_64-linux/tdxtest/default.nix @@ -0,0 +1,84 @@ +{ config +, pkgs +, lib +, ... +}: { + imports = [ + ./../../../packages/tdx_google/configuration.nix + ]; + + networking.hosts = { + "127.0.0.100" = [ "metadata.google.internal" ]; + # might want to run kafka on the testing host + "10.0.2.2" = [ "kafka" ]; + }; + + # emulate metadata.google.internal + services.static-web-server = { + enable = true; + listen = "127.0.0.100:80"; + root = ./web-root; + }; + + # systemd.services.vector = { + # environment = { + # KAFKA_URLS = "10.0.2.2:9092"; + # KAFKA_TOPIC = "tdx-google-test"; + # }; + # }; + + systemd.services.docker_start_container = { + # environment = { + # CONTAINER_IMAGE = "ghcr.io/matter-labs/tdx-test:pnj1ryxxb8gbzk9wh18s9bcqrzr1z9ff"; + # CONTAINER_HUB = "docker.io"; + # CONTAINER_TOKEN = ""; + # CONTAINER_USER = ""; + # }; + + postStop = '' + : + ''; + }; + + console.enable = true; + + services.getty.autologinUser = lib.mkOverride 999 "root"; + + networking.firewall.allowedTCPPorts = [ 22 ]; + services.sshd.enable = true; + services.openssh.settings.PermitRootLogin = lib.mkOverride 999 "yes"; + users.users.root.openssh.authorizedKeys.keys = [ + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDsb/Tr69YN5MQLweWPuJaRGm+h2kOyxfD6sqKEDTIwoAAAABHNzaDo=" + "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBACLgT81iB1iWWVuXq6PdQ5GAAGhaZhSKnveQCvcNnAOZ5WKH80bZShKHyAYzrzbp8IGwLWJcZQ7TqRK+qZdfagAAAAEc3NoOg==" + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAYbUTKpy4QR3s944/hjJ1UK05asFEs/SmWeUbtS0cdA660sT4xHnRfals73FicOoz+uIucJCwn/SCM804j+wtM=" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMNsmP15vH8BVKo7bdvIiiEjiQboPGcRPqJK0+bH4jKD" + ]; + + environment.systemPackages = with pkgs; [ + strace + tcpdump + static-web-server + ]; + + + fileSystems = { + "/" = { + fsType = "ext4"; + device = "/dev/disk/by-id/test"; + options = [ "mode=0755" ]; + }; + }; + + boot = { + loader.grub.enable = false; + initrd.systemd.enable = true; + }; + + virtualisation.vmVariant = { + # following configuration is added only when building VM with build-vm + virtualisation = { + memorySize = 2048; # Use 2048MiB memory. + cores = 4; + }; + }; +} diff --git a/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_config b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_config new file mode 100644 index 0000000..3258906 --- /dev/null +++ b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_config @@ -0,0 +1,21 @@ +{ + "server": { + "port": 8080, + "timeout_seconds": 30 + }, + "metrics": { + "port": 9000 + }, + "telemetry": { + "otlp": { + "enable": true, + "endpoint": "http://127.0.0.1:4317", + "protocol": "grpc" + }, + "logging": { + "level": "trace", + "console": true, + "json": false + } + } +} diff --git a/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_hub b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_hub new file mode 100644 index 0000000..243e482 --- /dev/null +++ b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_hub @@ -0,0 +1 @@ +docker.io diff --git a/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_image b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_image new file mode 100644 index 0000000..6049602 --- /dev/null +++ b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/container_image @@ -0,0 +1 @@ +ghcr.io/matter-labs/tdx-test:81hgl91s5hj0sb83c7ij9acf2s5qjvb5 diff --git a/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_topic b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_topic new file mode 100644 index 0000000..fba611d --- /dev/null +++ b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_topic @@ -0,0 +1 @@ +tdx-google diff --git a/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_urls b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_urls new file mode 100644 index 0000000..8758015 --- /dev/null +++ b/systems/x86_64-linux/tdxtest/web-root/computeMetadata/v1/instance/attributes/kafka_urls @@ -0,0 +1 @@ +10.0.2.2:9092 diff --git a/teepot-crate.nix b/teepot-crate.nix deleted file mode 100644 index 1b2081d..0000000 --- a/teepot-crate.nix +++ /dev/null @@ -1,65 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright (c) 2024 Matter Labs -{ lib -, inputs -, makeRustPlatform -, nixsgx -, pkg-config -, rust-bin -, pkgs -, src -, openssl -}: -let - rustVersion = rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; - rustPlatform = makeRustPlatform { - cargo = rustVersion; - rustc = rustVersion; - }; - craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rustVersion; - commonArgs = { - nativeBuildInputs = [ - pkg-config - rustPlatform.bindgenHook - ]; - - buildInputs = [ - openssl - nixsgx.sgx-sdk - nixsgx.sgx-dcap - nixsgx.sgx-dcap.quote_verify - ]; - - strictDeps = true; - - src = with lib.fileset; toSource { - root = src; - fileset = unions [ - ./Cargo.lock - ./Cargo.toml - ./bin - ./crates - ./rust-toolchain.toml - ./deny.toml - ./taplo.toml - ]; - }; - - checkType = "debug"; - env = { - OPENSSL_NO_VENDOR = "1"; - NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa"; - }; - }; - - cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { - pname = "teepot-workspace"; - }); -in -{ - inherit rustPlatform - rustVersion - commonArgs - craneLib - cargoArtifacts; -}