├── .github └── workflows │ ├── onmergerelease.yml │ ├── onpullrequest.yml │ └── security-scans.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── inteltrustauthorityclient ├── __init__.py ├── base │ ├── __init__.py │ └── evidence_adapter.py ├── cli │ ├── README.md │ ├── test │ │ └── test_trustauthority-pycli.py │ └── trustauthority-pycli │ │ └── trustauthority-pycli.py ├── configfs_tsm │ └── report.py ├── connector │ ├── README.md │ ├── config.py │ ├── connector.py │ └── evidence.py ├── examples │ ├── .env │ ├── __init__.py │ ├── sgx_sample_app │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── __init__.py │ │ ├── docker-compose.yml │ │ ├── minimal-enclave │ │ │ ├── .config_HW_RELEASE_x64 │ │ │ ├── Enclave.config.xml │ │ │ ├── Enclave.cpp │ │ │ ├── Enclave.edl │ │ │ ├── Enclave.lds │ │ │ ├── Enclave.o │ │ │ ├── Enclave_private_sample.pem │ │ │ ├── Enclave_t.c │ │ │ ├── Enclave_t.h │ │ │ ├── Enclave_t.o │ │ │ ├── Enclave_u.c │ │ │ ├── Enclave_u.h │ │ │ ├── Enclave_u.o │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── enclave.signed.so │ │ │ ├── libutils.so │ │ │ ├── utils.cpp │ │ │ ├── utils.h │ │ │ └── utils.o │ │ └── sgx_sample_app.py │ └── tdx_sample_app │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── __init__.py │ │ ├── docker-compose.yml │ │ └── tdx_sample_app.py ├── nvgpu │ ├── README.md │ └── gpu_adapter.py ├── resources │ ├── __init__.py │ ├── constants.py │ └── logger.py ├── sgx │ ├── __init__.py │ └── intel │ │ ├── README.md │ │ └── sgx_adapter.py └── tdx │ ├── README.md │ ├── azure │ ├── README.md │ ├── __init__.py │ └── azure_tdx_adapter.py │ └── tdx_adapter.py ├── poetry.lock ├── pyproject.toml └── test ├── README.md ├── test_azure_tdx_adapter.py ├── test_config.py ├── test_connector.py ├── test_intel_sgx_adapter.py └── test_tdx_adapter.py /.github/workflows/onmergerelease.yml: -------------------------------------------------------------------------------- 1 | name: OnMergeRelease 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | - 'release/*' 8 | tags: 9 | - '**' 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | build-test-scan: 15 | runs-on: [ ubuntu-20.04 ] 16 | env: 17 | http_proxy: ${{ secrets.HTTP_PROXY }} 18 | https_proxy: ${{ secrets.HTTPS_PROXY }} 19 | no_proxy: ${{ secrets.NO_PROXY }} 20 | PYTHONPATH: ${{ github.workspace }}:$PYTHONPATH 21 | 22 | steps: 23 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Setup Python 28 | uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 29 | with: 30 | python-version: '3.8.10' 31 | 32 | - name: Install Poetry 33 | run: pip install poetry==1.7.1 34 | 35 | - name: Install dependencies via Poetry 36 | run: poetry install 37 | 38 | - name: Run UT with Coverage enabled 39 | run: cd test && poetry run coverage run --source ../inteltrustauthorityclient/ --omit="../inteltrustauthorityclient/nvgpu/*,../inteltrustauthorityclient/examples/*,../inteltrustauthorityclient/resources/*" -m unittest discover -p 'test_*.py' 40 | 41 | - name: Check coverage percentage 42 | run: | 43 | cd test 44 | coverage_percentage=$(poetry run coverage report --format=total) 45 | if [ $coverage_percentage -lt 80 ]; then 46 | echo "Coverage is less than 80 %: $coverage_percentage %" 47 | exit 1 48 | else 49 | echo "Coverage is greater than or equal to 80 : $coverage_percentage %" 50 | fi 51 | -------------------------------------------------------------------------------- /.github/workflows/onpullrequest.yml: -------------------------------------------------------------------------------- 1 | name: OnPullRequest 2 | 3 | on: 4 | pull_request: 5 | 6 | 7 | permissions: read-all 8 | 9 | jobs: 10 | 11 | security-file-check: 12 | runs-on: [ ubuntu-20.04 ] 13 | steps: 14 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Check Security.md file 19 | run: | 20 | if [ ! -f ./SECURITY.md ]; then 21 | echo "Security.md file is missing" 22 | exit 1 23 | fi 24 | 25 | build-test-scan: 26 | runs-on: [ ubuntu-20.04 ] 27 | needs: [ "security-file-check" ] 28 | env: 29 | http_proxy: ${{ secrets.HTTP_PROXY }} 30 | https_proxy: ${{ secrets.HTTPS_PROXY }} 31 | no_proxy: ${{ secrets.NO_PROXY }} 32 | PYTHONPATH: ${{ github.workspace }}:$PYTHONPATH 33 | steps: 34 | - name: Setup Python 35 | uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 36 | with: 37 | python-version: '3.8.10' 38 | 39 | - name: Install Poetry 40 | run: pip install poetry==1.7.1 41 | 42 | - name: Install dependencies via Poetry 43 | run: poetry install 44 | 45 | - name: Run UT with Coverage enabled 46 | run: cd test && poetry run coverage run --source ../inteltrustauthorityclient/ --omit="../inteltrustauthorityclient/nvgpu/*,../inteltrustauthorityclient/examples/*,../inteltrustauthorityclient/resources/*" -m unittest discover -p 'test_*.py' 47 | 48 | - name: Check coverage percentage 49 | run: | 50 | cd test 51 | coverage_percentage=$(poetry run coverage report --format=total) 52 | if [ $coverage_percentage -lt 80 ]; then 53 | echo "Coverage is less than 80 %: $coverage_percentage %" 54 | exit 1 55 | else 56 | echo "Coverage is greater than or equal to 80 : $coverage_percentage %" 57 | fi 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/security-scans.yml: -------------------------------------------------------------------------------- 1 | name: "Security Scans" 2 | on: 3 | workflow_dispatch: 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | bandit-scan: 9 | runs-on: [ self-hosted, taas ] 10 | steps: 11 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Bandit Scan 16 | uses: intel-innersource/frameworks.devops.github.actions.bandit@main 17 | with: 18 | scan_path: . 19 | report_path: artifacts/ITAPythonClient 20 | exclude: .github 21 | 22 | Checkmarx: 23 | runs-on: [ self-hosted, taas ] 24 | steps: 25 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 26 | - name: Scan 27 | uses: intel-innersource/frameworks.devops.github.actions.checkmarx@main 28 | with: 29 | username: ${{ secrets.CHECKMARX_USERNAME }} 30 | password: ${{ secrets.CHECKMARX_PASSWORD }} 31 | http_proxy: ${{ secrets.HTTP_PROXY }} 32 | https_proxy: ${{ secrets.HTTPS_PROXY }} 33 | no_proxy: ${{ secrets.NO_PROXY }} 34 | project: ITA Python Client 35 | team: /CxServer/SP/Intel/IntelProjects/IAP/36221 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | py-api/__pycache__/ 3 | *.pyc 4 | *__pycache__ 5 | dist/* 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | CommunityCodeOfConduct AT intel DOT com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ### License 4 | 5 | is licensed under the terms in [LICENSE]. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. 6 | 7 | ### Sign your work 8 | 9 | Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify 10 | the below (from [developercertificate.org](http://developercertificate.org/)): 11 | 12 | ``` 13 | Developer Certificate of Origin 14 | Version 1.1 15 | 16 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 17 | 660 York Street, Suite 102, 18 | San Francisco, CA 94110 USA 19 | 20 | Everyone is permitted to copy and distribute verbatim copies of this 21 | license document, but changing it is not allowed. 22 | 23 | Developer's Certificate of Origin 1.1 24 | 25 | By making a contribution to this project, I certify that: 26 | 27 | (a) The contribution was created in whole or in part by me and I 28 | have the right to submit it under the open source license 29 | indicated in the file; or 30 | 31 | (b) The contribution is based upon previous work that, to the best 32 | of my knowledge, is covered under an appropriate open source 33 | license and I have the right under that license to submit that 34 | work with modifications, whether created in whole or in part 35 | by me, under the same open source license (unless I am 36 | permitted to submit under a different license), as indicated 37 | in the file; or 38 | 39 | (c) The contribution was provided directly to me by some other 40 | person who certified (a), (b) or (c) and I have not modified 41 | it. 42 | 43 | (d) I understand and agree that this project and the contribution 44 | are public and that a record of the contribution (including all 45 | personal information I submit with it, including my sign-off) is 46 | maintained indefinitely and may be redistributed consistent with 47 | this project or the open source license(s) involved. 48 | ``` 49 | 50 | Then you just add a line to every git commit message: 51 | 52 | Signed-off-by: Joe Smith 53 | 54 | Use your real name (sorry, no pseudonyms or anonymous contributions.) 55 | 56 | If you set your `user.name` and `user.email` git configs, you can sign your 57 | commit automatically with `git commit -s`. 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Intel Corporation. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Client for Python 2 | 3 |

· 02/13/2025 ·

4 | 5 | The Intel® Trust Authority Client for Python is a library of Python modules used to perform remote attestation of a Trusted Execution Environment (TEE) using Intel Trust Authority as the verifier. The client packages enable you to collect evidence from the TEE, request an attestation token (JWT), and verify the cryptographic signature of the token. 6 | 7 | The Python client currently supports the following TEEs: 8 | 9 | - Intel® Software Guard Extensions (Intel® SGX). 10 | - Intel® Trust Domain Extensions (Intel® TDX) for on-premises Intel TDX platforms. 11 | - Google Cloud Platform\* (GCP) confidential VMs with Intel TDX. 12 | - Azure\* confidential VMs with Intel TDX. 13 | - NVIDIA\* H100 GPU and Intel TDX Trust Domain. 14 | 15 | ## Library structure 16 | 17 | - [/inteltrustauthorityclient/connector](inteltrustauthorityclient/connector#readme): Contains the main ITAConnector class to connect to Intel Trust Authority. 18 | - [/inteltrustauthorityclient/nvgpu](inteltrustauthorityclient/nvgpu#readme): Contains the NVIDIA H100 GPU adapter. 19 | - [/inteltrustauthorityclient/cli](inteltrustauthorityclient/cli#readme): Contains the Intel Trust Authority Python CLI. This version of the CLI includes support for NVIDIA H100 GPU attestation. This feature is in limited preview status. 20 | - [/inteltrustauthorityclient/examples](inteltrustauthorityclient/examples): Contains sample applications to demonstrate the usage of the client. See [Sample applications](#sample-applications) for more information. 21 | - [inteltrustauthorityclient/sgx/intel](inteltrustauthorityclient/sgx/intel/README.md): Contains the Intel SGX adapter. 22 | - [inteltrustauthorityclient/tdx](inteltrustauthorityclient/tdx): Contains the Intel TDX bare metal and Google Cloud Platform (GCP) adapter (one adapter supports both platforms), and Azure TDX adapters. 23 | - [test](test/README.md): Contains unit tests for the client. 24 | 25 | 26 | ## System requirement 27 | 28 | - Python 3.8 or newer. 29 | - Ubuntu 22.04 with *kernel 6.7 or later,* or Ubuntu 24.04. Support for the ConfigFS-TSM subsystem is required for Intel TDX attestation. 30 | 31 | ## Installation 32 | 33 | The following installation steps assume that you are installing the Intel Trust Authority Client for Python on an Intel TDX TD, running on an Intel TDX-enabled host server. For information about how to prepare the Intel TDX host server, see [Setup Remote Attestation on Host OS and Inside TD](https://github.com/canonical/tdx?tab=readme-ov-file#8-setup-remote-attestation-on-host-os-and-inside-td) in the [Canonical/TDX](https://github.com/canonical/tdx) repo on GitHub. 34 | 35 | To install the latest version of the Intel TDX + NVIDIA H100 client on a TD: 36 | 37 | 1. Clone the repository, which automatically checks out the main branch. 38 | 39 | ```bash 40 | git clone https://github.com/intel/trustauthority-client-for-python.git 41 | ``` 42 | 43 | Run the following commands from the `inteltrustauthorityclient` directory. 44 | 45 | 2. Install **poetry** by running the following command: 46 | ```sh 47 | pip3 install --no-cache-dir poetry 48 | ``` 49 | 1. Create a wheel package using poetry: 50 | 51 | Spawn a poetry shell: 52 | ```bash 53 | poetry shell 54 | ``` 55 | Build wheel package: 56 | ```bash 57 | poetry build 58 | ``` 59 | 1. Run pip install to install the **inteltrustauthorityclient** package in site-packages: 60 | ```bash 61 | cd ../dist 62 | pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl 63 | ``` 64 | 65 | >[!NOTE] 66 | > When you install the client, you might see the following error: "ERROR: pip's dependency resolver does not currently take into account all the packages that are installed." That is followed by a list of version mismatch messages. You can safely ignore this error. The client uses newer versions of the Python libraries than the NVIDIA SDK. 67 | 68 | 69 | ## Usage 70 | 71 | More information about how to use this library is available in the READMEs for each package. [Library structure](#library-structure), above, has links to the READMEs for each package. 72 | 73 | The primary documentation is the [Python Connector Reference](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html) in the Intel Trust Authority documentation. 74 | 75 | 76 | ### Sample applications 77 | 78 | For more information on how to use the client, see the sample applications in the [examples](./inteltrustauthorityclient/examples) folder. 79 | 80 | - [Intel SGX sample app](./inteltrustauthorityclient/examples/sgx_sample_app/README.md) 81 | - [Intel TDX sample app](./inteltrustauthorityclient/examples/tdx_sample_app/README.md) — Works on Intel TDX hosts/VMs and Azure TDX VMs. 82 | 83 | - Create Adapter using: 84 | - **TDX** 85 | - [TDX](./inteltrustauthorityclient/tdx/README.md) 86 | - [Azure TDX](./inteltrustauthorityclient/tdx/azure/README.md) 87 | - **SGX** 88 | - [Intel SGX](./inteltrustauthorityclient/sgx/intel/README.md) 89 | - **NVIDIA** 90 | - [NVGPU](./inteltrustauthorityclient/nvgpu/README.md) 91 | ### Unit Tests 92 | 93 | For more information on how to run the unit tests, see the [Unit Tests README](./test/README.md). 94 | 95 | ## Code of Conduct and Contributing 96 | 97 | See the [Contributing](./CONTRIBUTING.md) file for more information on how to contribute to this project. This project follows the [Code of Conduct](./CODE_OF_CONDUCT.md). 98 | ## License 99 | 100 | This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) 101 | file. 102 | 103 |

104 | --- 105 | 106 | **\*** Other names and brands may be claimed as the property of others. 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 6 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/base/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/base/evidence_adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | from abc import ABC, abstractmethod 8 | from inteltrustauthorityclient.connector.evidence import Evidence 9 | 10 | 11 | class EvidenceAdapter(ABC): 12 | """Abstract class to be inherited by adapter implementation subclasses. 13 | 14 | Args: 15 | ABC (Abstract class object): Helper class that provides a standard way to create an ABC using inheritance. 16 | """ 17 | 18 | @abstractmethod 19 | def collect_evidence(self, nonce) -> Evidence: 20 | pass 21 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/cli/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Intel® Trust Authority CLI for Intel TDX and NVIDIA H100 GPU 3 | 4 |

· 02/27/2025 ·

5 | 6 | Intel® Trust Authority Python CLI ("CLI") for Intel® Trust Domain Extensions (Intel® TDX) and NVIDIA\* H100\* GPU [**trustauthority-pycli**](../cli) provides a CLI to attest an Intel TDX trust domain (TD) and a NVIDIA H100 GPU with Intel Trust Authority. 7 | 8 | This version of the CLI works with Intel® Trust Domain Extensions (Intel® TDX) and NVIDIA H100 Confidential Computing enabled platforms. 9 | 10 | For more information, see [GPU Remote Attestation](https://docs.trustauthority.intel.com/main/articles/concept-gpu-attestation.html) in the Intel Trust Authority documentation. 11 | 12 | ## Prerequisites 13 | 14 | The following prerequisites must be installed on the CVM (Confidential VM with Intel TDX): 15 | 16 | - Use **Python 3.8 or newer**. 17 | - Ubuntu 22.04 with *kernel 6.7 or later,* or Ubuntu 24.04. Support for the ConfigFS-TSM subsystem is required for Intel TDX attestation. 18 | - NVIDIA H100 GPU 19 | - [NVIDIA Attestation SDK v1.4.0](https://github.com/NVIDIA/nvtrust/releases/tag/v1.4.0) installed in the guest TD. NVIDIA Attestation SDK v2.x is _not_ supported. 20 | 21 | > [!NOTE] 22 | > The NVIDIA Attestation SDK requires the GPU Local Verifier. The version must match the SDK v1.4 23 | 24 | ## Intel Trust Authority Configuration 25 | 26 | The CLI requires a configuration file (config.json) to be provided for the CLI operations. The following is an example of the configuration file: 27 | 28 | ``` 29 | { 30 | "trustauthority_base_url": "https://portal.trustauthority.intel.com" 31 | "trustauthority_api_url": "https://api.trustauthority.intel.com", 32 | "trustauthority_api_key": "" 33 | } 34 | ``` 35 | > [!NOTE] 36 | > If you are in the European Union (EU) region, use the following Intel Trust Authority URLs:
Base URL — https://portal.eu.trustauthority.intel.com
API URL — https://api.eu.trustauthority.intel.com 37 | 38 | Save the configuration to a 'config.json' file. The `attest` command requires the configuration file path as an argument, and allows you to specify a path to the file so that it doesn't need to be in the same directory as the CLI binary. 39 | 40 | ## Installation 41 | 42 | Refer to the main [README](../../README.md#installation) for installation instructions. 43 | 44 | For convenience, you may want to define an alias to run the CLI by running the following commands. You must replace **\** with the path to the directory where you'll install the client (e.g., pythonclient). 45 | 46 | ```bash 47 | # Create an alias for the Trust Authority CLI (inteltrustauthorityclient/cli) 48 | export CLIPATH=/inteltrustauthorityclient/cli/trustauthority-pycli; 49 | alias trustauthority-pycli="sudo python3 $CLIPATH/trustauthority-cli.py" 50 | ``` 51 | Sudo is optional in the alias defined above, but it's required to run the CLI commands that collect evidence from the TEE and it's convenient to have it in the alias. 52 | 53 | You can check to see that the CLI is installed correctly by running the following command. 54 | 55 | ```bash 56 | # Print the help message 57 | trustauthority-pycli -h 58 | ``` 59 | If you didn't define an alias, use the following commands. 60 | 61 | ```bash 62 | cd /inteltrustauthorityclient/cli/trustauthority-pycli 63 | python3 trustauthority_cli.py -h 64 | ``` 65 | 66 | ## Usage 67 | 68 | **Sudo** or root is required to run the `evidence` and `attest` commands. That's because root permission is needed to access **config/tsm** to collect evidence for an Intel TDX quote. The `verify` command doesn't require root privileges. 69 | 70 | The following examples assume that you've defined a `trustauthority_pycli` alias. 71 | 72 | ### `evidence` 73 | 74 | Collects evidence for attestation from an Intel TDX trust domain or a NVIDIA H100 GPU (one at a time; evidence doesn't support both in a single call). This command collects evidence but doesn't send it to Intel Trust Authority for attestation. If successful, `evidence` prints the GPU evidence or Intel TDX quote to the screen (of course, output can also be piped to a file). This command can be used in Background-check attestation flow or in development and testing. 75 | 76 | ```sh 77 | trustauthority_pycli evidence -a [-n ] [-u ] 78 | ``` 79 | Options: 80 | ``` 81 | -a, --attest-type: Specify the TEE type, valid values are "tdx" or "nvgpu". 82 | -n, --nonce: Optional nonce in base64-encoded format. 83 | -u, --user-data: Optional user data in base64-encoded format. 84 | ``` 85 | 86 | ### `attest` 87 | 88 | Collects evidence from the TEE or GPU and sends it to Intel Trust Authority for attestation. `attest` returns an attestation token in JWT format if the attestation is successful. This command can attest an Intel TDX trust domain, a NVIDIA H100 GPU, or both. `attest` uses values set in the `config.json` file to locate the Trust Authority REST API gateway and authenticate requests. 89 | 90 | ```sh 91 | trustauthority_pycli attest -a -c [-u ] [-p ] [-s [--policy-must-match] 92 | ``` 93 | Options: 94 | ``` 95 | -a, --attest-type: Specify the TEE type to attest; valid values are "tdx", "nvgpu", or "tdx+nvgpu". 96 | -c, --config: Configuration file path (config.json). 97 | -u, --user-data: Optional user data in base64-encoded format. 98 | -p, --policy-ids: An optional list of Trust Authority policy IDs (comma separated). 99 | -s, --token-sign-alg: Optional token signing algorithm ("RS256" or "PS384"). The default is "PS384". 100 | --policy-must-match: Optional boolean for enforcing policy match during attestation. If set to "True", a token is issued only if all attestation policies match. If set to "False", a token is issued even if one or all policies are unmatched. The default is "False". 101 | ``` 102 | 103 | ### `verify` 104 | 105 | Verifies an attestation token to ensure that the token is generated from a genuine Intel Trust Authority service. This command verifies the token signature and format, but it doesn't verify claims. If the token is valid, the command prints the token claims to the screen. 106 | 107 | ```sh 108 | trustauthority_pycli verify -c -t 109 | ``` 110 | Options: 111 | ``` 112 | -c, --config: Configuration file path. 113 | -t, --token: An Intel Trust Authority attestation token in JWT format. 114 | ``` 115 | 116 | ## License 117 | 118 | This source is distributed under the BSD-style license found in the [LICENSE](../../LICENSE) 119 | file. 120 | 121 |

122 | --- 123 | 124 | **\*** Other names and brands may be claimed as the property of others. 125 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/cli/test/test_trustauthority-pycli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import unittest 8 | from unittest.mock import patch, MagicMock 9 | import io 10 | import argparse 11 | import json 12 | from io import StringIO 13 | from inteltrustauthorityclient.cli import * 14 | 15 | 16 | class TestIntelTrustAuthorityCLI(unittest.TestCase): 17 | 18 | def test_cmd_evidence_tdx_with_user_data(self, mock_tdx_adapter): 19 | args = argparse.Namespace(attest_type='tdx', user_data='user_data', nonce=None) 20 | mock_tdx_adapter.return_value.collect_evidence.return_value = 'evidence' 21 | 22 | trustauthority-pycli.cmd_evidence(args) 23 | 24 | mock_tdx_adapter.assert_called_once_with('user_data', None) 25 | mock_tdx_adapter.return_value.collect_evidence.assert_called_once_with(None) 26 | mock_print.assert_called_once_with('evidence.evidence') 27 | 28 | def test_cmd_evidence_tdx_without_user_data(self, mock_tdx_adapter): 29 | args = argparse.Namespace(attest_type='tdx', user_data=None, nonce='nonce') 30 | mock_tdx_adapter.return_value.collect_evidence.return_value = 'evidence' 31 | 32 | trustauthority-pycli.cmd_evidence(args) 33 | 34 | mock_tdx_adapter.assert_called_once_with('', None) 35 | mock_tdx_adapter.return_value.collect_evidence.assert_called_once_with('nonce') 36 | mock_print.assert_called_once_with('evidence.evidence') 37 | 38 | 39 | def test_cmd_evidence_tdx_with_user_data_nonce(self, mock_tdx_adapter): 40 | args = argparse.Namespace(attest_type='tdx', user_data='user_data', nonce='nonce') 41 | mock_tdx_adapter.return_value.collect_evidence.return_value = 'evidence' 42 | 43 | trustauthority-pycli.cmd_evidence(args) 44 | 45 | mock_tdx_adapter.assert_called_once_with('user_data', 'nonce') 46 | mock_tdx_adapter.return_value.collect_evidence.assert_called_once_with('user_data', 'nonce') 47 | mock_print.assert_called_once_with('evidence.evidence') 48 | 49 | def test_cmd_evidence_nvgpu_without_user_data(self, mock_gpu_adapter): 50 | args = argparse.Namespace(attest_type='nvgpu', user_data=None, nonce='nonce') 51 | mock_gpu_adapter.return_value.collect_evidence.return_value = 'evidence' 52 | 53 | trustauthority-pycli.cmd_evidence(args) 54 | 55 | mock_gpu_adapter.assert_called_once_with() 56 | mock_gpu_adapter.return_value.collect_evidence.assert_called_once_with(None) 57 | mock_print.assert_called_once_with('evidence.evidence') 58 | 59 | def test_cmd_evidence_nvgpu_without_nonce(self, mock_gpu_adapter): 60 | args = argparse.Namespace(attest_type='nvgpu', user_data=None, nonce=None) 61 | mock_gpu_adapter.return_value.collect_evidence.return_value = 'evidence' 62 | 63 | trustauthority-pycli.cmd_evidence(args) 64 | 65 | mock_gpu_adapter.assert_called_once_with() 66 | mock_gpu_adapter.return_value.collect_evidence.assert_called_once_with(None) 67 | mock_print.assert_called_once_with('evidence.evidence') 68 | 69 | def test_cmd_attest_tdx(self, mock_tdx_adapter, mock_ita_connector, mock_config, mock_log, mock_json_load, mock_open): 70 | args = MagicMock() 71 | args.config = 'config.json' 72 | args.attest_type = 'tdx' 73 | args.user_data = 'user_data' 74 | 75 | mock_json_load.return_value = { 76 | 'trustauthority_base_url': 'base_url', 77 | 'trustauthority_api_url': 'api_url', 78 | 'trustauthority_api_key': 'api_key', 79 | 'trust_authority_request_id': 'request_id', 80 | 'trust_authority_policy_id': 'policy_id' 81 | } 82 | 83 | mock_config.return_value = MagicMock() 84 | mock_tdx_adapter.return_value = MagicMock() 85 | mock_ita_connector.return_value = MagicMock() 86 | mock_ita_connector.return_value.attest_composite.return_value = MagicMock(token='token', headers="{'request-id': '123', 'trace-id': '456'}") 87 | 88 | trustauthority-pycli.cmd_attest(args) 89 | 90 | mock_open.assert_called_once_with('config.json', 'r') 91 | mock_json_load.assert_called_once_with(mock_open().__enter__()) 92 | mock_log.error.assert_not_called() 93 | mock_config.assert_called_once_with( 94 | mock_config.RetryConfig(1, 1, 1), 95 | 'base_url', 96 | 'api_url', 97 | 'api_key' 98 | ) 99 | mock_ita_connector.assert_called_once_with(mock_config()) 100 | mock_tdx_adapter.assert_called_once_with('user_data', None) 101 | mock_ita_connector().attest_composite.assert_called_once_with(mock_ita_connector.TDXAttestArgs(), None) 102 | mock_ita_connector().attest_composite().token = 'token' 103 | self.assertEqual(mock_ita_connector().attest_composite().headers, "{'request-id': '123', 'trace-id': '456'}") 104 | 105 | def test_cmd_attest_nvgpu(self, mock_gpu_adapter, mock_ita_connector, mock_config, mock_log, mock_json_load, mock_open): 106 | args = MagicMock() 107 | args.config = 'config.json' 108 | args.attest_type = 'nvgpu' 109 | args.user_data = 'user_data' 110 | 111 | mock_json_load.return_value = { 112 | 'trustauthority_base_url': 'base_url', 113 | 'trustauthority_api_url': 'api_url', 114 | 'trustauthority_api_key': 'api_key', 115 | 'trust_authority_request_id': 'request_id', 116 | 'trust_authority_policy_id': 'policy_id' 117 | } 118 | 119 | mock_config.return_value = MagicMock() 120 | mock_tdx_adapter.return_value = MagicMock() 121 | mock_ita_connector.return_value = MagicMock() 122 | mock_ita_connector.return_value.attest_composite.return_value = MagicMock(token='token', headers="{'request-id': '123', 'trace-id': '456'}") 123 | 124 | trustauthority-pycli.cmd_attest(args) 125 | 126 | mock_open.assert_called_once_with('config.json', 'r') 127 | mock_json_load.assert_called_once_with(mock_open().__enter__()) 128 | mock_log.error.assert_not_called() 129 | mock_config.assert_called_once_with( 130 | mock_config.RetryConfig(1, 1, 1), 131 | 'base_url', 132 | 'api_url', 133 | 'api_key' 134 | ) 135 | mock_ita_connector.assert_called_once_with(mock_config()) 136 | mock_gpu_adapter.assert_called_once_with('user_data', None) 137 | mock_ita_connector().attest_composite.assert_called_once_with(None, mock_ita_connector.GPUAttestArgs()) 138 | mock_ita_connector().attest_composite().token = 'token' 139 | self.assertEqual(mock_ita_connector().attest_composite().headers, "{'request-id': '123', 'trace-id': '456'}") 140 | 141 | def test_cmd_attest_tdx_nvgpu(self, mock_ita_connector, mock_config, mock_json_load, mock_open): 142 | args = MagicMock() 143 | args.attest_type = 'tdx+nvgpu' 144 | args.config = 'config.json' 145 | args.user_data = 'user_data' 146 | 147 | mock_json_load.return_value = { 148 | 'trustauthority_base_url': 'base_url', 149 | 'trustauthority_api_url': 'api_url', 150 | 'trustauthority_api_key': 'api_key', 151 | 'trust_authority_request_id': 'request_id', 152 | 'trust_authority_policy_id': 'policy_id' 153 | } 154 | 155 | mock_config.return_value = MagicMock() 156 | mock_tdx_adapter.return_value = MagicMock() 157 | mock_gpu_adapter.return_value = MagicMock() 158 | mock_ita_connector.return_value = MagicMock() 159 | mock_ita_connector.return_value.attest_composite.return_value = MagicMock(token='token', headers="{'request-id': '123', 'trace-id': '456'}") 160 | 161 | trustauthority-pycli.cmd_attest(args) 162 | 163 | mock_open.assert_called_once_with('config.json', 'r') 164 | mock_json_load.assert_called_once_with(mock_open().__enter__()) 165 | mock_log.error.assert_not_called() 166 | mock_config.assert_called_once_with( 167 | mock_config.RetryConfig(1, 1, 1), 168 | 'base_url', 169 | 'api_url', 170 | 'api_key' 171 | ) 172 | mock_ita_connector.assert_called_once_with(mock_config()) 173 | mock_tdx_adapter.assert_called_once_with('user_data', None) 174 | mock_gpu_adapter.assert_called_once_with('user_data', None) 175 | mock_ita_connector().attest_composite.assert_called_once_with(mock_ita_connector.TDXAttestArgs(), mock_ita_connector.GPUAttestArgs()) 176 | mock_ita_connector().attest_composite().token = 'token' 177 | self.assertEqual(mock_ita_connector().attest_composite().headers, "{'request-id': '123', 'trace-id': '456'}") 178 | 179 | def test_cmd_verify_success(self, mock_config, mock_exit, mock_open): 180 | mock_file = StringIO() 181 | mock_file.write(json.dumps({ 182 | 'trustauthority_base_url': 'https://example.com', 183 | 'trustauthority_api_url': 'https://api.example.com', 184 | 'trustauthority_api_key': 'API_KEY' 185 | })) 186 | mock_file.seek(0) 187 | mock_open.return_value = mock_file 188 | 189 | with patch('your_module.config.Config') as mock_config, \ 190 | patch('your_module.connector.ITAConnector') as mock_connector: 191 | trustauthority-pycli.cmd_verify(['--config', 'config.json', '--token', 'TOKEN']) 192 | 193 | mock_open.assert_called_once_with('config.json', 'r') 194 | mock_config.assert_called_once_with( 195 | mock_config.RetryConfig(1, 1, 1), 196 | 'https://example.com', 197 | 'https://api.example.com', 198 | 'API_KEY' 199 | ) 200 | mock_connector.assert_called_once_with(mock_config.return_value) 201 | mock_connector.return_value.verify_token.assert_called_once_with('TOKEN') 202 | self.assertEqual(mock_exit.call_count, 0) 203 | 204 | def test_cmd_verify_missing_config(self, mock_config, mock_exit, mock_open): 205 | mock_file = StringIO() 206 | mock_file.write(json.dumps({})) 207 | mock_file.seek(0) 208 | mock_open.return_value = mock_file 209 | 210 | trustauthority-pycli.cmd_verify(['--config', 'config.json', '--token', 'TOKEN']) 211 | 212 | mock_open.assert_called_once_with('config.json', 'r') 213 | mock_exit.assert_called_once_with(1) 214 | 215 | def test_cmd_verify_missing_token(self, mock_config, mock_exit, mock_open): 216 | mock_file = StringIO() 217 | mock_file.write(json.dumps({ 218 | 'trustauthority_base_url': 'https://example.com', 219 | 'trustauthority_api_url': 'https://api.example.com', 220 | 'trustauthority_api_key': 'API_KEY' 221 | })) 222 | mock_file.seek(0) 223 | mock_open.return_value = mock_file 224 | 225 | trustauthority-pycli.cmd_verify(['--config', 'config.json']) 226 | 227 | mock_open.assert_called_once_with('config.json', 'r') 228 | mock_exit.assert_called_once_with(1) 229 | 230 | 231 | if __name__ == '__main__': 232 | unittest.main() 233 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/cli/trustauthority-pycli/trustauthority-pycli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | # trustauthority-pycli 7 | 8 | import argparse 9 | import json 10 | import uuid 11 | import sys 12 | import os 13 | import base64 14 | from inteltrustauthorityclient.resources import constants as const 15 | from inteltrustauthorityclient.connector import config, connector 16 | from inteltrustauthorityclient.tdx.tdx_adapter import TDXAdapter 17 | from inteltrustauthorityclient.nvgpu.gpu_adapter import GPUAdapter 18 | 19 | def cmd_evidence(args): 20 | if args.nonce != None: 21 | try: 22 | nonce_bytes = base64.b64decode(args.nonce, validate=True) 23 | except Exception as err: 24 | print(f"Error while base64 decoding of nonce: : {err}") 25 | exit(1) 26 | else: 27 | nonce_bytes = None 28 | 29 | # Collect Intel TDX evidence(quote) 30 | if args.attest_type == 'tdx': 31 | if args.user_data != None: 32 | try: 33 | user_data_bytes = base64.b64decode(args.user_data, validate=True) 34 | except Exception as err: 35 | print(f"Error while base64 decoding of user data: : {err}") 36 | exit(1) 37 | else: 38 | user_data_bytes = b"" 39 | 40 | tdx_adapter = TDXAdapter(user_data_bytes) 41 | evidence = tdx_adapter.collect_evidence(nonce_bytes) 42 | if evidence is None: 43 | print("TDX Quote is not returned") 44 | return None 45 | print(f"TDX Quote : {evidence.evidence}") 46 | 47 | # Collect NVGPU evidence 48 | elif args.attest_type == 'nvgpu': 49 | if args.user_data != None: 50 | print("User Data (-u) is used in 'tdx' or 'tdx+nvgpu' attestaion") 51 | exit(1) 52 | gpu_adapter = GPUAdapter() 53 | evidence = gpu_adapter.collect_evidence(nonce_bytes) 54 | if evidence is None: 55 | print("GPU Evidence is not returned") 56 | return None 57 | print(f"GPU evidence : {evidence.evidence}") 58 | 59 | 60 | def cmd_attest(args): 61 | # Check if request id is valid 62 | if args.request_id != None: 63 | if len(args.request_id) > const.REQUEST_ID_MAX_LEN: 64 | print("Request ID should be atmost 128 characters long.") 65 | exit(1) 66 | for req_char in args.request_id: 67 | if req_char != '-' and req_char.isalnum() == False: 68 | print("Request ID should contain only a-z, A-Z, 0-9, and - (hyphen), Special characters are not allowed.") 69 | exit(1) 70 | 71 | # Check if policy uuid is valid and the number of policy counts not exceeded. 72 | if args.policy_ids != None: 73 | policyIds = args.policy_ids.split(",") 74 | if len(policyIds) > const.POLICY_IDS_MAX_LEN: 75 | print("policy count in request must be between 1 - 10") 76 | exit(1) 77 | for pId in policyIds: 78 | if not config.validate_uuid(pId): 79 | print(f"Invalid policy UUID :{pId}") 80 | exit(1) 81 | else: 82 | policyIds = None 83 | 84 | # Check if the token signing algorithm is supported 85 | if args.token_sign_alg != None: 86 | tsa = args.token_sign_alg 87 | if tsa not in ["RS256", "PS384"]: 88 | print(f"Unsupported token signing algorithm {tsa}, refer help section for supported algorithms") 89 | exit(1) 90 | 91 | if args.user_data != None: 92 | if args.attest_type =='nvgpu': 93 | print("User Data (-u) is used in 'tdx' or 'tdx+nvgpu' attestaion") 94 | exit(1) 95 | try: 96 | user_data_bytes = base64.b64decode(args.user_data) 97 | except Exception as err: 98 | print(f"Error while base64 decoding of user data: : {err}") 99 | exit(1) 100 | else: 101 | user_data_bytes = b"" 102 | 103 | # Read ITA Env configuration from json file 104 | with open(args.config, 'r') as cf: 105 | cf_dict = json.load(cf) 106 | 107 | trustauthority_base_url = cf_dict['trustauthority_base_url'] 108 | if trustauthority_base_url is None: 109 | print("TRUSTAUTHORITY_BASE_URL is not set.") 110 | exit(1) 111 | 112 | trustauthority_api_url = cf_dict['trustauthority_api_url'] 113 | if trustauthority_api_url is None: 114 | print("TRUSTAUTHORITY_API_URL is not set.") 115 | exit(1) 116 | 117 | trustauthority_api_key = cf_dict['trustauthority_api_key'] 118 | if trustauthority_api_key is None: 119 | print("TRUSTAUTHORITY_API_KEY is not set.") 120 | exit(1) 121 | 122 | cf.close() 123 | 124 | try: 125 | config_obj = config.Config( 126 | config.RetryConfig( 127 | int(const.DEFAULT_RETRY_WAIT_MIN_SEC), 128 | int(const.DEFAULT_RETRY_WAIT_MAX_SEC), 129 | int(const.DEFAULT_RETRY_MAX_NUM), 130 | int(const.DEFAULT_CLIENT_TIMEOUT_SEC), 131 | ), 132 | trustauthority_base_url, 133 | trustauthority_api_url, 134 | trustauthority_api_key, 135 | ) 136 | except ValueError as exc: 137 | log.error(f"Value Error in config object creation : {exc}") 138 | exit(1) 139 | 140 | if config_obj == None: 141 | log.error("Error in config() instance initialization") 142 | exit(1) 143 | 144 | ita_connector = connector.ITAConnector(config_obj) 145 | 146 | if args.attest_type == 'tdx': 147 | tdx_adapter = TDXAdapter(user_data_bytes) 148 | tdx_attest_args = connector.AttestArgs(tdx_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds) 149 | gpu_attest_args = None 150 | elif args.attest_type =='nvgpu': 151 | gpu_adapter = GPUAdapter() 152 | gpu_attest_args = connector.AttestArgs(gpu_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds) 153 | tdx_attest_args = None 154 | elif args.attest_type =='tdx+nvgpu': 155 | tdx_adapter = TDXAdapter(user_data_bytes) 156 | gpu_adapter = GPUAdapter() 157 | tdx_attest_args = connector.AttestArgs(tdx_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds) 158 | gpu_attest_args = connector.AttestArgs(gpu_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds) 159 | else: 160 | print("Attestation Type %s is unknown.", args.attest_type) 161 | exit(1) 162 | 163 | response = ita_connector.attest_v2(tdx_attest_args, gpu_attest_args) 164 | 165 | if response is None: 166 | print("Attestation Token is not returned.") 167 | return None 168 | 169 | if response.headers: 170 | response_headers_json = json.loads(response.headers.replace("'", '"')) 171 | trace_id = response_headers_json.get("trace-id") 172 | print(f"Trace Id: {trace_id}") 173 | request_id = response_headers_json.get("request-id") 174 | if request_id != None: 175 | print(f"Request Id: {request_id}") 176 | 177 | token = response.token 178 | print(f"Attestation token : {token}") 179 | 180 | 181 | def cmd_verify(args): 182 | if args.token == None: 183 | print("Token is not provided") 184 | exit(1) 185 | 186 | with open(args.config, 'r') as cf: 187 | cf_dict = json.load(cf) 188 | 189 | trustauthority_base_url = cf_dict['trustauthority_base_url'] 190 | if trustauthority_base_url is None: 191 | print("TRUSTAUTHORITY_BASE_URL is not set.") 192 | exit(1) 193 | 194 | trustauthority_api_url = cf_dict['trustauthority_api_url'] 195 | if trustauthority_api_url is None: 196 | print("TRUSTAUTHORITY_API_URL is not set.") 197 | exit(1) 198 | 199 | trustauthority_api_key = cf_dict['trustauthority_api_key'] 200 | if trustauthority_api_key is None: 201 | print("TRUSTAUTHORITY_API_KEY is not set.") 202 | exit(1) 203 | 204 | cf.close() 205 | 206 | try: 207 | config_obj = config.Config( 208 | config.RetryConfig( 209 | int(const.DEFAULT_RETRY_WAIT_MIN_SEC), 210 | int(const.DEFAULT_RETRY_WAIT_MAX_SEC), 211 | int(const.DEFAULT_RETRY_MAX_NUM), 212 | int(const.DEFAULT_CLIENT_TIMEOUT_SEC), 213 | ), 214 | trustauthority_base_url, 215 | trustauthority_api_url, 216 | trustauthority_api_key, 217 | ) 218 | except ValueError as exc: 219 | log.error(f"Value Error in config object creation : {exc}") 220 | exit(1) 221 | 222 | if config_obj == None: 223 | log.error("Error in config() instance initialization") 224 | exit(1) 225 | 226 | ita_connector = connector.ITAConnector(config_obj) 227 | 228 | try: 229 | verified_token = ita_connector.verify_token(args.token) 230 | except Exception as exc: 231 | verified_token = None 232 | print(f"Token verification returned exception : {exc}") 233 | if verified_token != None: 234 | print("Token Verification Successful") 235 | print(f"Verified Attestation Token : {verified_token}") 236 | else: 237 | print("Token Verification failed") 238 | exit(1) 239 | 240 | 241 | def main(): 242 | parser = argparse.ArgumentParser(description="Trust Authority Python CLI") 243 | subparsers = parser.add_subparsers(title="Commands", dest="command", help="Command to execute") 244 | 245 | # evidence command 246 | parser_evidence = subparsers.add_parser("evidence", help="Evidence command") 247 | parser_evidence.add_argument('-a', '--attest-type', choices=['tdx', 'nvgpu'], required=True, help='Attestation Type selection') 248 | parser_evidence.add_argument('-n', '--nonce', help="Nonce in base64 encoded format") 249 | parser_evidence.add_argument('-u', '--user-data', help="User Data in base64 encoded format (*Use for 'tdx' or 'tdx+nvgpu' attestation)") 250 | parser_evidence.set_defaults(func=cmd_evidence) 251 | 252 | # attest command 253 | parser_attest = subparsers.add_parser("attest", help="Attest command") 254 | parser_attest.add_argument('-a', '--attest-type', choices=['tdx','nvgpu','tdx+nvgpu'], required=True, help="Attestation Type selection") 255 | parser_attest.add_argument('-c', '--config', type=str, required=True, help="Trust Authority config in JSON format") 256 | parser_attest.add_argument('-u', '--user-data', help="User Data in base64 encoded format (*Use for 'tdx' or 'tdx+nvgpu' attestation)") 257 | parser_attest.add_argument('-p', '--policy-ids', help="Trust Authority Policy Ids, comma separated without space") 258 | parser_attest.add_argument('-r', '--request-id', help="Request id to be associated with request") 259 | parser_attest.add_argument('-s', '--token-sign-alg', choices=["RS256","PS384"], help="Token Signing Algorithm to be used, support PS384 and RS256") 260 | parser_attest.add_argument('--policy-must-match', default=False, action="store_true", help="Enforce policies match during attestation") 261 | parser_attest.set_defaults(func=cmd_attest) 262 | 263 | # verify command 264 | parser_verify = subparsers.add_parser("verify", help="Verify command") 265 | parser_verify.add_argument('-c', '--config', type=str, required=True, help="ITA environment argument") 266 | parser_verify.add_argument('-t', '--token', type=str, required=True, help="Token in JWT format") 267 | parser_verify.set_defaults(func=cmd_verify) 268 | 269 | if len(sys.argv)==1: 270 | parser.print_help(sys.stderr) 271 | sys.exit(1) 272 | else: 273 | args = parser.parse_args() 274 | args.func(args) 275 | 276 | 277 | if __name__ == '__main__': 278 | main() 279 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/configfs_tsm/report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import os 8 | import tempfile 9 | import logging as log 10 | from dataclasses import dataclass 11 | from inteltrustauthorityclient.resources import constants as const 12 | 13 | 14 | @dataclass 15 | class Request: 16 | """ 17 | Request represents a request for an attestation report. 18 | 19 | Attributes: 20 | _in_blob (bytearray): The input blob. 21 | _get_aux_blob (bool): A flag indicating whether to get the auxiliary blob. 22 | """ 23 | 24 | _in_blob: bytearray 25 | _get_aux_blob: bool 26 | 27 | 28 | @dataclass 29 | class Response: 30 | """ 31 | Represents a response object containing attestation report. 32 | Attributes: 33 | _provider (str): The provider of the response. 34 | _out_blob (bytearray): The output blob of the response. 35 | _aux_blob (bytearray): The auxiliary blob of the response. 36 | """ 37 | 38 | _provider: str 39 | _out_blob: bytearray 40 | _aux_blob: bytearray 41 | 42 | @property 43 | def provider(self): 44 | return self._provider 45 | 46 | @property 47 | def out_blob(self): 48 | return self._out_blob 49 | 50 | @property 51 | def aux_blob(self): 52 | return self._aux_blob 53 | 54 | 55 | class Report: 56 | """ 57 | Provides an API to the configfs/tsm/report subsystem for collecting attestation reports 58 | """ 59 | 60 | tsm_subsystem_path = "/sys/kernel/config/tsm/report" 61 | 62 | def get_report(self, request: Request) -> Response: 63 | """ 64 | Retrieves a report based on the given request from configfs-tsm. 65 | 66 | Args: 67 | request (Request): The request object containing the input data. 68 | 69 | Returns: 70 | Response: The response object containing the report. 71 | 72 | Raises: 73 | FileNotFoundError: If any file or directory required for the report is not found. 74 | OSError: If there is an error in reading or writing files. 75 | ValueError: If there is an error in the value of a variable. 76 | Exception: If any other exception occurs. 77 | """ 78 | provider = None 79 | generation = None 80 | td_quote = None 81 | 82 | if not os.path.exists(self.tsm_subsystem_path): 83 | raise Exception("TSM directory not found.") 84 | 85 | try: 86 | with tempfile.TemporaryDirectory( 87 | prefix="entry", dir=self.tsm_subsystem_path 88 | ) as tempdir: 89 | log.debug(f"Creating tempdir {tempdir} to request report") 90 | # Check if configfs-tsm inblob file is present 91 | if not os.path.exists(os.path.join(tempdir, "inblob")): 92 | os.rmdir(tempdir) 93 | raise Exception(f"Inblob file not found under directory: {tempdir}") 94 | 95 | with open(os.path.join(tempdir, "inblob"), "wb") as inblob_file: 96 | inblob_file.write(request._in_blob) 97 | 98 | # Read the output of quote and prevent case of resource busy 99 | try: 100 | with open(os.path.join(tempdir, "outblob"), "rb") as outblob_file: 101 | td_quote = outblob_file.read() 102 | except OSError as e: 103 | raise OSError(f"Read outblob failed with OSError: {str(e)}") 104 | except Exception as e: 105 | raise Exception(f"Error in opening outblob file: {str(e)}") 106 | 107 | # Read provider info 108 | with open( 109 | os.path.join(tempdir, "provider"), "r", encoding="utf-8" 110 | ) as provider_file: 111 | provider = provider_file.read() 112 | 113 | # Read generation info 114 | with open( 115 | os.path.join(tempdir, "generation"), "r", encoding="utf-8" 116 | ) as generation_file: 117 | generation = generation_file.read() 118 | # Check if the outblob has been corrupted during file open 119 | if int(generation) > 1: 120 | raise Exception( 121 | f"report generation was {int(generation)} when expecting 1 while reading subtree" 122 | ) 123 | 124 | os.rmdir(tempdir) 125 | if td_quote is not None: 126 | return Response(provider, td_quote, None) 127 | return None 128 | 129 | except FileNotFoundError as e: 130 | raise FileNotFoundError( 131 | f"Caught FileNotFoundError exception in collect_evidence():{str(e.filename)}" 132 | ) 133 | except OSError as e: 134 | raise OSError(f"Caught OSError exception in collect_evidence(): {str(e)}") 135 | except ValueError as e: 136 | raise ValueError( 137 | f"Caught ValueError exception in collect_evidence(): {str(e)}" 138 | ) 139 | except Exception as e: 140 | raise Exception(f"Caught exception in collect_evidence(): {str(e)}") 141 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/connector/README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Client Connector for Python 2 | 3 |

· 08/14/2024 ·

4 | 5 | The Intel® Trust Authority Client Connector for Python is a library of Python modules used to perform remote attestation of a Trusted Execution Environment (TEE) using Intel Trust Authority as the verifier. The "connector" (as we call it) is an interface for the Intel Trust Authority REST API. The process logic for attestation and verification is implemented in the Intel Trust Authority service, not in the connector. The connector is a client that sends requests to the Intel Trust Authority service and processes the responses. 6 | 7 | The **ITAConnector** class includes the following methods for attestation and verification: 8 | 9 | [**`attest`**](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html#attest)
Collects evidence and requests an attestation token from Intel Trust Authority for clients using a Passport validation model. 10 | 11 | ** For Intel TDX and NVIDIA H100 Attestation use `attest_v2`. For more information, see the [GPU attestation](https://docs.trustauthority.intel.com/main/articles/concept-gpu-attestation.html) in the Intel Trust Authority documentation. 12 | 13 | [**`get_nonce`**](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html#get_nonce)
Gets a nonce and parses it to JSON. 14 | 15 | [**`get_token`**](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html#get_token)
Requests an attestation token from Intel Trust Authority. `get_token` Provides more control than `attest` by allowing a confidential app to include user data, provide a nonce, and modify evidence structures before requesting a token. `get_token` supports both Passport and Background-check attestation models. 16 | 17 | ** For NVIDIA H100 Attesation and Unified Attesation please use `get_token_v2`. For more information, see the [GPU attestation](https://docs.trustauthority.intel.com/main/articles/concept-gpu-attestation.html) in the Intel Trust Authority documentation. 18 | 19 | [**`get_token_signing_certificates`**](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html#get_token_signing_certificates)
Retrieves a JSON Web Key Set (JWKS) that contains the collection of signing certificates used by Intel Trust Authority to sign attestation tokens. 20 | 21 | [**`verify_token`**](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html#verify_token)
Verifies that an Intel Trust Authority attestation token is properly formatted and signed. 22 | 23 | A connector requires a TEE adapter (`adapter: EvidenceAdapter` in **AttestArgs**) to collect evidence from the attesting platform. However, a relying party can use the connector to verify a token or perform a background-check attestation without a TEE adapter. The only ITAConnector method that requires a TEE adapter is **attest**. 24 | 25 | To collect evidence from a TEE without requesting attestation, such as for background-check attestation, development, or validation, use the TEE adapter's **collect_evidence** method directly. 26 | 27 | ## Usage 28 | 29 | Follow this basic workflow, modifying it as necessary for your use case: 30 | 31 | 1. Install the library from a wheel package as described in the main README [installation section](https://github.com/intel/trustauthority-client-for-python/tree/main#installation). 32 | 1. Import the required modules into your application. For examples of minimal imports, see the sample applications in the [examples](./inteltrustauthorityclient/examples) folder. 33 | 1. Create an **ITAConnector.config** object and set the required properties: Intel Trust Authority _base URL_**\*\***, _API URL_**\*\***, _API key_, and _retry configuration_. 34 | 1. Create an ITAConnector object with the config object. 35 | 1. If you need to collect evidence from the TEE, you'll need to create an adapter object of the correct type for your TEE. `attest` requires an adapter object in **AttestArgs**, and `collect_evidence` is a method of the adapter object. 36 | 1. Use the connector object (and adapter object, if required) to call the desired method. 37 | 38 | For more information, see the [Python Connector Reference](https://docs.trustauthority.intel.com/main/articles/integrate-python-client.html) in the Intel Trust Authority documentation. Also see the sample applications in the [examples](../../inteltrustauthorityclient/examples) folder. 39 | 40 |

41 | --- 42 | 43 | **\*\*** Intel Trust Authority subscription region determines the base URL you must use. In the European Union region, the base URL is `https://portal.eu.trustauthority.intel.com`, and the API URL is `https://api.eu.trustauthority.intel.com`.
For subscriptions outside the EU, the base URL is `https://portal.trustauthority.intel.com`, and the API URL is `https://api.trustauthority.intel.com`. Subscriptions, URLs, and API keys are region-specific. 44 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/connector/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import re 8 | import binascii 9 | from urllib.parse import urlparse 10 | import uuid 11 | import base64 12 | import validators 13 | import logging as log 14 | from tenacity import wait_exponential 15 | from inteltrustauthorityclient.resources import constants 16 | 17 | 18 | class Config: 19 | """This class creates config object with Intel Trust Authority attributes i.e base url, api url, api key and 20 | instance of retry config class to be used in creating connector object""" 21 | 22 | def __init__(self, retry_cfg, base_url, api_url, api_key) -> None: 23 | """Initialises config object 24 | 25 | Args: 26 | base_url: Intel Trust Authority base url 27 | retry_cfg: Instance of RetryConfig class 28 | api_url: Intel Trust Authority api url 29 | api_key: Intel Trust Authority api key 30 | """ 31 | if not validate_url(base_url): 32 | raise ValueError("Invalid Intel Trust Authority Base URL") 33 | if not validate_url(api_url): 34 | raise ValueError("Invalid Intel Trust Authority API URL") 35 | if not validate_apikey(api_key): 36 | raise ValueError("Invalid Intel Trust Authority API Key") 37 | 38 | self._base_url = base_url 39 | self._api_url = api_url 40 | self._retry_cfg = retry_cfg 41 | self._api_key = api_key 42 | 43 | @property 44 | def base_url(self): 45 | """Getter method.""" 46 | return self._base_url 47 | 48 | @property 49 | def retry_cfg(self): 50 | """Getter method.""" 51 | return self._retry_cfg 52 | 53 | @property 54 | def api_url(self): 55 | """Getter method.""" 56 | return self._api_url 57 | 58 | @property 59 | def api_key(self): 60 | """Getter method.""" 61 | return self._api_key 62 | 63 | 64 | class RetryConfig: 65 | """This class creates Retry Config object with retry max and retry wait time attributes""" 66 | 67 | def __init__( 68 | self, 69 | retry_wait_min: int = None, 70 | retry_wait_max: int = None, 71 | retry_max_num: int = None, 72 | timeout_sec: int = None, 73 | check_retry=None, 74 | backoff=None, 75 | ) -> None: 76 | """Initialises Retry config object""" 77 | self.retry_wait_min_sec = ( 78 | retry_wait_min 79 | if retry_wait_min != 0 80 | else constants.DEFAULT_RETRY_WAIT_MIN_SEC 81 | ) 82 | self.retry_wait_max_sec = ( 83 | retry_wait_max 84 | if retry_wait_max != 0 85 | else constants.DEFAULT_RETRY_WAIT_MAX_SEC 86 | ) 87 | self.retry_max_num = ( 88 | retry_max_num if retry_max_num != 0 else constants.DEFAULT_RETRY_MAX_NUM 89 | ) 90 | self.backoff = ( 91 | backoff 92 | if backoff != None 93 | else wait_exponential( 94 | multiplier=1, min=self.retry_wait_min_sec, max=self.retry_wait_max_sec 95 | ) 96 | ) 97 | self.check_retry = check_retry if check_retry != None else self.retry_policy 98 | self.timeout_sec = ( 99 | timeout_sec if timeout_sec != None else constants.DEFAULT_CLIENT_TIMEOUT_SEC 100 | ) 101 | 102 | def retry_policy(self, status_code): 103 | retryable_status_code = (500, 503, 504) 104 | return status_code in retryable_status_code 105 | 106 | 107 | def validate_url(url): 108 | parsed_url = validators.url(url) 109 | if parsed_url: 110 | if urlparse(url).scheme != "https": 111 | log.error("URL scheme has to be https") 112 | return False 113 | return True 114 | return False 115 | 116 | 117 | def validate_uuid(uuid_str): 118 | try: 119 | uuid.UUID(uuid_str) 120 | return True 121 | except ValueError as exc: 122 | log.error(f"ValueError occurred in UUID check request: {exc}") 123 | return False 124 | except TypeError as exc: 125 | log.error(f"TypeError occurred in UUID check request: {exc}") 126 | return False 127 | 128 | 129 | def validate_requestid(req_id): 130 | # Request ID should be atmost 128 characters long and should contain only alphanumeric characters, _, space, -, ., / or \ 131 | request_id_pattern = r'^[a-zA-Z0-9\s\-_\.\\\/]{1,128}$' 132 | return bool(re.fullmatch(request_id_pattern, req_id)) 133 | 134 | def validate_apikey(api_key): 135 | # api_key has to be a valid base64 encoded string 136 | try: 137 | if(api_key != base64.b64encode(base64.b64decode(api_key)).decode()): 138 | return False 139 | return True 140 | except binascii.Error as exc: 141 | log.error(f"Error in apikey validation :{exc}, API key must be a valid Base64 Encoded string") 142 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/connector/evidence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | from enum import Enum 8 | 9 | 10 | class EvidenceType(Enum): 11 | """Enum for Evidence Type.""" 12 | 13 | SGX, TDX, AZTDX, NVGPU = range(4) 14 | 15 | def __str__(self) -> str: 16 | if self == EvidenceType.SGX: 17 | return "sgx" 18 | elif self == EvidenceType.TDX: 19 | return "tdx" 20 | elif self == EvidenceType.AZTDX: 21 | return "aztdx" 22 | elif self == EvidenceType.NVGPU: 23 | return "nvgpu" 24 | else: 25 | return "unknown" 26 | 27 | 28 | class Evidence: 29 | """Contains the attributes to be sent for attestation of platform.""" 30 | 31 | def __init__( 32 | self, 33 | type: EvidenceType, 34 | evidence: bytearray, 35 | user_data: bytearray, 36 | runtime_data: bytearray, 37 | ) -> None: 38 | self._type = type 39 | self._evidence = evidence 40 | self._user_data = user_data 41 | self._runtime_data = runtime_data 42 | 43 | @property 44 | def type(self): 45 | """Getter method.""" 46 | return self._type 47 | 48 | @property 49 | def evidence(self): 50 | return self._evidence 51 | 52 | @property 53 | def user_data(self): 54 | """Getter method.""" 55 | return self._user_data 56 | 57 | @property 58 | def runtime_data(self): 59 | """Getter method.""" 60 | return self._runtime_data 61 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/.env: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Copyright (c) 2023-2024 Intel Corporation 3 | # All rights reserved. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | ########################################### 6 | 7 | # Ubuntu base image version 8 | UBUNTU_VERSION=20.04 9 | 10 | # Trust Authority Client version 11 | # Change this value for release 12 | TRUST_AUTHORITY_CLIENT_VERSION=v1.1.0 13 | 14 | # Intel SGX sdk version variables 15 | DCAP_VERSION=1.19.100.3-focal1 16 | PSW_VERSION=2.22.100.3 17 | 18 | # Adapter selection 19 | # Change this to sgx/aztdx/nvgpu as per the need to build the correct image. 20 | ADAPTER_TYPE=tdx -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/Dockerfile: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Copyright (c) 2023-2024 Intel Corporation 3 | # All rights reserved. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | ########################################### 6 | 7 | ARG VERSION=v0.0.0 8 | ARG DCAP_VERSION=0.0 9 | ARG PSW_VERSION=0.0 10 | 11 | ARG UBUNTU_VERSION 12 | FROM ubuntu:$UBUNTU_VERSION as build_base 13 | ARG DEBIAN_FRONTEND=noninteractive 14 | ARG ENABLE_DEBUG 15 | 16 | RUN apt-get update && \ 17 | apt-get upgrade -y && \ 18 | apt-get autoremove -y && \ 19 | apt-get install -y --no-install-recommends ca-certificates pip 20 | 21 | # Set the working directory inside the container 22 | WORKDIR /app 23 | COPY pyproject.toml poetry.lock ./ 24 | # Install poetry 25 | RUN pip3 install --no-cache-dir poetry 26 | COPY . . 27 | RUN poetry build 28 | 29 | FROM ubuntu:$UBUNTU_VERSION 30 | ARG DCAP_VERSION 31 | ARG PSW_VERSION 32 | ARG USERNAME=intel 33 | ARG USER_UID=1001 34 | ARG USER_GID=$USER_UID 35 | 36 | RUN groupadd --gid $USER_GID $USERNAME && \ 37 | useradd --uid $USER_UID --gid $USER_GID -m $USERNAME 38 | ARG ADAPTER_TYPE 39 | ENV ADAPTER_TYPE=${ADAPTER_TYPE} 40 | RUN apt update && apt install -y --no-install-recommends gnupg wget python3 pip build-essential 41 | 42 | # Adding SGX repo to Ubuntu focal 43 | RUN echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main' > /etc/apt/sources.list.d/intel-sgx.list && \ 44 | wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key -O /tmp/intel-sgx-deb.key && \ 45 | apt-key add /tmp/intel-sgx-deb.key && \ 46 | rm /tmp/intel-sgx-deb.key 47 | 48 | WORKDIR /opt/intel 49 | # Download SGX SDK binary 50 | RUN wget -q https://download.01.org/intel-sgx/sgx-linux/2.22/distro/ubuntu20.04-server/sgx_linux_x64_sdk_${PSW_VERSION}.bin && \ 51 | chmod +x sgx_linux_x64_sdk_${PSW_VERSION}.bin && \ 52 | echo 'yes' | ./sgx_linux_x64_sdk_${PSW_VERSION}.bin 53 | 54 | # Installing Linux SGX SDK 55 | RUN apt-get update && apt-get install -y --no-install-recommends \ 56 | libsgx-urts=${PSW_VERSION}-focal1 \ 57 | libsgx-qe3-logic=${DCAP_VERSION} \ 58 | libsgx-pce-logic=${DCAP_VERSION} \ 59 | libsgx-dcap-ql=${DCAP_VERSION} \ 60 | libsgx-dcap-ql-dev=${DCAP_VERSION} \ 61 | libcurl4-openssl-dev \ 62 | libsgx-dcap-default-qpl=${DCAP_VERSION} \ 63 | libsgx-quote-ex=${PSW_VERSION}-focal1 64 | 65 | COPY --from=build_base /app/dist/applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl . 66 | RUN pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl 67 | WORKDIR /app 68 | COPY . . 69 | 70 | # Change permissions for intel user to run make command in /minimal-enclave 71 | RUN chown -R $USERNAME:$USERNAME /app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave 72 | 73 | # Change current user to intel inside the container 74 | USER $USERNAME 75 | WORKDIR /app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave 76 | RUN make clean all 77 | 78 | # Set LD_LIBRARY_PATH 79 | ENV LD_LIBRARY_PATH=/app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave:/usr/local/lib/:/usr/lib/ 80 | 81 | # Set the working directory inside the container 82 | WORKDIR /app/inteltrustauthorityclient/examples/sgx_sample_app 83 | ENTRYPOINT ["python3", "sgx_sample_app.py"] -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/README.md: -------------------------------------------------------------------------------- 1 | # Intel SGX Attestation Sample Application 2 | 3 |

· 09/28/2024 ·

4 | 5 | The Intel® Software Guard Extensions (Intel® SGX) attestation sample app is a Python application that uses the Intel® Trust Authority Client for Python packages to attest an Intel SGX enclave. The attestation verifier is [Intel® Trust Authority](https://trustauthority.intel.com). 6 | 7 | The sample application runs in a minimal Intel SGX enclave. When the sample app is run, it does the following: 8 | 9 | 1. Evokes the Python client and TEE adapter to collect a quote from the host 10 | 1. Calls the Intel Trust Authority REST API to request an attestation token, using the quote obtained in step 1. 11 | 1. If attestation is successful, the sample app prints the JWT and other information to the terminal. A real application would most likely use the attestation token to authenticate with a service or to authorize access to a resource. This is known as the _passport_ attestation model. 12 | 13 | The following retro, ASCII-like diagram depicts the components used in the Intel SGX attestation sample app running within a Docker container that is itself running on an Intel SGX host. 14 | 15 | ``` 16 | ┌────────────────────────────────────────────────┐ 17 | │ ┌──────────────────────────────────────┐ │ 18 | │ │ Docker Container │ │ 19 | │ │ │ │ 20 | │ │ ┌──────────────────────────┐ │ │ 21 | │ │ │Intel SGX attestation app │ │ │ 22 | │ │ └──────────────────────────┘ │ │ 23 | │ │ │ │ 24 | │ │ ┌──────────────────────────┐ │ | 25 | │ │ │ enclave.signed.so │ │ │ 26 | │ │ └──────────────────────────┘ │ │ ┌────────────────┐ 27 | | | | | | | 28 | | | | | | | 29 | | │ ┌────────────────────────────┐ │◄───┼───────────────►│ INTEL TRUST │ 30 | │ │ │applications_security_amber | | | | AUTHORITY | 31 | | | | _trustauthority_client_ | | | | SERVICE | 32 | | | | for_python-1.1.0-py3-none | | | | | 33 | | | | -any.whl | | | | | 34 | | │ | │ │ │ | │ 35 | │ │ └────────────────────────────┘ │ │ │ │ 36 | │ │ │ │ └────────────────┘ 37 | │ │ │ │ 38 | │ └──────────────────────────────────────┘ │ 39 | │ │ 40 | │ Intel SGX Host │ 41 | └────────────────────────────────────────────────┘ 42 | ``` 43 | 44 | 45 | ## How to build and run the application 46 | 47 | The sample app can be run as a [Docker container](#run-the-application-in-a-docker-container) or as a [native application on an Intel SGX host](#run-the-sample-application-from-an-intel-sgx-host). In either case, you must set certain runtime variables before running the sample app. 48 | 49 | ### Runtime variables 50 | 51 | The Python client and REST API require configuration before use. The following runtime variables correspond to client configuration, API endpoints, and authorization settings in the client API. For the Docker container, you will set these variables in the `sgx_token.env` file. For the native application, you'll export a set of variables in the shell environment. The following table describes the runtime variables and their purpose. 52 | 53 | Many of these variables are optional but several are required as indicated. 54 | 55 | | Variable | Type | Required? | Description | 56 | | :--- | :--- | :--- | :--- | 57 | | `HTTP_PROXY` | String | No | HTTP proxy host. [1] | 58 | | `HTTPS_PROXY` | String | No | HTTPS proxy host. [1]| 59 | | `TRUSTAUTHORITY_BASE_URL` | String | Yes | Base URL for the Intel Trust Authority service. [2]| 60 | | `TRUSTAUTHORITY_API_URL` | String | Yes | API URL for the Intel Trust Authority service. [2] | 61 | | `TRUSTAUTHORITY_API_KEY` | String | Yes | Attestation API key required for authorization. [3] For more information, see [User roles and API keys](https://docs.trustauthority.intel.com/main/articles/concept-user-roles-and-api-keys.html) in the Intel Trust Authority Documentation. | 62 | | `TRUSTAUTHORITY_REQUEST_ID` | String | No | If not provided, a request ID will be created by the API gateway. An auto-generated request ID is not guaranteed to be unique.| 63 | | `TRUSTAUTHORITY_POLICY_ID` | String | No | If supplied, the value can be a single policy ID (UUID) or a list of policy IDs separated by commas. For more information, see [Attestation Policies](https://docs.trustauthority.intel.com/main/articles/concept-policy-v2.html) in the Intel Trust Authority documentation.| 64 | | `RETRY_MAX` | Integer | No | Maximum number of retries. | 65 | | `RETRY_WAIT_TIME_MAX` | Integer | No | Maximum retry wait time. | 66 | | `RETRY_WAIT_TIME_MIN` | Integer | No | Minimum retry wait time. | 67 | | `CLIENT_TIMEOUT_SEC` | Integer | No | Request timeout in seconds. | 68 | | `LOG_LEVEL` | String | No | Log level. | 69 | | `ADAPTER_TYPE` | String | Yes | Specifies which adapter to collect evidence for a quote. For this example, the only allowable value is "sgx". | 70 | | `POLICY_MUST_MATCH` | Boolean | No | If set to `true`, the policy (or policies, if more than one) specified by TRUSTAUTHORITY_POLICY_ID or the API key must all match for an attestation token to be issued. If not supplied or set to `false`, the default behavior is to issue an attestation token even if one or more policies fail to match. | 71 | | `TOKEN_SIGNING_ALGORITHM` | String | No | It specifies the algorithm to be used for signing the token. If supplied, it must be one of **PS384** or **RS256**. Defaults to PS384. | 72 | 73 | **Notes**
74 | **[1]** The `HTTP_PROXY` and `HTTPS_PROXY` variables are optional. If your system is behind a proxy, set these variables to the proxy host. If you are not behind a proxy, you can leave these variables unset.
75 | **[2]** The `TRUSTAUTHORITY_BASE_URL` and `TRUSTAUTHORITY_API_URL` URLs are determined by the region associated with your subscription. If you're using the **US** region, the URLS are `https://portal.trustauthority.intel.com` and `https://api.trustauthority.intel.com` respectively. If you're using the **EU** region, the URLs are `https://portal.eu.trustauthority.intel.com` and `https://api.eu.trustauthority.intel.com` respectively.
**[3]** The API key must be created in the same region as the URLs above. An API key that was created in one region won't work in the other region. 76 | 77 | ### Run the application in a Docker container 78 | 79 | The Intel SGX attestation sample application can be encapsulated as a container, enabling it to be executed in containerized environments. Begin by copying the Intel Trust Authority Client for Python repository to a local folder. Then build the Docker image, create the `sgx_token.env` file, and run the sample app. 80 | 81 | #### Prerequisites 82 | 83 | - Use Docker version 20.10.17 or a more recent release. Refer to the guide at https://docs.docker.com/engine/install/ubuntu/ for detailed instructions on Docker installation. 84 | - Use docker-compose version 1.29.2 or a more recent release. Follow the steps outlined at https://docs.docker.com/compose/install/linux/#install-the-plugin-manually for installing docker-compose. 85 | - A Intel SGX host with the Intel SGX driver. 86 | - A subscription to Intel Trust Authority. If you don't have a subscription, you can find out how to get one at [Intel Trust Authority](https://trustauthority.intel.com). 87 | 88 | #### Build instructions 89 | 90 | 1. Build the sample application Docker image in **/inteltrustauthorityclient/examples/sgx_sample_app/** with the following command. 91 | 92 | ```sh 93 | cat < 104 | TRUST_AUTHORITY_CLIENT_VERSION — The version of the sample app Docker image. This version number is used to tag the Docker image.
105 | DCAP_VERSION — The version of Intel® SGX DCAP installed on the Intel SGX host.
106 | PSW_VERSION — The version of Intel® SGX PSW installed on the Intel SGX host.
107 | ADAPTER_TYPE — "sgx" is the only allowable value for this example. 108 | 109 | 2. The docker image must be run on the Intel SGX host. For example, it can be exported or copied 110 | from a build machine as follows... 111 | ```sh 112 | #Save the sgx sample app Docker image into trust_authority_python_client_sgx_sample_app.tar.gz 113 | docker save trust_authority_python_client_sgx_sample_app:v1.1.0 > trust_authority_python_client_sgx_sample_app.tar.gz 114 | #scp trust_authority_python_client_sgx_sample_app.tar.gz to the TD VM. 115 | #On the TD VM load/import trust_authority_python_client_sgx_sample_app.tar.gz docker image using below command 116 | docker load -i trust_authority_python_client_sgx_sample_app.tar.gz 117 | ``` 118 | 119 | 3. Once the image is built using the above `docker compose` command or loaded from the tar file, 120 | the `SGX Attestation Sample App` can be run using the following commands. Substitute the correct values for the placeholders in the `sgx_token.env` file, and remove the variables that are not required. 121 | 122 | ```sh 123 | # Creating sgx_token.env file 124 | cat < 126 | HTTPS_PROXY= 127 | TRUSTAUTHORITY_BASE_URL= 128 | TRUSTAUTHORITY_API_URL= 129 | TRUSTAUTHORITY_API_KEY= 130 | TRUSTAUTHORITY_REQUEST_ID= 131 | TRUSTAUTHORITY_POLICY_ID= 132 | RETRY_MAX= 133 | RETRY_WAIT_TIME_MAX= 134 | RETRY_WAIT_TIME_MIN= 135 | CLIENT_TIMEOUT_SEC= 136 | LOG_LEVEL= 137 | SGX_AESM_ADDR=1 138 | POLICY_MUST_MATCH=True/False 139 | TOKEN_SIGNING_ALGORITHM= 140 | EOF 141 | 142 | # Use docker to run the SGX Sample App... 143 | sudo docker run \ 144 | --rm \ 145 | -it \ 146 | --device=/dev/sgx_enclave \ 147 | --device=/dev/sgx_provision \ 148 | -v /var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket \ 149 | --env-file sgx_token.env \ 150 | --group-add $(getent group sgx_prv | cut -d: -f3) \ 151 | trust_authority_python_client_sgx_sample_app:v1.1.0 152 | 153 | ``` 154 | 155 | If the sample application runs successfully, the attestation token returned from Intel Trust Authority and other information will be displayed. 156 | 157 | ### Run the sample application from an Intel SGX host 158 | 159 | Begin by copying the Intel Trust Authority Client for Python repository to a local folder where you will build and install the application. Then build and install the Python wheel package containing the connector and adapter packages. Export the required environment variables and run the sample app. 160 | 161 | #### Prerequisites 162 | 163 | - Python 3.8 or later 164 | - Poetry. You can install **poetry** using the command `pip3 install --no-cache-dir poetry`. 165 | - An Intel SGX host with the Intel SGX driver. 166 | - A subscription to Intel Trust Authority. If you don't have a subscription, you can find out how to get one at [Intel Trust Authority](https://trustauthority.intel.com). 167 | 168 | #### Build instructions 169 | 170 | 1. Build the Python wheel package containing connector and adapter packages from https://github.com/intel/trustauthority-client-for-python folder containing the poetry configuration files using the following command: 171 | 172 | ```sh 173 | cd ../../.. && \ 174 | poetry shell && \ 175 | poetry build 176 | ``` 177 | 178 | 2. Go to the distribution folder where the whl package was created and install the package using the following command. 179 | 180 | ```python 181 | pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl 182 | ``` 183 | 184 | 3. Export environment variables. 185 | 186 | The following environment variables can be exported before running the sample app. Most of these variables are optional, but several are required. Substitute the correct values for the placeholders and remove the variables that are not required. See [Runtime variables](#runtime-variables) for details. 187 | 188 | ```sh 189 | export HTTP_PROXY= 190 | export HTTPS_PROXY= 191 | export TRUSTAUTHORITY_BASE_URL= 192 | export TRUSTAUTHORITY_API_URL= 193 | export TRUSTAUTHORITY_API_KEY= 194 | export TRUSTAUTHORITY_REQUEST_ID= 195 | export TRUSTAUTHORITY_POLICY_ID= 196 | export RETRY_MAX= 197 | export RETRY_WAIT_TIME_MAX= 198 | export RETRY_WAIT_TIME_MIN= 199 | export CLIENT_TIMEOUT_SEC= 200 | export LOG_LEVEL= 201 | export SGX_AESM_ADDR=1 202 | export ADAPTER_TYPE="sgx" 203 | export POLICY_MUST_MATCH=True/False 204 | export TOKEN_SIGNING_ALGORITHM= 205 | ``` 206 | 207 | 4. Run the Sample App in **/inteltrustauthorityclient/examples/sgx_sample_app/** after setting the environment variables using the following command: 208 | 209 | ```sh 210 | python sgx_sample_app.py 211 | ``` 212 | 213 | If successful, the token and other information will be displayed. 214 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Copyright (c) 2023-2024 Intel Corporation 3 | # All rights reserved. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | ########################################### 6 | 7 | version: '3' 8 | services: 9 | trust_authority_python_client_sgx_sample_app: 10 | image: trust_authority_python_client_sgx_sample_app:${TRUST_AUTHORITY_CLIENT_VERSION} 11 | container_name: trust_authority_python_client_sgx_sample_app 12 | build: 13 | context: ../../.. 14 | dockerfile: inteltrustauthorityclient/examples/sgx_sample_app/Dockerfile 15 | args: 16 | - UBUNTU_VERSION=${UBUNTU_VERSION} 17 | - DCAP_VERSION=${DCAP_VERSION} 18 | - PSW_VERSION=${PSW_VERSION} 19 | - ADAPTER_TYPE=${ADAPTER_TYPE} 20 | env_file: 21 | - ../.env 22 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/.config_HW_RELEASE_x64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/.config_HW_RELEASE_x64 -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.config.xml: -------------------------------------------------------------------------------- 1 | 2 | 0 3 | 0 4 | 0x40000 5 | 0x100000 6 | 1 7 | 1 8 | 1 9 | 0 10 | 0xFFFFFFFF 11 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Intel Corporation. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in 12 | * the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Intel Corporation nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #include "Enclave_t.h" 33 | 34 | #include "sgx_trts.h" 35 | #include "sgx_error.h" 36 | #include "sgx_report.h" 37 | #include "sgx_utils.h" 38 | #include "sgx_tcrypto.h" 39 | #include "mbusafecrt.h" /* memcpy_s */ 40 | 41 | /* Global copy of RSA key pair */ 42 | static rsa_params_t g_rsa_key; 43 | 44 | /* Have we generated RSA key pair already? */ 45 | static bool key_pair_created = false; 46 | 47 | sgx_status_t enclave_create_pubkey( 48 | rsa_params_t* key) 49 | { 50 | sgx_status_t status; 51 | key->e[0] = 0x10001; 52 | g_rsa_key.e[0] = 0x10001; 53 | 54 | if (!key_pair_created) { 55 | 56 | status = sgx_create_rsa_key_pair(N_SIZE_IN_BYTES, 57 | E_SIZE_IN_BYTES, 58 | (unsigned char*)g_rsa_key.n, 59 | (unsigned char*)g_rsa_key.d, 60 | (unsigned char*)g_rsa_key.e, 61 | (unsigned char*)g_rsa_key.p, 62 | (unsigned char*)g_rsa_key.q, 63 | (unsigned char*)g_rsa_key.dmp1, 64 | (unsigned char*)g_rsa_key.dmq1, 65 | (unsigned char*)g_rsa_key.iqmp); 66 | 67 | if (SGX_SUCCESS != status) { 68 | return status; 69 | } 70 | key_pair_created = true; 71 | } 72 | 73 | for(int i = 0; i < N_SIZE_IN_BYTES; i++) { 74 | key->n[i] = g_rsa_key.n[i]; 75 | } 76 | for(int i = 0; i < E_SIZE_IN_BYTES; i++) { 77 | key->e[i] = g_rsa_key.e[i]; 78 | } 79 | 80 | return SGX_SUCCESS; 81 | } 82 | 83 | uint32_t enclave_create_report(const sgx_target_info_t* p_qe3_target, 84 | uint8_t* nonce, 85 | uint32_t nonce_size, 86 | sgx_report_t* p_report) 87 | { 88 | sgx_status_t status = SGX_SUCCESS; 89 | sgx_report_data_t report_data = {0}; 90 | uint8_t msg_hash[64] = {0}; 91 | 92 | const uint32_t size = nonce_size + E_SIZE_IN_BYTES + N_SIZE_IN_BYTES; 93 | 94 | uint8_t* pdata = (uint8_t *)malloc(size); 95 | if (!pdata) { 96 | return status; 97 | } 98 | 99 | errno_t err = 0; 100 | err = memcpy_s(pdata, nonce_size, nonce, nonce_size); 101 | if (err != 0) { 102 | goto ERROR; 103 | } 104 | 105 | err = memcpy_s(pdata + nonce_size, E_SIZE_IN_BYTES, ((unsigned char *)g_rsa_key.e), E_SIZE_IN_BYTES); 106 | if (err != 0) { 107 | goto ERROR; 108 | } 109 | 110 | err = memcpy_s(pdata + nonce_size + E_SIZE_IN_BYTES, N_SIZE_IN_BYTES, ((unsigned char *)g_rsa_key.n), N_SIZE_IN_BYTES); 111 | if (err != 0) { 112 | goto ERROR; 113 | } 114 | 115 | status = sgx_sha256_msg(pdata, size, (sgx_sha256_hash_t *)msg_hash); 116 | if (SGX_SUCCESS != status) { 117 | goto ERROR; 118 | } 119 | 120 | err = memcpy_s(report_data.d, sizeof(msg_hash), msg_hash, sizeof(msg_hash)); 121 | if (err != 0) { 122 | status = SGX_ERROR_UNEXPECTED; 123 | goto ERROR; 124 | } 125 | 126 | // Generate the report for the app_enclave 127 | status = sgx_create_report(p_qe3_target, &report_data, p_report); 128 | if (SGX_SUCCESS != status) { 129 | goto ERROR; 130 | } 131 | 132 | ERROR: 133 | free(pdata); 134 | return status; 135 | } 136 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.edl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Intel Corporation. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in 12 | * the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Intel Corporation nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | enclave { 33 | include "sgx_report.h" 34 | include "sgx_tcrypto.h" 35 | 36 | trusted { 37 | 38 | public sgx_status_t enclave_create_pubkey([out]rsa_params_t* key); 39 | 40 | public uint32_t enclave_create_report([in]const sgx_target_info_t* p_qe3_target, 41 | [in, count=nonce_size]uint8_t* nonce, uint32_t nonce_size, 42 | [out]sgx_report_t* p_report); 43 | 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.lds: -------------------------------------------------------------------------------- 1 | Enclave.so 2 | { 3 | global: 4 | g_global_data_sim; 5 | g_global_data; 6 | enclave_entry; 7 | g_peak_heap_used; 8 | local: 9 | *; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.o -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_private_sample.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIG4QIBAAKCAYEAkj+OHOD9NYg+GFWalesDTpViDG21caYJuQtSqiebsbZsHvn8 3 | ErP3zfOTwIkBJkUNxHc4CBJe8UFB1Bke/q86eL2Cfwy50PrGxf0tmENFklRGFaFb 4 | tyCsZYoJiOuRG9UxUj01NBYDy6cCi9s7UUyMrwPHxgRV4KtnhNmg0f2rCxXUkhx4 5 | m/jYZATj7b42DYxrb0iV8Z/WQ85KTyQlh43b3rJRZLvffsil5NqKMJJ9DK7xmzq8 6 | Ymns3rvqhJUUHvk4Ptj75yfZrNUNKJVf6eGs7514hRAuecNwBuA574vAlIJ3kAWB 7 | UicVWUOSkTNMDHKyRUn077f2bJHnmu3EWUATw21gcLN2XP1p7tBglZejAJYYipYK 8 | CQ19q542gIpZR5nMdKatGINPBB0yEV/TPKBV4rO4E2t659oaH6BR/VuNPDb/LdG0 9 | /dsItCZwUPM16dEVE+nVPG8uy1HNPbkapU5moEs6+QOOz3ERSuBVnsP0PsC6MdMx 10 | MI+rtREVayZ2mj61AgEDAoIBgGF/tBNAqM5a1BA5EblHV4m47AhJI6EZW9CyNxwa 11 | Z8vO8r9RUrcipTP3t9Wwq27Ys9hPerAMP0uA1o1mFKnKJvspAaoIe+CnLy6oyRAs 12 | 2Qw4LrkWPSTAcu5cBltHthKOIOF+I3gOrTJvVwfnfODdsx9X2oQC4+sc763mazap 13 | HLIOjba9pb1QkEKt7UkpeV5dnPTbDqEVOYKJht9tblpekpR24O3SlP8wbpiRsXW2 14 | /gh0oRInKEGb8z8n8a24uBSmJHk7XF7aC/0k8ZOc4ONCA+yPZAB9+oehtjg3Ydke 15 | /O00bTSUiDQtq+ZDXT12LLJnMw870HjWRG4rDQsVR99HfnaH5iW02HHfRpi2zzqn 16 | pNlr9bjJIJIl7Pv4wigWtJRA7RwyGfQiar8xmDL8VBPYgAzjysWOCJw3FBfMBllN 17 | bEsrMi8r9qq7/2MBk3rjsD67KRF+Z41yb44md3Pz+9I12+zwjU1tyWquEpu/PDa0 18 | Hl4u7yD25ap89+Z75tSURq6hKwKBwQCiQYJ2ViroJYYbsfKd37lMwV3at+pjStM1 19 | oQGrHBRgzlVji+rSkYAfFnjs++931JF68Yn1YYZ0TgLiLygcE+AZJXzaK1e+q0vH 20 | FWyHQ6yRvaTEEbXMjVp9wz9U0vHc4Yap9eBhEfAYs+r1t20L1rNuTc+oQrN3Qz3l 21 | 11/M2wLcwlOz8Zc6SEUUsRBo5+uU6hjRcktaDffSvVOiY/uBN1QvmqO87kSOKXdB 22 | ZddK0tatPXD7q7uDlXPi3KcSWRJgBnECgcEA5r5u4oqcyPgcr3gb9x7twAUEqZtM 23 | SwYLfOwlfsEwz+V+XZrKMVETYMgNyAYZiQNUM36yRRUJUwNRKB6wbkD1+HwKXfjZ 24 | 84M0Qqi9Nlh/KLaDMVajYUc/s+XT75VYjFbiIGWlyT2+zJ7izy3oEwTiYRA4QtO6 25 | hkQ+Fhboqoxn7rPgMx4rw3jEivMCr7XB9s4+qDnujjWPo1rGbWclaMZIjUfDR/Cc 26 | IUJ+H9ukAeLwBDzihLUdLT7D+HqUGe76NEaFAoHAbCusTuQcmsOuvSFMaT/Q3dY+ 27 | kc/xl4c3eRYBHL1i6zQ47Qfx4bZVag77SKf0+o22UfZb+Ouu+DQB7B9wErfqu25T 28 | PBzlKceH2g5IWi0dtn5t2AvOiF48U9d/jeH2k0EEcU6Vlgv1Zc1HTnpIso8iSYk1 29 | GtciT4IpQ+TqiJIB6IGNIqEPfDAuDctgRe/yY0a7NkwyPAlP4dONFu1SViTiymcX 30 | 00mDCXD6K5k6MeHkc35LUnJ9AmOilz3EtuYMQARLAoHBAJnUSexcaIX6vcpQEqS/ 31 | SSquAxu83YdZXP3yw6nWIIqY/ukR3CDgt5Xas9quu7Cs4sz/IYNjW4ys4MVpyvQr 32 | TqWoBulQkU0CItcbKM7lqhskV3Y5wkDaKnfujUpjkF2PQWruboYpKd2/Qd9z8Ayt 33 | 7EC1etc30a7YKWQPRccIRUnNQCIUHSz7LbH3Vx/OgU80KcV79F7OX8I8hEjvbkXZ 34 | hbOFLNqgaBYsVBU9GAFB9VgolwMjaMjUgqWnDWafUXgvAwKBwFadxA0Nle+f6Q12 35 | vbxbPQ22BJfWM/+GuABaRDr2NR4BsvqgaU+c3LCYr1rv2GhGVoOOvvuAKJS4XJ8Z 36 | FvsVnjPJfFfHZRisOdUDqif3Op1MUclqiWzkIPyP7388YVBDrtp1KrikIiXPojyx 37 | PEJMeOpxqbNiiMCjhp0/NQ9umC15aNT9N0AsEwhV1HssARW587JC3heOCA2AInD0 38 | 0miY3le4fHmF+H4gTKqiNbTGPnCp+D4h7mEgysJZUbSZcYZveA== 39 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.c: -------------------------------------------------------------------------------- 1 | #include "Enclave_t.h" 2 | 3 | #include "sgx_trts.h" /* for sgx_ocalloc, sgx_is_outside_enclave */ 4 | #include "sgx_lfence.h" /* for sgx_lfence */ 5 | 6 | #include 7 | #include /* for memcpy_s etc */ 8 | #include /* for malloc/free etc */ 9 | 10 | #define CHECK_REF_POINTER(ptr, siz) do { \ 11 | if (!(ptr) || ! sgx_is_outside_enclave((ptr), (siz))) \ 12 | return SGX_ERROR_INVALID_PARAMETER;\ 13 | } while (0) 14 | 15 | #define CHECK_UNIQUE_POINTER(ptr, siz) do { \ 16 | if ((ptr) && ! sgx_is_outside_enclave((ptr), (siz))) \ 17 | return SGX_ERROR_INVALID_PARAMETER;\ 18 | } while (0) 19 | 20 | #define CHECK_ENCLAVE_POINTER(ptr, siz) do { \ 21 | if ((ptr) && ! sgx_is_within_enclave((ptr), (siz))) \ 22 | return SGX_ERROR_INVALID_PARAMETER;\ 23 | } while (0) 24 | 25 | #define ADD_ASSIGN_OVERFLOW(a, b) ( \ 26 | ((a) += (b)) < (b) \ 27 | ) 28 | 29 | 30 | typedef struct ms_enclave_create_pubkey_t { 31 | sgx_status_t ms_retval; 32 | rsa_params_t* ms_key; 33 | } ms_enclave_create_pubkey_t; 34 | 35 | typedef struct ms_enclave_create_report_t { 36 | uint32_t ms_retval; 37 | const sgx_target_info_t* ms_p_qe3_target; 38 | uint8_t* ms_nonce; 39 | uint32_t ms_nonce_size; 40 | sgx_report_t* ms_p_report; 41 | } ms_enclave_create_report_t; 42 | 43 | static sgx_status_t SGX_CDECL sgx_enclave_create_pubkey(void* pms) 44 | { 45 | CHECK_REF_POINTER(pms, sizeof(ms_enclave_create_pubkey_t)); 46 | // 47 | // fence after pointer checks 48 | // 49 | sgx_lfence(); 50 | ms_enclave_create_pubkey_t* ms = SGX_CAST(ms_enclave_create_pubkey_t*, pms); 51 | ms_enclave_create_pubkey_t __in_ms; 52 | if (memcpy_s(&__in_ms, sizeof(ms_enclave_create_pubkey_t), ms, sizeof(ms_enclave_create_pubkey_t))) { 53 | return SGX_ERROR_UNEXPECTED; 54 | } 55 | sgx_status_t status = SGX_SUCCESS; 56 | rsa_params_t* _tmp_key = __in_ms.ms_key; 57 | size_t _len_key = sizeof(rsa_params_t); 58 | rsa_params_t* _in_key = NULL; 59 | sgx_status_t _in_retval; 60 | 61 | CHECK_UNIQUE_POINTER(_tmp_key, _len_key); 62 | 63 | // 64 | // fence after pointer checks 65 | // 66 | sgx_lfence(); 67 | 68 | if (_tmp_key != NULL && _len_key != 0) { 69 | if ((_in_key = (rsa_params_t*)malloc(_len_key)) == NULL) { 70 | status = SGX_ERROR_OUT_OF_MEMORY; 71 | goto err; 72 | } 73 | 74 | memset((void*)_in_key, 0, _len_key); 75 | } 76 | _in_retval = enclave_create_pubkey(_in_key); 77 | if (memcpy_verw_s(&ms->ms_retval, sizeof(ms->ms_retval), &_in_retval, sizeof(_in_retval))) { 78 | status = SGX_ERROR_UNEXPECTED; 79 | goto err; 80 | } 81 | if (_in_key) { 82 | if (memcpy_verw_s(_tmp_key, _len_key, _in_key, _len_key)) { 83 | status = SGX_ERROR_UNEXPECTED; 84 | goto err; 85 | } 86 | } 87 | 88 | err: 89 | if (_in_key) free(_in_key); 90 | return status; 91 | } 92 | 93 | static sgx_status_t SGX_CDECL sgx_enclave_create_report(void* pms) 94 | { 95 | CHECK_REF_POINTER(pms, sizeof(ms_enclave_create_report_t)); 96 | // 97 | // fence after pointer checks 98 | // 99 | sgx_lfence(); 100 | ms_enclave_create_report_t* ms = SGX_CAST(ms_enclave_create_report_t*, pms); 101 | ms_enclave_create_report_t __in_ms; 102 | if (memcpy_s(&__in_ms, sizeof(ms_enclave_create_report_t), ms, sizeof(ms_enclave_create_report_t))) { 103 | return SGX_ERROR_UNEXPECTED; 104 | } 105 | sgx_status_t status = SGX_SUCCESS; 106 | const sgx_target_info_t* _tmp_p_qe3_target = __in_ms.ms_p_qe3_target; 107 | size_t _len_p_qe3_target = sizeof(sgx_target_info_t); 108 | sgx_target_info_t* _in_p_qe3_target = NULL; 109 | uint8_t* _tmp_nonce = __in_ms.ms_nonce; 110 | uint32_t _tmp_nonce_size = __in_ms.ms_nonce_size; 111 | size_t _len_nonce = _tmp_nonce_size * sizeof(uint8_t); 112 | uint8_t* _in_nonce = NULL; 113 | sgx_report_t* _tmp_p_report = __in_ms.ms_p_report; 114 | size_t _len_p_report = sizeof(sgx_report_t); 115 | sgx_report_t* _in_p_report = NULL; 116 | uint32_t _in_retval; 117 | 118 | if (sizeof(*_tmp_nonce) != 0 && 119 | (size_t)_tmp_nonce_size > (SIZE_MAX / sizeof(*_tmp_nonce))) { 120 | return SGX_ERROR_INVALID_PARAMETER; 121 | } 122 | 123 | CHECK_UNIQUE_POINTER(_tmp_p_qe3_target, _len_p_qe3_target); 124 | CHECK_UNIQUE_POINTER(_tmp_nonce, _len_nonce); 125 | CHECK_UNIQUE_POINTER(_tmp_p_report, _len_p_report); 126 | 127 | // 128 | // fence after pointer checks 129 | // 130 | sgx_lfence(); 131 | 132 | if (_tmp_p_qe3_target != NULL && _len_p_qe3_target != 0) { 133 | _in_p_qe3_target = (sgx_target_info_t*)malloc(_len_p_qe3_target); 134 | if (_in_p_qe3_target == NULL) { 135 | status = SGX_ERROR_OUT_OF_MEMORY; 136 | goto err; 137 | } 138 | 139 | if (memcpy_s(_in_p_qe3_target, _len_p_qe3_target, _tmp_p_qe3_target, _len_p_qe3_target)) { 140 | status = SGX_ERROR_UNEXPECTED; 141 | goto err; 142 | } 143 | 144 | } 145 | if (_tmp_nonce != NULL && _len_nonce != 0) { 146 | if ( _len_nonce % sizeof(*_tmp_nonce) != 0) 147 | { 148 | status = SGX_ERROR_INVALID_PARAMETER; 149 | goto err; 150 | } 151 | _in_nonce = (uint8_t*)malloc(_len_nonce); 152 | if (_in_nonce == NULL) { 153 | status = SGX_ERROR_OUT_OF_MEMORY; 154 | goto err; 155 | } 156 | 157 | if (memcpy_s(_in_nonce, _len_nonce, _tmp_nonce, _len_nonce)) { 158 | status = SGX_ERROR_UNEXPECTED; 159 | goto err; 160 | } 161 | 162 | } 163 | if (_tmp_p_report != NULL && _len_p_report != 0) { 164 | if ((_in_p_report = (sgx_report_t*)malloc(_len_p_report)) == NULL) { 165 | status = SGX_ERROR_OUT_OF_MEMORY; 166 | goto err; 167 | } 168 | 169 | memset((void*)_in_p_report, 0, _len_p_report); 170 | } 171 | _in_retval = enclave_create_report((const sgx_target_info_t*)_in_p_qe3_target, _in_nonce, _tmp_nonce_size, _in_p_report); 172 | if (memcpy_verw_s(&ms->ms_retval, sizeof(ms->ms_retval), &_in_retval, sizeof(_in_retval))) { 173 | status = SGX_ERROR_UNEXPECTED; 174 | goto err; 175 | } 176 | if (_in_p_report) { 177 | if (memcpy_verw_s(_tmp_p_report, _len_p_report, _in_p_report, _len_p_report)) { 178 | status = SGX_ERROR_UNEXPECTED; 179 | goto err; 180 | } 181 | } 182 | 183 | err: 184 | if (_in_p_qe3_target) free(_in_p_qe3_target); 185 | if (_in_nonce) free(_in_nonce); 186 | if (_in_p_report) free(_in_p_report); 187 | return status; 188 | } 189 | 190 | SGX_EXTERNC const struct { 191 | size_t nr_ecall; 192 | struct {void* ecall_addr; uint8_t is_priv; uint8_t is_switchless;} ecall_table[2]; 193 | } g_ecall_table = { 194 | 2, 195 | { 196 | {(void*)(uintptr_t)sgx_enclave_create_pubkey, 0, 0}, 197 | {(void*)(uintptr_t)sgx_enclave_create_report, 0, 0}, 198 | } 199 | }; 200 | 201 | SGX_EXTERNC const struct { 202 | size_t nr_ocall; 203 | } g_dyn_entry_table = { 204 | 0, 205 | }; 206 | 207 | 208 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCLAVE_T_H__ 2 | #define ENCLAVE_T_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "sgx_edger8r.h" /* for sgx_ocall etc. */ 8 | 9 | #include "sgx_report.h" 10 | #include "sgx_tcrypto.h" 11 | 12 | #include /* for size_t */ 13 | 14 | #define SGX_CAST(type, item) ((type)(item)) 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | sgx_status_t enclave_create_pubkey(rsa_params_t* key); 21 | uint32_t enclave_create_report(const sgx_target_info_t* p_qe3_target, uint8_t* nonce, uint32_t nonce_size, sgx_report_t* p_report); 22 | 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif /* __cplusplus */ 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.o -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.c: -------------------------------------------------------------------------------- 1 | #include "Enclave_u.h" 2 | #include 3 | 4 | typedef struct ms_enclave_create_pubkey_t { 5 | sgx_status_t ms_retval; 6 | rsa_params_t* ms_key; 7 | } ms_enclave_create_pubkey_t; 8 | 9 | typedef struct ms_enclave_create_report_t { 10 | uint32_t ms_retval; 11 | const sgx_target_info_t* ms_p_qe3_target; 12 | uint8_t* ms_nonce; 13 | uint32_t ms_nonce_size; 14 | sgx_report_t* ms_p_report; 15 | } ms_enclave_create_report_t; 16 | 17 | static const struct { 18 | size_t nr_ocall; 19 | void * table[1]; 20 | } ocall_table_Enclave = { 21 | 0, 22 | { NULL }, 23 | }; 24 | sgx_status_t enclave_create_pubkey(sgx_enclave_id_t eid, sgx_status_t* retval, rsa_params_t* key) 25 | { 26 | sgx_status_t status; 27 | ms_enclave_create_pubkey_t ms; 28 | ms.ms_key = key; 29 | status = sgx_ecall(eid, 0, &ocall_table_Enclave, &ms); 30 | if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval; 31 | return status; 32 | } 33 | 34 | sgx_status_t enclave_create_report(sgx_enclave_id_t eid, uint32_t* retval, const sgx_target_info_t* p_qe3_target, uint8_t* nonce, uint32_t nonce_size, sgx_report_t* p_report) 35 | { 36 | sgx_status_t status; 37 | ms_enclave_create_report_t ms; 38 | ms.ms_p_qe3_target = p_qe3_target; 39 | ms.ms_nonce = nonce; 40 | ms.ms_nonce_size = nonce_size; 41 | ms.ms_p_report = p_report; 42 | status = sgx_ecall(eid, 1, &ocall_table_Enclave, &ms); 43 | if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval; 44 | return status; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCLAVE_U_H__ 2 | #define ENCLAVE_U_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "sgx_edger8r.h" /* for sgx_status_t etc. */ 9 | 10 | #include "sgx_report.h" 11 | #include "sgx_tcrypto.h" 12 | 13 | #include /* for size_t */ 14 | 15 | #define SGX_CAST(type, item) ((type)(item)) 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | 22 | sgx_status_t enclave_create_pubkey(sgx_enclave_id_t eid, sgx_status_t* retval, rsa_params_t* key); 23 | sgx_status_t enclave_create_report(sgx_enclave_id_t eid, uint32_t* retval, const sgx_target_info_t* p_qe3_target, uint8_t* nonce, uint32_t nonce_size, sgx_report_t* p_report); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif /* __cplusplus */ 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.o -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Copyright (C) 2023 Intel Corporation. All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions 7 | # are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in 13 | # the documentation and/or other materials provided with the 14 | # distribution. 15 | # * Neither the name of Intel Corporation nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # 32 | 33 | ######## SGX SDK Settings ######## 34 | SGX_DEBUG ?= 0 35 | 36 | SGX_SDK ?= /opt/intel/sgxsdk 37 | SGX_MODE ?= HW 38 | SGX_ARCH ?= x64 39 | OUT_OF_PROC ?= 0 40 | 41 | SGX_COMMON_CFLAGS := -m64 42 | SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 43 | SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign 44 | SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r 45 | 46 | ifeq ($(SGX_DEBUG), 1) 47 | SGX_COMMON_CFLAGS += -O0 -g 48 | else 49 | SGX_COMMON_CFLAGS += -O2 50 | endif 51 | 52 | ######## Enclave Settings ######## 53 | Trts_Library_Name := sgx_trts 54 | Service_Library_Name := sgx_tservice 55 | Crypto_Library_Name := sgx_tcrypto 56 | 57 | Enclave_Cpp_Files := Enclave.cpp 58 | Enclave_Include_Paths := -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/libcxx 59 | 60 | CC_BELOW_4_9 := $(shell expr "`$(CC) -dumpversion`" \< "4.9") 61 | ifeq ($(CC_BELOW_4_9), 1) 62 | Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -ffunction-sections -fdata-sections -fstack-protector 63 | else 64 | Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -ffunction-sections -fdata-sections -fstack-protector-strong 65 | endif 66 | Enclave_C_Flags += $(Enclave_Include_Paths) 67 | Enclave_Cpp_Flags := $(Enclave_C_Flags) -std=c++11 -nostdinc++ 68 | 69 | App_Library := libutils.so 70 | 71 | App_C_Flags := $(SGX_COMMON_CFLAGS) $(Enclave_Include_Paths) -fPIC -Wno-attributes $(App_Include_Paths) 72 | App_Cpp_Flags := $(App_C_Flags) -std=c++11 73 | App_Library_Link_Flags := $(SGX_COMMON_CFLAGS) -lsgx_dcap_ql -lsgx_urts -L${SGXSSL_LIB_PATH} -lsgx_usgxssl -shared 74 | 75 | App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o) 76 | App_Library_Link_Flags := $(SGX_COMMON_CFLAGS) -lsgx_dcap_ql -lsgx_urts -L${SGXSSL_LIB_PATH} -lsgx_usgxssl -shared 77 | 78 | # To generate a proper enclave, it is recommended to follow below guideline to link the trusted libraries: 79 | # 1. Link sgx_trts with the `--whole-archive' and `--no-whole-archive' options, 80 | # so that the whole content of trts is included in the enclave. 81 | # 2. For other libraries, you just need to pull the required symbols. 82 | # Use `--start-group' and `--end-group' to link these libraries. 83 | # Do NOT move the libraries linked with `--start-group' and `--end-group' within `--whole-archive' and `--no-whole-archive' options. 84 | # Otherwise, you may get some undesirable errors. 85 | Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ 86 | -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ 87 | -Wl,--start-group -lsgx_tstdc -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \ 88 | -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ 89 | -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ 90 | -Wl,--defsym,__ImageBase=0 -Wl,--gc-sections \ 91 | -Wl,-z,relro,-z,now,-z,noexecstack \ 92 | -Wl,--version-script=Enclave.lds 93 | 94 | Enclave_Cpp_Objects := $(Enclave_Cpp_Files:.cpp=.o)# utils.o 95 | ENCLAVE_LIBRARY_PATH := ./ 96 | 97 | Enclave_Name := enclave.so 98 | Signed_Enclave_Name := enclave.signed.so 99 | Enclave_Config_File := Enclave.config.xml 100 | 101 | ifeq ($(SGX_DEBUG), 1) 102 | Build_Mode = HW_DEBUG 103 | else 104 | Build_Mode = HW_RELEASE 105 | endif 106 | 107 | .PHONY: all target run 108 | 109 | all: .config_$(Build_Mode)_$(SGX_ARCH) 110 | $(MAKE) target 111 | 112 | target: $(App_Library) $(Signed_Enclave_Name) 113 | ifeq ($(Build_Mode), HW_RELEASE) 114 | @echo "The project has been built in release hardware mode." 115 | else 116 | @echo "The project has been built in debug hardware mode." 117 | endif 118 | 119 | run: all 120 | ifneq ($(Build_Mode), HW_RELEASE) 121 | @$(CURDIR)/$(App_Name) 122 | @echo "RUN => $(App_Name) [$(SGX_MODE)|$(SGX_ARCH), OK]" 123 | endif 124 | 125 | .config_$(Build_Mode)_$(SGX_ARCH): 126 | @rm -f .config_* $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave_t.* 127 | @touch .config_$(Build_Mode)_$(SGX_ARCH) 128 | 129 | ######## App Objects ######## 130 | 131 | Enclave_u.o: Enclave_u.c 132 | @$(CC) $(App_C_Flags) -c $< -o $@ 133 | @echo "CC <= $<" 134 | 135 | utils.o: utils.cpp Enclave_u.c 136 | @$(CXX) $(App_Cpp_Flags) -c $< -o $@ 137 | @echo "CXX <= $<" 138 | 139 | $(App_Library): utils.o Enclave_u.o 140 | @$(CXX) $(App_Cpp_Flags) $^ -o $@ $(App_Library_Link_Flags) 141 | @echo "LINK => $@" 142 | 143 | ######## Enclave Objects ######## 144 | Enclave_t.c: 145 | @$(SGX_EDGER8R) --trusted Enclave.edl 146 | @echo "GEN => $@" 147 | 148 | Enclave_u.c: 149 | @$(SGX_EDGER8R) --untrusted Enclave.edl 150 | @echo "GEN => $@" 151 | 152 | Enclave_t.o: Enclave_t.c Enclave_u.c 153 | @$(CC) $(Enclave_C_Flags) -c $< -o $@ 154 | @echo "CC <= $<" 155 | 156 | $(Enclave_Cpp_Objects): Enclave_t.c 157 | %.o: %.cpp 158 | @$(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ 159 | @echo "CXX <= $<" 160 | 161 | $(Enclave_Name): Enclave_t.o $(Enclave_Cpp_Objects) 162 | @$(CXX) $^ -o $@ $(Enclave_Link_Flags) 163 | @echo "LINK => $@" 164 | 165 | $(Signed_Enclave_Name): $(Enclave_Name) 166 | @$(SGX_ENCLAVE_SIGNER) sign -key Enclave_private_sample.pem -enclave $(Enclave_Name) -out $@ -config $(Enclave_Config_File) 167 | @rm -f $(Enclave_Name) 168 | @echo "SIGN => $@" 169 | 170 | .PHONY: clean 171 | clean: 172 | @rm -f $(App_Library) *.o 173 | @rm -f $(Enclave_Name) $(Signed_Enclave_Name) $(Enclave_Cpp_Objects) Enclave_t.* Enclave_u.* 174 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/README.md: -------------------------------------------------------------------------------- 1 | Based from SGXDataPrimitives/SampleCode/QuoteGenerationSample -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/enclave.signed.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/enclave.signed.so -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/libutils.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/libutils.so -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Intel Corporation 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #include "Enclave_u.h" 7 | #include "sgx_tcrypto.h" 8 | #include "utils.h" 9 | 10 | int get_public_key(sgx_enclave_id_t eid, uint8_t **pp_key, uint32_t *p_key_size) 11 | { 12 | sgx_status_t retval; 13 | sgx_status_t sgx_status = SGX_SUCCESS; 14 | rsa_params_t rsa_key; 15 | 16 | sgx_status = enclave_create_pubkey(eid, &retval, &rsa_key); 17 | if ((SGX_SUCCESS != sgx_status) || (0 != retval)) { 18 | return -1; 19 | } 20 | 21 | // Public key format : 22 | *pp_key = (uint8_t*)malloc(N_SIZE_IN_BYTES + E_SIZE_IN_BYTES); 23 | if (NULL == *pp_key) { 24 | return -1; 25 | } 26 | memcpy(*pp_key, ((const char *)rsa_key.e), E_SIZE_IN_BYTES); 27 | memcpy(*pp_key + E_SIZE_IN_BYTES, ((const char *)rsa_key.n), N_SIZE_IN_BYTES); 28 | 29 | *p_key_size = E_SIZE_IN_BYTES + N_SIZE_IN_BYTES; 30 | 31 | return 0; 32 | } 33 | 34 | void free_public_key(uint8_t *p_key) 35 | { 36 | free(p_key); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Intel Corporation 3 | * SPDX-License-Identifier: BSD-3-Clause 4 | */ 5 | 6 | #ifndef _UTILS_H_ 7 | #define _UTILS_H_ 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | int get_public_key(sgx_enclave_id_t eid, uint8_t **pp_key, uint32_t *p_key_size); 14 | void free_public_key(uint8_t *p_key); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif 19 | 20 | #endif /*_UTILS_H_*/ 21 | 22 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/utils.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/trustauthority-client-for-python/7e54bc7f2366165fb711b18963236b0e21d7dc36/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/utils.o -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/sgx_sample_app/sgx_sample_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2023-2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import ctypes 8 | import json 9 | import os 10 | from inteltrustauthorityclient.connector import config, connector 11 | from inteltrustauthorityclient.resources import logger as logger 12 | from inteltrustauthorityclient.resources import constants as const 13 | from inteltrustauthorityclient.sgx.intel.sgx_adapter import SGXAdapter 14 | 15 | import logging as log 16 | 17 | 18 | def create_sgx_enclave(enclave_path): 19 | """Create SGX Enclave using SGX Dcap Libraries""" 20 | c_lib = ctypes.CDLL("libsgx_urts.so") 21 | 22 | class sgx_enclave_id_t(ctypes.Structure): 23 | _fields_ = [("handle", ctypes.c_void_p)] 24 | 25 | class sgx_launch_token_t(ctypes.c_uint8 * 1024): 26 | pass 27 | 28 | c_lib.sgx_create_enclave.argtypes = [ 29 | ctypes.c_char_p, 30 | ctypes.c_int, 31 | ctypes.POINTER(sgx_launch_token_t), 32 | ctypes.POINTER(ctypes.c_int), 33 | ctypes.POINTER(sgx_enclave_id_t), 34 | ctypes.c_void_p, 35 | ] 36 | c_lib.restype = ctypes.c_int 37 | launch_token = sgx_launch_token_t() 38 | enclave_id = sgx_enclave_id_t() 39 | token_updated = ctypes.c_int(0) 40 | status = c_lib.sgx_create_enclave( 41 | enclave_path.encode(), 42 | 0, 43 | ctypes.byref(launch_token), 44 | ctypes.byref(token_updated), 45 | ctypes.byref(enclave_id), 46 | None, 47 | ) 48 | if status != 0: 49 | log.error(f"Error creating enclave. SGX error code: {hex(status)}") 50 | exit(1) 51 | 52 | return enclave_id 53 | 54 | 55 | def loadPublicKey(eid): 56 | """Fetch the public key to be passed to SgxAdapter""" 57 | c_lib = ctypes.CDLL("./minimal-enclave/libutils.so") 58 | c_lib.argtypes = [ 59 | ctypes.c_long, 60 | ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)), 61 | ctypes.POINTER(ctypes.c_uint32), 62 | ] 63 | c_lib.restype = ctypes.c_int 64 | key_buf = ctypes.POINTER(ctypes.c_uint8)() 65 | key_size = ctypes.c_uint32() 66 | status = c_lib.get_public_key(eid, ctypes.byref(key_buf), ctypes.byref(key_size)) 67 | if status != 0: 68 | print(f"Error creating public key. SGX error code: {hex(status)}") 69 | exit(1) 70 | public_key = ctypes.cast( 71 | key_buf, ctypes.POINTER(ctypes.c_uint8 * key_size.value) 72 | ).contents 73 | return bytearray(public_key) 74 | 75 | 76 | def main(): 77 | # Set logging 78 | try: 79 | logger.setup_logging() 80 | except ValueError as e: 81 | log.exception(f"Exception while setting up log : {type(e).__name__}: {e}") 82 | exit(1) 83 | 84 | # get all the environment variables 85 | trustauthority_base_url = os.getenv("TRUSTAUTHORITY_BASE_URL") 86 | if trustauthority_base_url is None or trustauthority_base_url == "": 87 | log.error("TRUSTAUTHORITY_BASE_URL is not set.") 88 | exit(1) 89 | 90 | trustAuthority_api_url = os.getenv("TRUSTAUTHORITY_API_URL") 91 | if trustAuthority_api_url is None or trustAuthority_api_url == "": 92 | log.error("TRUSTAUTHORITY_API_URL is not set.") 93 | exit(1) 94 | 95 | trust_authority_api_key = os.getenv("TRUSTAUTHORITY_API_KEY") 96 | if trust_authority_api_key is None or trust_authority_api_key == "": 97 | log.error("TRUSTAUTHORITY_API_KEY is not set.") 98 | exit(1) 99 | 100 | trust_authority_request_id = os.getenv("TRUSTAUTHORITY_REQUEST_ID") 101 | if trust_authority_request_id is not None: 102 | if not config.validate_requestid(trust_authority_request_id): 103 | log.error(f"Invalid Request ID :{trust_authority_request_id}") 104 | exit(1) 105 | 106 | trust_authority_policy_id = os.getenv("TRUSTAUTHORITY_POLICY_ID") 107 | if trust_authority_policy_id != None: 108 | policy_ids = json.loads(trust_authority_policy_id) 109 | if len(policy_ids) > const.POLICY_IDS_MAX_LEN: 110 | log.error("policy count in request must be between 1 - 10") 111 | exit(1) 112 | for uuid_str in policy_ids: 113 | if not config.validate_uuid(uuid_str): 114 | log.error(f"Invalid policy UUID :{uuid_str}") 115 | exit(1) 116 | 117 | retry_max = os.getenv("RETRY_MAX") 118 | if retry_max is None: 119 | log.debug("RETRY_MAX is not provided. Hence, setting default value.") 120 | retry_max = const.DEFAULT_RETRY_MAX_NUM 121 | else: 122 | if not retry_max.isnumeric(): 123 | log.error("Invalid RETRY_MAX format: RETRY_MAX must be an Integer.") 124 | exit(1) 125 | 126 | retry_wait_time_min = os.getenv("RETRY_WAIT_TIME_MIN") 127 | if retry_wait_time_min is None: 128 | log.debug("RETRY_WAIT_TIME_MIN is not provided. Hence, setting default value.") 129 | retry_wait_time_min = const.DEFAULT_RETRY_WAIT_MIN_SEC 130 | else: 131 | if not retry_wait_time_min.isnumeric(): 132 | log.error( 133 | "Invalid RETRY_WAIT_TIME_MIN format: RETRY_WAIT_TIME_MIN must be an Integer." 134 | ) 135 | exit(1) 136 | 137 | retry_wait_time_max = os.getenv("RETRY_WAIT_TIME_MAX") 138 | if retry_wait_time_max is None: 139 | log.debug("RETRY_WAIT_TIME_MAX is not provided. Hence, setting default value.") 140 | retry_wait_time_max = const.DEFAULT_RETRY_WAIT_MAX_SEC 141 | else: 142 | if not retry_wait_time_max.isnumeric(): 143 | log.error( 144 | "Invalid RETRY_WAIT_TIME_MAX format: RETRY_WAIT_TIME_MAX must be an Integer." 145 | ) 146 | exit(1) 147 | 148 | timeout_second = os.getenv("CLIENT_TIMEOUT_SEC") 149 | if timeout_second is None: 150 | log.debug( 151 | "CLIENT_TIMEOUT_SEC is not provided. Hence, setting to default value." 152 | ) 153 | timeout_second = const.DEFAULT_CLIENT_TIMEOUT_SEC 154 | 155 | token_signing_algorithm = os.getenv("TOKEN_SIGNING_ALGORITHM") 156 | policy_must_match = os.getenv("POLICY_MUST_MATCH") 157 | if policy_must_match is not None and policy_must_match.lower() in {"true", "false"}: 158 | policy_must_match = True if policy_must_match.lower() == "true" else False 159 | # enclave related work 160 | enclave_path = "./minimal-enclave/enclave.signed.so" 161 | eid = create_sgx_enclave(enclave_path) 162 | pub_bytes = loadPublicKey(eid) 163 | try: 164 | # Populate config object 165 | config_obj = config.Config( 166 | config.RetryConfig( 167 | int(retry_wait_time_min), 168 | int(retry_wait_time_max), 169 | int(retry_max), 170 | int(timeout_second), 171 | ), 172 | trustauthority_base_url, 173 | trustAuthority_api_url, 174 | trust_authority_api_key, 175 | ) 176 | except ValueError as exc: 177 | log.error(f"Value Error in config object creation : {exc}") 178 | exit(1) 179 | 180 | ita_connector = connector.ITAConnector(config_obj) 181 | adapter_type = os.getenv("ADAPTER_TYPE") 182 | if adapter_type is None: 183 | log.error("ADAPTER_TYPE is not set.") 184 | exit(1) 185 | c_lib = ctypes.CDLL("./minimal-enclave/libutils.so") 186 | adapter = SGXAdapter(eid, c_lib.enclave_create_report, pub_bytes) 187 | if trust_authority_policy_id != None: 188 | attest_args = connector.AttestArgs( 189 | adapter, 190 | token_signing_algorithm, 191 | policy_must_match, 192 | trust_authority_request_id, 193 | policy_ids, 194 | ) 195 | else: 196 | attest_args = connector.AttestArgs( 197 | adapter, 198 | token_signing_algorithm, 199 | policy_must_match, 200 | trust_authority_request_id, 201 | ) 202 | # Fetch Attestation Token from ITA 203 | attestation_token = ita_connector.attest(attest_args) 204 | if attestation_token is None: 205 | log.error("Attestation Token is not returned.") 206 | exit(1) 207 | token = attestation_token.token 208 | log.info(f"Attestation token : {token}") 209 | token_headers_json = json.loads(attestation_token.headers.replace("'", '"')) 210 | log.info( 211 | "Request id and Trace id are: %s, %s", 212 | token_headers_json.get("request-id"), 213 | token_headers_json.get("trace-id"), 214 | ) 215 | # verify token- recieved from connector 216 | try: 217 | verified_token = ita_connector.verify_token(token) 218 | except Exception as exc: 219 | log.error(f"Token verification returned exception : {exc}") 220 | if verified_token is not None: 221 | log.info("Token Verification Successful") 222 | log.info(f"Verified Attestation Token : {verified_token}") 223 | else: 224 | log.info("Token Verification failed") 225 | 226 | 227 | # main for function call. 228 | if __name__ == "__main__": 229 | main() 230 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/tdx_sample_app/Dockerfile: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Copyright (c) 2023-2024 Intel Corporation 3 | # All rights reserved. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | ########################################### 6 | 7 | ARG UBUNTU_VERSION 8 | FROM ubuntu:$UBUNTU_VERSION as build_base 9 | 10 | RUN apt-get update && \ 11 | apt-get upgrade -y && \ 12 | apt-get autoremove -y && \ 13 | apt-get install -y --no-install-recommends pip 14 | 15 | # Set the working directory inside the container 16 | WORKDIR /app 17 | COPY pyproject.toml poetry.lock ./ 18 | ENV PYTHONPATH=/app 19 | # Install poetry 20 | RUN pip3 install --no-cache-dir poetry 21 | COPY . . 22 | RUN poetry build 23 | 24 | FROM ubuntu:$UBUNTU_VERSION 25 | 26 | # Set environment variables 27 | ENV DEBIAN_FRONTEND=noninteractive \ 28 | TZ=UTC \ 29 | PYTHONDONTWRITEBYTECODE=1 \ 30 | PYTHONUNBUFFERED=1 31 | 32 | # Install system dependencies 33 | RUN apt-get update; \ 34 | apt-get upgrade -y; \ 35 | apt-get autoremove -y; \ 36 | apt-get install -y --no-install-recommends gnupg curl ca-certificates 37 | 38 | #Adding sgx repo to ubuntu focals 39 | ARG ADAPTER_TYPE 40 | ENV ADAPTER_TYPE=${ADAPTER_TYPE} 41 | 42 | RUN apt-get update && apt-get install -y --no-install-recommends build-essential python3 pip 43 | 44 | RUN if [ "${ADAPTER_TYPE}" = "aztdx" ]; then \ 45 | apt-get install -y --no-install-recommends tpm2-tools; \ 46 | fi 47 | 48 | COPY --from=build_base /app/dist/applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl . 49 | RUN pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl 50 | 51 | # Set the working directory inside the container 52 | WORKDIR /app 53 | COPY . . 54 | WORKDIR /app/inteltrustauthorityclient/examples/tdx_sample_app 55 | CMD ["python3", "tdx_sample_app.py"] 56 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/tdx_sample_app/README.md: -------------------------------------------------------------------------------- 1 | # Intel TDX Attestation Sample Application 2 | 3 |

· 09/28/2024 ·

4 | 5 | The Intel® Trust Domain Extensions (Intel® TDX) attestation sample app is a Python application that uses the Intel® Trust Authority Client for Python packages to attest an Intel TDX trust domain. The attestation verifier is [Intel® Trust Authority](https://trustauthority.intel.com). 6 | 7 | The sample can be run as a Docker container or as a native application on an Intel TDX Trust Domain (TD) VM. Supported platforms are Intel TDX ("INTEL-TDX"), Azure confidential VMs with TDX ("AZURE-TDX"), and Google Cloud Platform confidential VMs with TDX ("GCP-TDX"). When the sample is run, it does the following: 8 | 9 | 1. Evokes the Python client to connect to the Intel Trust Authority service. 10 | 1. The client calls the TEE adapter to collect evidence for a quote. 11 | 1. The client sends the quote to the Intel Trust Authority service for attestation. 12 | 1. If attestation is successful, the sample app prints the JWT and other information to the terminal. A real application would most likely use the attestation token to authenticate with a service or to authorize access to a resource. 13 | 14 | 15 | The following diagram depicts the components of the sample application. 16 | 17 | ``` 18 | ┌────────────────────────────────────────────────┐ 19 | │ ┌──────────────────────────────────────┐ │ 20 | │ │ Docker Container │ │ 21 | │ │ │ │ 22 | │ │ ┌──────────────────────────┐ │ │ 23 | │ │ │Intel TDX attestation app │ │ │ ┌────────────────┐ 24 | │ │ └──────────────────────────┘ │ │ │ │ 25 | │ │ │ │ │ │ 26 | | │ ┌────────────────────────────┐ │◄───┼───────────────►│ INTEL TRUST │ 27 | │ │ │applications_security_amber | | | | AUTHORITY | 28 | | | | _trustauthority_client_ | | | | SERVICE | 29 | | | | for_python-1.1.0-py3-none | | | | | 30 | | | | -any.whl | | | | | 31 | | │ | │ │ │ | │ 32 | │ │ └────────────────────────────┘ │ │ │ │ 33 | │ │ │ │ └────────────────┘ 34 | │ │ | | 35 | │ │ │ │ 36 | │ └──────────────────────────────────────┘ │ 37 | │ │ 38 | │ Intel TDX VM │ 39 | └────────────────────────────────────────────────┘ 40 | ``` 41 | 42 | ## How to build and run the application 43 | 44 | The sample app can be run as a [Docker container](#run-the-application-in-a-docker-container) or as a [native application on an Intel TDX TD VM](#run-the-sample-application-from-a-td-vm ). In either case, you must set certain runtime variables before running the sample app. 45 | 46 | ### Runtime variables 47 | 48 | The Python client and REST API require configuration before use. The following runtime variables correspond to client configuration, API endpoints, and authorization settings in the client API. For the Docker container, you will set these variables in the `sgx_token.env` file. For the native application, you'll export a set of variables in the shell environment. The following table describes the runtime variables and their purpose. 49 | 50 | Many of these variables are optional but several are required as indicated. 51 | 52 | | Variable | Type | Required? | Description | 53 | | :--- | :--- | :--- | :--- | 54 | | `HTTP_PROXY` | String | No | HTTP proxy host. [1] | 55 | | `HTTPS_PROXY` | String | No | HTTPS proxy host. [1]| 56 | | `TRUSTAUTHORITY_BASE_URL` | String | Yes | Base URL for the Intel Trust Authority service. [2]| 57 | | `TRUSTAUTHORITY_API_URL` | String | Yes | API URL for the Intel Trust Authority service. [2] | 58 | | `TRUSTAUTHORITY_API_KEY` | String | Yes | Attestation API key required for authorization. [3] For more information, see [User roles and API keys](https://docs.trustauthority.intel.com/main/articles/concept-user-roles-and-api-keys.html) in the Intel Trust Authority Documentation. | 59 | | `TRUSTAUTHORITY_REQUEST_ID` | String | No | If not provided, a request ID will be created by the API gateway. An auto-generated request ID is not guaranteed to be unique.| 60 | | `TRUSTAUTHORITY_POLICY_ID` | String | No | If supplied, the value can be a single policy ID (UUID) or a list of policy IDs separated by commas. For more information, see [Attestation Policies](https://docs.trustauthority.intel.com/main/articles/concept-policy-v2.html) in the Intel Trust Authority documentation.| 61 | | `RETRY_MAX` | Integer | No | Maximum number of retries. | 62 | | `RETRY_WAIT_TIME_MAX` | Integer | No | Maximum retry wait time. | 63 | | `RETRY_WAIT_TIME_MIN` | Integer | No | Minimum retry wait time. | 64 | | `CLIENT_TIMEOUT_SEC` | Integer | No | Request timeout in seconds. | 65 | | `LOG_LEVEL` | String | No | Log level. | 66 | | `POLICY_MUST_MATCH` | Boolean | No | If set to `true`, the policy (or policies, if more than one) specified by TRUSTAUTHORITY_POLICY_ID or the API key must all match for an attestation token to be issued. If not supplied or set to `false`, the default behavior is to issue an attestation token even if one or more policies fail to match. | 67 | | `TOKEN_SIGNING_ALGORITHM` | String | No | It specifies the algorithm to be used for signing the token. If supplied, it must be one of **PS384** or **RS256**. Defaults to PS384. | 68 | 69 | - Use docker compose version 1.29.2 or a more recent release. Follow the steps outlined at https://docs.docker.com/compose/install/linux/#install-the-plugin-manually for installing docker compose. 70 | 71 | **Notes**
72 | **[1]** The `HTTP_PROXY` and `HTTPS_PROXY` variables are optional. If your system is behind a proxy, set these variables to the proxy host. If you are not behind a proxy, you can leave these variables unset.
73 | **[2]** The `TRUSTAUTHORITY_BASE_URL` and `TRUSTAUTHORITY_API_URL` URLs are determined by the region associated with your subscription. If you're using the **US** region, the URLS are `https://portal.trustauthority.intel.com` and `https://api.trustauthority.intel.com` respectively. If you're using the **EU** region, the URLs are `https://portal.eu.trustauthority.intel.com` and `https://api.eu.trustauthority.intel.com` respectively.
**[3]** The API key must be created in the same region as the URLs above. An API key that was created in one region won't work in the other region. 74 | 75 | ### Run the application in a Docker container 76 | 77 | The Intel TDX attestation sample can be encapsulated as a container, enabling it to be executed in containerized environments. Begin by copying the Intel Trust Authority Client for Python repository to a local folder. Then build the Docker image, create the container, and run the sample app. 78 | 79 | #### Prerequisites 80 | 81 | - Use Docker version 20.10.17 or a more recent release. Refer to the guide at https://docs.docker.com/engine/install/ubuntu/ for detailed instructions on Docker installation. 82 | - Use docker-compose version 1.29.2 or a more recent release. Follow the steps outlined at https://docs.docker.com/compose/install/linux/#install-the-plugin-manually for installing docker-compose. 83 | - A Intel TDX host with the Linux Kernel 6.7 or newer to collect quotes from the configsys/TSM subsystem. 84 | 85 | #### Build Instructions 86 | 87 | 1. Build the sample application Docker image in **/inteltrustauthorityclient/examples/tdx_sample_app/** with the following command: 88 | 89 | ```sh 90 | cat < 99 | TRUST_AUTHORITY_CLIENT_VERSION — The version of the sample app Docker image. This version number is used to tag the Docker image.
100 | 101 | 2. The docker image must be present inside the TD vm. For example, it can be exported or copied from a build machine as follows. 102 | 103 | ```sh 104 | #Save the tdx sample app Docker image into trust_authority_python_client_tdx_sample_app.tar.gz 105 | docker save trust_authority_python_client_tdx_sample_app:v1.1.0 > trust_authority_python_client_tdx_sample_app.tar.gz 106 | #scp trust_authority_python_client_tdx_sample_app.tar.gz to the TD VM. 107 | #On the TD VM load/import trust_authority_python_client_tdx_sample_app.tar.gz docker image using below command 108 | docker load -i trust_authority_python_client_tdx_sample_app.tar.gz 109 | ``` 110 | 111 | 3. Once the image is built using the above `docker-compose build` command or loaded from the tar file, the `TDX Attestation Sample App` image can be run using the following commands. Substitute the correct values for the placeholders in the `tdx_token.env` file, and remove the variables that are not required. 112 | 113 | ```sh 114 | # Creating tdx_token.env file 115 | cat < 117 | HTTPS_PROXY= 118 | TRUSTAUTHORITY_BASE_URL= 119 | TRUSTAUTHORITY_API_URL= 120 | TRUSTAUTHORITY_API_KEY= 121 | TRUSTAUTHORITY_REQUEST_ID= 122 | TRUSTAUTHORITY_POLICY_ID= 123 | RETRY_MAX= 124 | RETRY_WAIT_TIME_MAX= 125 | RETRY_WAIT_TIME_MIN= 126 | CLIENT_TIMEOUT_SEC= 127 | LOG_LEVEL= 128 | POLICY_MUST_MATCH=True/False 129 | TOKEN_SIGNING_ALGORITHM= 130 | EOF 131 | ``` 132 | 133 | #### Use docker to run the Intel TDX Sample App 134 | 135 | For Azure TDX: 136 | ```sh 137 | sudo docker run \ 138 | -it --rm --device=/dev/tpm0 \ 139 | --device=/dev/tpmrm0 \ 140 | --env-file tdx_token.env \ 141 | --group-add $(getent group tss | cut -d: -f3) \ 142 | trust_authority_python_client_tdx_sample_app:v1.1.0 143 | ``` 144 | For Google Cloud / Intel® Developer Cloud TDX adapters: 145 | ```sh 146 | docker run \ 147 | --rm \ 148 | --privileged \ 149 | --network host \ 150 | -v /sys/kernel/config:/sys/kernel/config \ 151 | --env-file tdx_token.env \ 152 | trust_authority_python_client_tdx_sample_app:v1.1.0 153 | ``` 154 | 155 | If the sample application runs successfully, the attestation token returned from Intel Trust Authority and other information will be displayed. 156 | 157 | ### Run the sample application from a TD VM 158 | 159 | #### Prerequisites 160 | 161 | - Python 3.8 or later 162 | - Poetry. Install **poetry** using the command `pip3 install --no-cache-dir poetry`. 163 | - An Intel TDX TD VM running on a local Intel TDX host or as a confidential VM in the cloud. 164 | - A subscription to Intel Trust Authority. If you don't have a subscription, you can find out how to get one at [Intel Trust Authority](https://trustauthority.intel.com). 165 | 166 | #### Build Instructions 167 | 168 | Begin by copying the Intel Trust Authority Client for Python repository to a local folder. Then build the Python wheel package containing connector and adapter packages, install the packages, export environment variables, and run the sample app. 169 | 170 | 1. Build the Python wheel package containing connector and adapter packages from **/applications.security.amber.trustauthority-client-for-python** folder containing **poetry** configuration files using the following command: 171 | 172 | ```sh 173 | cd /inteltrustauthorityclient && \ 174 | poetry shell && \ 175 | poetry build 176 | ``` 177 | 178 | 2. Go to the distribution folder where the whl package was created and install the package using the following command. 179 | 180 | ```python 181 | pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl 182 | ``` 183 | 184 | 3. Set environment variables. 185 | 186 | The following environment variables can be exported before running the sample app. Most of these variables are optional, but several are required. Substitute the correct values for the placeholders and remove the variables that are not required. See [Runtime variables](#runtime-variables) for details. 187 | 188 | ```sh 189 | export HTTP_PROXY= 190 | export HTTPS_PROXY= 191 | export TRUSTAUTHORITY_BASE_URL= 192 | export TRUSTAUTHORITY_API_URL= 193 | export TRUSTAUTHORITY_API_KEY= 194 | export TRUSTAUTHORITY_REQUEST_ID= 195 | export TRUSTAUTHORITY_POLICY_ID= 196 | export RETRY_MAX= 197 | export RETRY_WAIT_TIME_MAX= 198 | export RETRY_WAIT_TIME_MIN= 199 | export CLIENT_TIMEOUT_SEC= 200 | export LOG_LEVEL= 201 | export POLICY_MUST_MATCH=True/False 202 | export TOKEN_SIGNING_ALGORITHM= 203 | ``` 204 | 205 | 4. Run the Sample App in **/inteltrustauthorityclient/examples/tdx_sample_app/** after setting the environment variables using the following command: 206 | 207 | ```sh 208 | python tdx_sample_app.py 209 | ``` 210 | 211 | If successful, the token and other information will be displayed. 212 | 213 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/tdx_sample_app/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/tdx_sample_app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Copyright (c) 2023-2024 Intel Corporation 3 | # All rights reserved. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | ########################################### 6 | 7 | version: '3' 8 | services: 9 | trust_authority_python_client_tdx_sample_app: 10 | image: trust_authority_python_client_tdx_sample_app:${TRUST_AUTHORITY_CLIENT_VERSION} 11 | container_name: trust_authority_python_client_tdx_sample_app 12 | build: 13 | context: ../../.. 14 | dockerfile: inteltrustauthorityclient/examples/tdx_sample_app/Dockerfile 15 | args: 16 | - UBUNTU_VERSION=${UBUNTU_VERSION} 17 | - ADAPTER_TYPE=${ADAPTER_TYPE} 18 | env_file: 19 | - ../.env 20 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/examples/tdx_sample_app/tdx_sample_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import json 8 | import os 9 | import uuid 10 | import logging as log 11 | from inteltrustauthorityclient.resources import logger as logger 12 | from inteltrustauthorityclient.resources import constants as const 13 | from inteltrustauthorityclient.tdx.tdx_adapter import TDXAdapter 14 | from inteltrustauthorityclient.tdx.azure.azure_tdx_adapter import AzureTDXAdapter 15 | from inteltrustauthorityclient.connector import config, connector 16 | from inteltrustauthorityclient.base.evidence_adapter import EvidenceAdapter 17 | from inteltrustauthorityclient.connector.evidence import EvidenceType 18 | 19 | 20 | def main(): 21 | """Sample App to generate evidence, attest the platform and get the token from ITA.""" 22 | 23 | # Set logging 24 | try: 25 | logger.setup_logging() 26 | except ValueError as e: 27 | log.exception(f"Exception while setting up log : {type(e).__name__}: {e}") 28 | exit(1) 29 | 30 | # get all the environment variables 31 | trustauthority_base_url = os.getenv("TRUSTAUTHORITY_BASE_URL") 32 | if trustauthority_base_url is None or trustauthority_base_url == "": 33 | log.error("TRUSTAUTHORITY_BASE_URL is not set.") 34 | exit(1) 35 | 36 | trustAuthority_api_url = os.getenv("TRUSTAUTHORITY_API_URL") 37 | if trustAuthority_api_url is None or trustAuthority_api_url == "": 38 | log.error("TRUSTAUTHORITY_API_URL is not set.") 39 | exit(1) 40 | 41 | trust_authority_api_key = os.getenv("TRUSTAUTHORITY_API_KEY") 42 | if trust_authority_api_key is None or trust_authority_api_key == "": 43 | log.error("TRUSTAUTHORITY_API_KEY is not set.") 44 | exit(1) 45 | 46 | trust_authority_request_id = os.getenv("TRUSTAUTHORITY_REQUEST_ID") 47 | if trust_authority_request_id is not None: 48 | if not config.validate_requestid(trust_authority_request_id): 49 | log.error(f"Invalid Request ID :{trust_authority_request_id}") 50 | exit(1) 51 | 52 | trust_authority_policy_id = os.getenv("TRUSTAUTHORITY_POLICY_ID") 53 | if trust_authority_policy_id != None: 54 | policy_ids = json.loads(trust_authority_policy_id) 55 | if len(policy_ids) > const.POLICY_IDS_MAX_LEN: 56 | log.error("policy count in request must be between 1 - 10") 57 | exit(1) 58 | for uuid_str in policy_ids: 59 | if not config.validate_uuid(uuid_str): 60 | log.error(f"Invalid policy UUID :{uuid_str}") 61 | exit(1) 62 | 63 | retry_max = os.getenv("RETRY_MAX") 64 | if retry_max is None: 65 | log.debug("RETRY_MAX is not provided. Hence, setting default value.") 66 | retry_max = const.DEFAULT_RETRY_MAX_NUM 67 | else: 68 | if not retry_max.isnumeric(): 69 | log.error("Invalid RETRY_MAX format: RETRY_MAX must be an Integer.") 70 | exit(1) 71 | 72 | retry_wait_time_min = os.getenv("RETRY_WAIT_TIME_MIN") 73 | if retry_wait_time_min is None: 74 | log.debug("RETRY_WAIT_TIME is not provided. Hence, setting default value.") 75 | retry_wait_time_min = const.DEFAULT_RETRY_WAIT_MIN_SEC 76 | else: 77 | if not retry_wait_time_min.isnumeric(): 78 | log.error( 79 | "Invalid RETRY_WAIT_TIME_MIN format: RETRY_WAIT_TIME_MIN must be an Integer." 80 | ) 81 | exit(1) 82 | 83 | retry_wait_time_max = os.getenv("RETRY_WAIT_TIME_MAX") 84 | if retry_wait_time_max is None: 85 | log.debug("RETRY_WAIT_TIME_MAX is not provided. Hence, setting default value.") 86 | retry_wait_time_max = const.DEFAULT_RETRY_WAIT_MAX_SEC 87 | else: 88 | if not retry_wait_time_max.isnumeric(): 89 | log.error( 90 | "Invalid RETRY_WAIT_TIME_MAX format: RETRY_WAIT_TIME_MAX must be an Integer." 91 | ) 92 | exit(1) 93 | 94 | timeout_second = os.getenv("CLIENT_TIMEOUT_SEC") 95 | if timeout_second is None: 96 | log.debug( 97 | "CLIENT_TIMEOUT_SEC is not provided. Hence, setting to default value." 98 | ) 99 | timeout_second = const.DEFAULT_CLIENT_TIMEOUT_SEC 100 | 101 | token_signing_algorithm = os.getenv("TOKEN_SIGNING_ALGORITHM") 102 | policy_must_match = os.getenv("POLICY_MUST_MATCH") 103 | if policy_must_match is not None and policy_must_match.lower() in {"true", "false"}: 104 | policy_must_match = True if policy_must_match.lower() == "true" else False 105 | 106 | try: 107 | # Populate config object 108 | config_obj = config.Config( 109 | config.RetryConfig( 110 | int(retry_wait_time_min), 111 | int(retry_wait_time_max), 112 | int(retry_max), 113 | int(timeout_second), 114 | ), 115 | trustauthority_base_url, 116 | trustAuthority_api_url, 117 | trust_authority_api_key, 118 | ) 119 | except ValueError as exc: 120 | log.error(f"Value Error in config object creation : {exc}") 121 | exit(1) 122 | 123 | if config_obj == None: 124 | log.error("Error in config() instance initialization") 125 | exit(1) 126 | ita_connector = connector.ITAConnector(config_obj) 127 | # Create TDX Adapter 128 | user_data = b"data generated inside tee" 129 | adapter_type = os.getenv("ADAPTER_TYPE") 130 | if adapter_type is None: 131 | log.error("ADAPTER_TYPE is not set.") 132 | exit(1) 133 | adapter = None 134 | if adapter_type == str(EvidenceType.TDX): 135 | adapter = TDXAdapter(user_data) 136 | elif adapter_type == str(EvidenceType.AZTDX): 137 | adapter = AzureTDXAdapter(user_data) 138 | else: 139 | log.error(f"Invalid Adapter Type Provided: {adapter_type}.") 140 | exit(1) 141 | log.debug(f"adapter type: {adapter_type}") 142 | if trust_authority_policy_id != None: 143 | attest_args = connector.AttestArgs( 144 | adapter, 145 | token_signing_algorithm, 146 | policy_must_match, 147 | trust_authority_request_id, 148 | policy_ids, 149 | ) 150 | else: 151 | attest_args = connector.AttestArgs( 152 | adapter, 153 | token_signing_algorithm, 154 | policy_must_match, 155 | trust_authority_request_id, 156 | ) 157 | # Fetch Attestation Token from ITA 158 | attestation_token = ita_connector.attest(attest_args) 159 | if attestation_token is None: 160 | log.error("Attestation Token is not returned.") 161 | exit(1) 162 | token = attestation_token.token 163 | log.info(f"Attestation token : {token}") 164 | token_headers_json = json.loads(attestation_token.headers.replace("'", '"')) 165 | log.info( 166 | "Request id and Trace id are: %s, %s", 167 | token_headers_json.get("request-id"), 168 | token_headers_json.get("trace-id"), 169 | ) 170 | # verify token- recieved from connector 171 | try: 172 | verified_token = ita_connector.verify_token(token) 173 | except Exception as exc: 174 | log.error(f"Token verification returned exception : {exc}") 175 | if verified_token is not None: 176 | log.info("Token Verification Successful") 177 | log.info(f"Verified Attestation Token : {verified_token}") 178 | else: 179 | log.info("Token Verification failed") 180 | 181 | 182 | # main for function call. 183 | if __name__ == "__main__": 184 | main() 185 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/nvgpu/README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Python NVIDIA\* H100\* GPU Adapter 2 | 3 |

· 02/26/2025 ·

4 | 5 | The Intel Trust Authority Client for NVIDIA\* H100 GPU is a Python package for collecting evidence for attestation from a NVIDIA H100 GPU. This library uses the NVIDIA Attestation SDK (https://github.com/NVIDIA/nvtrust/tree/main/guest_tools/attestation_sdk) for H100 GPU Evidence generation. This GPU adapter is used with the Intel Trust Authority [**connector**](../connector/README.md) for Python to request an attestation token and verify the same. 6 | 7 | This version of the GPU adapter works with Intel® Trust Domain Extensions (Intel® TDX) and NVIDIA H100 Confidential Computing platforms. 8 | 9 | The GPU adapter can be used to attest only a NVIDIA H100 GPU (or only the Intel TDX TEE), but the primary use case is a combined attestation of both the Intel TDX trust domain and the NVIDIA H100 GPU. The GPU adapter collects evidence from the GPU, and the Intel TDX adapter collects evidence from the trust domain. The connector combines the evidence from both adapters and sends it to Intel Trust Authority for verification. If successful, the response is an attestation token (JWT) that can be used to verify the integrity of the platform. 10 | 11 | For more information, see [GPU Remote Attestation](https://docs.trustauthority.intel.com/main/articles/concept-gpu-attestation.html) in the Intel Trust Authority documentation. 12 | 13 | ## Requirements 14 | 15 | The following prerequisites must be installed on the CVM (Confidential VM with Intel TDX): 16 | 17 | - Use **Python 3.8 or newer**. 18 | - Ubuntu 22.04 with *kernel 6.7 or later,* or Ubuntu 24.04. Support for the ConfigFS-TSM subsystem is required for Intel TDX attestation. 19 | - NVIDIA H100 GPU 20 | - [NVIDIA Attestation SDK v1.4.0](https://github.com/NVIDIA/nvtrust/releases/tag/v1.4.0) installed in the guest TD. NVIDIA Attestation SDK v2.x is _not_ supported. 21 | 22 | > [!NOTE] 23 | > The NVIDIA Attestation SDK requires the GPU Local Verifier. The version must match the SDK v1.4 24 | 25 | ## Usage 26 | 27 | To create a new NVIDIA GPU adapter and use the adapter to collect evidence from Intel TDX and NVIDIA H100 Condidential Computing enabled platform. 28 | 29 | ```python 30 | #Create a new GPU adapter 31 | adapter = GPUAdapter() 32 | 33 | #Use this adapter to get evidence 34 | evidence = adapter.collect_evidence(nonce) 35 | if evidence == None: 36 | return None #error condition 37 | ``` 38 | 39 | ## License 40 | 41 | This source is distributed under the BSD-style license found in the [LICENSE](../../LICENSE) 42 | file. 43 | 44 |

45 | --- 46 | 47 | **\*** Other names and brands may be claimed as the property of others. 48 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/nvgpu/gpu_adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import json 8 | import base64 9 | import secrets 10 | import hashlib 11 | import logging as log 12 | from nv_attestation_sdk.gpu import attest_gpu_remote 13 | from inteltrustauthorityclient.resources import constants as const 14 | from inteltrustauthorityclient.base.evidence_adapter import EvidenceAdapter 15 | from inteltrustauthorityclient.connector.evidence import Evidence, EvidenceType 16 | 17 | class GPUAdapter(EvidenceAdapter): 18 | def __init__(self): 19 | """Initializes GPU adapter object 20 | """ 21 | 22 | def collect_evidence(self, nonce): 23 | if nonce != None: 24 | # If ITA verifier nonce is used or user provides a nonce, transform it to 32-byte Hex string nonce (NVDIA SDK accepts nonce in 32-byte Hex only ) 25 | gpu_nonce = hashlib.sha256(nonce).hexdigest() 26 | else: 27 | # If nonce is not provided, generate random nonce in size of 32byte hex string 28 | gpu_nonce = secrets.token_bytes(32).hex() 29 | try: 30 | evidence_list = attest_gpu_remote.generate_evidence(gpu_nonce) 31 | # Only single GPU attestaton is supported for now. 32 | raw_evidence = evidence_list[0] 33 | log.debug("Collected GPU Evidence Successfully") 34 | log.debug("GPU Nonce : {gpu_nonce}") 35 | log.info(f"GPU Evidence : {raw_evidence}") 36 | except Exception as e: 37 | log.exception(f"Caught Exception: {e}") 38 | return None 39 | 40 | # Build GPU evidence payload to be sent to Intel Trust Authority Service 41 | evidence_payload = self.build_payload(gpu_nonce, raw_evidence['attestationReportHexStr'], raw_evidence['certChainBase64Encoded']) 42 | if evidence_payload is None: 43 | log.error("GPU Evidence not returned") 44 | return None 45 | 46 | gpu_evidence = Evidence(EvidenceType.NVGPU, evidence_payload, None, None) 47 | return gpu_evidence 48 | 49 | def build_payload(self, nonce, evidence, cert_chain): 50 | data = dict() 51 | data['nonce'] = nonce 52 | 53 | try: 54 | encoded_evidence_bytes = evidence.encode("ascii") 55 | encoded_evidence = base64.b64encode(encoded_evidence_bytes) 56 | encoded_evidence = encoded_evidence.decode('utf-8') 57 | except Exception as exc: 58 | log.error(f"Error while encoding data :{exc}") 59 | return None 60 | 61 | data['evidence'] = encoded_evidence 62 | data['arch'] = 'HOPPER' 63 | data['certificate'] = str(cert_chain) 64 | 65 | try: 66 | payload = json.dumps(data) 67 | except TypeError as exc: 68 | log.error(f"Unable to serialize the data: {exc}") 69 | return None 70 | return payload 71 | 72 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/resources/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/resources/constants.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2023-2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | DEFAULT_RETRY_WAIT_MIN_SEC = 2 8 | DEFAULT_RETRY_WAIT_MAX_SEC = 2 9 | DEFAULT_RETRY_MAX_NUM = 2 10 | DEFAULT_CLIENT_TIMEOUT_SEC = 30 11 | ATS_CERTCHAIN_MAXLENGTH = 10 12 | REQUEST_ID_MAX_LEN = 128 13 | POLICY_IDS_MAX_LEN = 10 14 | HTTP_PROXY = "HTTP_PROXY" 15 | HTTPS_PROXY = "HTTPS_PROXY" 16 | HTTP_HEADER_API_KEY = "x-api-key" 17 | HTTP_HEADER_APPLICATION_JSON = "application/json" 18 | NV_GPU_ADAPTER = "NV-GPU" 19 | HTTP_HEADER_KEY_ACCEPT = "Accept" 20 | HTTP_HEADER_KEY_CONTENT_TYPE = "Content-Type" 21 | HTTP_HEADER_REQUEST_ID = "Request-Id" 22 | 23 | 24 | # Intel Trust Authority URLs 25 | NONCE_URL = "appraisal/v2/nonce" 26 | AZURE_TDX_ATTEST_URL = "appraisal/v1/attest/azure/tdxvm" 27 | INTEL_TDX_ATTEST_URL = "appraisal/v1/attest" 28 | COMPOSITE_ATTEST_URL = "appraisal/v2/attest" 29 | 30 | # GCP Specific 31 | TDX_ATTEST_DEV_PATH = "/dev/tdx_guest" 32 | REQ_BUF_SIZE = 4 * 4 * 1024 33 | TDX_REPORT_OFFSET = 32 34 | TDX_REPORT_DATA_LEN = 64 35 | TDX_REPORT_SIZE = 1024 36 | RUNTIME_DATA_SIZE_OFFSET = 1232 37 | RUNTIME_DATA_OFFSET = 1236 38 | IOC_WRITE = 1 39 | IOC_READ = 2 40 | IOC_NR_BITS = 8 41 | IOC_TYPE_BITS = 8 42 | IOC_SIZE_BITS = 14 43 | IOC_NR_SHIFT = 0 44 | IOC_TYPE_SHIFT = IOC_NR_SHIFT + IOC_NR_BITS 45 | IOC_SIZE_SHIFT = IOC_TYPE_SHIFT + IOC_TYPE_BITS 46 | IOC_DIR_SHIFT = IOC_SIZE_SHIFT + IOC_SIZE_BITS 47 | 48 | # SGX Specific 49 | SGX_REPORT_BODY_RESERVED1_BYTES = 12 50 | SGX_REPORT_BODY_RESERVED2_BYTES = 32 51 | SGX_REPORT_BODY_RESERVED3_BYTES = 32 52 | SGX_REPORT_BODY_RESERVED4_BYTES = 42 53 | SGX_TARGET_INFO_RESERVED1_BYTES = 2 54 | SGX_TARGET_INFO_RESERVED2_BYTES = 8 55 | SGX_TARGET_INFO_RESERVED3_BYTES = 384 56 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/resources/logger.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import os 8 | import logging 9 | 10 | 11 | def setup_logging(): 12 | """This function sets the log level to value provided by user or default value as INFO 13 | 14 | Raises: 15 | ValueError: Value error is raised if the log level is not within values defined by logging. 16 | """ 17 | log_level = os.environ.get("LOGGING_LEVEL", "INFO").upper() 18 | numeric_level = getattr(logging, log_level, None) 19 | if not isinstance(numeric_level, int): 20 | raise ValueError(f"Invalid log level: {log_level}") 21 | 22 | logging.basicConfig( 23 | level=numeric_level, 24 | format="[%(levelname)s] :: %(asctime)s :: {%(pathname)s:%(lineno)d} :: %(message)s", 25 | datefmt="%Y-%m-%d %H:%M:%S", 26 | ) 27 | print(f"log level set to: ", log_level) 28 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/sgx/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/sgx/intel/README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Client for Python Intel SGX Adapter 2 | 3 |

· 07/03/2024 ·

4 | 5 | The **sgx/intel** adapter enables a confidential confidential computing client to collect a quote from an SGX enclave for attestation by Intel Trust Authority. This sgx adapter is used with the [**connector**](../../connector/README.md) to request an attestation token and verify the same. 6 | 7 | ## Requirements 8 | 9 | - Use **Python 3.8 or newer**. 10 | - Intel® SGX DCAP for quote generation. For more information, see https://github.com/intel/SGXDataCenterAttestationPrimitives 11 | 12 | ## Unit Tests 13 | To run the tests, refer [Readme](../../../test/README.md). 14 | 15 | ## Usage 16 | 17 | To Create a new SGX adapter, then use the adapter to collect quote from SGX enabled platform. 18 | 19 | ```python 20 | #Create a new sgx adapter 21 | # enclave_id: SGX Enclave id 22 | # report_function: Callback Report function to get Enclave Report Data. 23 | ## returns: SGX Enclave Report Data 24 | adapter = SGXAdapter(enclave_id, report_function, user_data) 25 | 26 | #Use this adapter to get evidence 27 | evidence = adapter.collect_evidence(nonce) 28 | if evidence == None: 29 | return None #error condition 30 | ``` 31 | 32 | ## License 33 | 34 | This source is distributed under the BSD-style license found in the [LICENSE](../../../LICENSE) 35 | file. -------------------------------------------------------------------------------- /inteltrustauthorityclient/sgx/intel/sgx_adapter.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import base64 3 | import logging as log 4 | from dataclasses import dataclass 5 | from ctypes import * 6 | from inteltrustauthorityclient.connector.evidence import Evidence, EvidenceType 7 | from inteltrustauthorityclient.resources import constants as const 8 | 9 | 10 | class sgx_attributes_t(ctypes.Structure): 11 | _fields_ = [("flags", ctypes.c_long), ("xfrm", ctypes.c_long)] 12 | 13 | 14 | class sgx_measurement_t(ctypes.Structure): 15 | _fields_ = [("m", ctypes.c_uint8 * 32)] 16 | 17 | 18 | sgx_config_svn_t = ctypes.c_uint16 19 | sgx_misc_select_t = ctypes.c_uint32 20 | sgx_config_id_t = ctypes.c_uint8 * 64 21 | 22 | 23 | class sgx_target_info_t(ctypes.Structure): 24 | _fields_ = [ 25 | ("mr_enclave", sgx_measurement_t), 26 | ("attributes", sgx_attributes_t), 27 | ("reserved1", ctypes.c_uint8 * const.SGX_TARGET_INFO_RESERVED1_BYTES), 28 | ("config_svn", sgx_config_svn_t), 29 | ("misc_select", sgx_misc_select_t), 30 | ("reserved2", ctypes.c_uint8 * const.SGX_TARGET_INFO_RESERVED2_BYTES), 31 | ("config_id", sgx_config_id_t), 32 | ("reserved3", ctypes.c_uint8 * const.SGX_TARGET_INFO_RESERVED3_BYTES), 33 | ] 34 | 35 | 36 | class sgx_cpu_svn_t(ctypes.Structure): 37 | _fields_ = [("svn", ctypes.c_uint8 * 16)] 38 | 39 | 40 | class sgx_report_data_t(ctypes.Structure): 41 | _fields_ = [("d", c_uint8 * 64)] 42 | 43 | 44 | class sgx_isvext_prod_id_t(ctypes.Structure): 45 | _fields_ = [("id", ctypes.c_uint8 * 16)] 46 | 47 | 48 | class sgx_report_body_t(ctypes.Structure): 49 | _fields_ = [ 50 | ("cpu_svn", sgx_cpu_svn_t), 51 | ("misc_select", ctypes.c_uint32), 52 | ("reserved1", ctypes.c_uint8 * const.SGX_REPORT_BODY_RESERVED1_BYTES), 53 | ("isv_ext_prod_id", ctypes.c_uint8 * 16), 54 | ("attributes", sgx_attributes_t), 55 | ("mr_enclave", sgx_measurement_t), 56 | ("reserved2", ctypes.c_uint8 * const.SGX_REPORT_BODY_RESERVED2_BYTES), 57 | ("mr_signer", sgx_measurement_t), 58 | ("reserved3", ctypes.c_uint8 * const.SGX_REPORT_BODY_RESERVED3_BYTES), 59 | ("config_id", ctypes.c_uint8 * 64), 60 | ("isv_prod_id", ctypes.c_uint16), 61 | ("isv_svn", ctypes.c_uint16), 62 | ("config_svn", ctypes.c_uint16), 63 | ("reserved4", ctypes.c_uint8 * const.SGX_REPORT_BODY_RESERVED4_BYTES), 64 | ("isv_family_id", ctypes.c_uint8 * 16), 65 | ("report_data", sgx_report_data_t), 66 | ] 67 | 68 | 69 | class sgx_key_id_t(ctypes.Structure): 70 | _fields_ = [("id", ctypes.c_uint8 * 32)] 71 | 72 | 73 | class sgx_mac_t(ctypes.Structure): 74 | _fields_ = [("mac", ctypes.c_uint8 * 16)] 75 | 76 | 77 | class sgx_report_t(ctypes.Structure): 78 | _fields_ = [ 79 | ("body", sgx_report_body_t), 80 | ("key_id", sgx_key_id_t), 81 | ("mac", sgx_mac_t), 82 | ] 83 | 84 | 85 | class SGXAdapter: 86 | """This class creates adapter which collects SGX Quote from Intel SGX platform.""" 87 | 88 | def __init__(self, eid, report_function, user_data: bytearray = None) -> None: 89 | """Initializes Intel sgx adapter object 90 | Args: 91 | eid (string): Enclave id 92 | report_function (function): Function to Get Enclave Report Data 93 | user_data (byte array, optional): User data. 94 | """ 95 | self.eid = eid 96 | self.report_function = report_function 97 | self.user_data = user_data 98 | 99 | def collect_evidence(self, nonce=None) -> Evidence: 100 | """This Function calls Intel SGX Dcap Library Functions to get SGX quote. 101 | 102 | Args: 103 | nonce ([]byte]): optional nonce provided by Intel Trust Authority 104 | 105 | Returns: 106 | evidence: object to Evidence class 107 | """ 108 | try: 109 | # Load the SGX DCAP library 110 | sgx_dcap_ql = ctypes.CDLL("libsgx_dcap_ql.so") 111 | except FileNotFoundError as e: 112 | log.exception( 113 | f"Caught Exception in loading the libsgx_dcap_ql.so library: {e}" 114 | ) 115 | return None 116 | except OSError as e: 117 | log.exception( 118 | f"Caught Exception in loading the libsgx_dcap_ql.so library: {e}" 119 | ) 120 | return None 121 | except Exception as e: 122 | log.exception( 123 | f"Caught Exception in loading the libsgx_dcap_ql.so library: {e}" 124 | ) 125 | return None 126 | 127 | sgx_dcap_ql.sgx_qe_get_target_info.argtypes = [ 128 | ctypes.POINTER(sgx_target_info_t) 129 | ] 130 | sgx_dcap_ql.sgx_qe_get_target_info.restype = ctypes.c_int 131 | 132 | sgx_dcap_ql.sgx_qe_get_quote_size.argtypes = [ctypes.POINTER(ctypes.c_int)] 133 | sgx_dcap_ql.sgx_qe_get_quote_size.restype = ctypes.c_int 134 | 135 | sgx_dcap_ql.sgx_qe_get_quote.argtypes = [ 136 | ctypes.POINTER(sgx_report_t), 137 | ctypes.c_int, 138 | ctypes.c_void_p, 139 | ] 140 | sgx_dcap_ql.sgx_qe_get_quote.restype = ctypes.c_int 141 | 142 | ret_val = ctypes.c_int(0) 143 | # Define structs required to be passed to fetch the report 144 | qe3_target = sgx_target_info_t() 145 | p_report = sgx_report_t() 146 | 147 | # Fetch target info by calling the respective sgx sdk function 148 | qe3_ret = sgx_dcap_ql.sgx_qe_get_target_info(ctypes.byref(qe3_target)) 149 | if qe3_ret != 0: 150 | raise RuntimeError( 151 | f"sgx_qe_get_target_info return error code 0x{qe3_ret:04x}" 152 | ) 153 | 154 | # Create Nonce object based on nonce input provided by user 155 | nonce_ptr = ctypes.create_string_buffer(nonce) 156 | 157 | # Call the report function 158 | status = self.report_function( 159 | self.eid, 160 | ctypes.byref(ret_val), 161 | ctypes.byref(qe3_target), 162 | nonce_ptr, 163 | len(nonce), 164 | ctypes.byref(p_report), 165 | ) 166 | if status != 0: 167 | raise RuntimeError(f"Report callback returned error code {hex(status)}") 168 | if ret_val.value != 0: 169 | raise RuntimeError(f"Report retval returned error {hex(ret_val.value)}") 170 | # Quote size C native object 171 | quote_size = ctypes.c_int() 172 | 173 | # Fetch the quote size by calling the respective sgx sdk function 174 | qe3_ret = sgx_dcap_ql.sgx_qe_get_quote_size(ctypes.byref(quote_size)) 175 | if qe3_ret != 0: 176 | raise RuntimeError( 177 | f"sgx_qe_get_quote_size return error code {hex(qe3_ret)}" 178 | ) 179 | 180 | # Create a quote buffer object with the required quote size 181 | quote_buffer = ctypes.create_string_buffer(quote_size.value) 182 | 183 | # Fetch the sgx quote by calling the respective sgx sdk function 184 | qe3_ret = sgx_dcap_ql.sgx_qe_get_quote( 185 | ctypes.byref(p_report), quote_size.value, quote_buffer 186 | ) 187 | if qe3_ret != 0: 188 | raise RuntimeError(f"sgx_qe_get_quote return error code {hex(qe3_ret)}") 189 | try: 190 | quote_data = base64.b64encode( 191 | bytearray(quote_buffer[: quote_size.value]) 192 | ).decode("utf-8") 193 | except Exception as exc: 194 | log.error(f"Error while encoding data :{exc}") 195 | return None 196 | return Evidence(EvidenceType.SGX, quote_data, None, self.user_data) 197 | -------------------------------------------------------------------------------- /inteltrustauthorityclient/tdx/README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Intel TDX Adapter 2 | 3 |

· 09/13/2024 ·

4 | 5 | There are two types of Intel Trust Authority adapters for Intel® Trust Domain Extensions (Intel® TDX) platforms: 6 | 7 | 1. An adapter for systems that use the configfs/TSM report subsystem to collect evidence for attestation. Supported platforms include bare-metal Intel TDX hosts and and Google Cloud Platform (GCP) confidential VMs with Intel TDX. The `./tdx` folder contains the Intel Trust Authority Intel TDX adapter for Intel TDX platforms. 8 | 2. An adapter for use with Microsoft Azure confidential VMs with Intel TDX. The `./tdx/azure` folder contains a the Intel Trust Authority Intel TDX adapter for Azure confidential VMs. 9 | 10 | 11 | ## Requirements 12 | 13 | - Python 3.8 or newer 14 | - Linux Kernel 6.7 or newer 15 | 16 | ## Unit Tests 17 | To run the tests, refer [Readme](../../../test/README.md). 18 | 19 | ## Usage 20 | 21 | To create a new Intel TDX adapter, then use the adapter to collect a quote (evidence): 22 | 23 | ```python 24 | #Create a new tdx adapter 25 | adapter = TDXAdapter(user_data) 26 | 27 | #Use this adapter to get evidence 28 | evidence = adapter.collect_evidence(nonce) 29 | if evidence == None: 30 | return None #error condition 31 | ``` 32 | 33 | ## License 34 | 35 | This source is distributed under the BSD-style license found in the [LICENSE](../../../LICENSE) 36 | file. 37 | 38 |

39 | --- 40 | 41 | **\*** Other names and brands may be claimed as the property of others. -------------------------------------------------------------------------------- /inteltrustauthorityclient/tdx/azure/README.md: -------------------------------------------------------------------------------- 1 | # Intel® Trust Authority Python Adapter for Azure Confidential VMs with Intel® TDX 2 | 3 |

· 09/16/2024 ·

4 | 5 | There are two types of Intel® Trust Authority adapters for Intel® Trust Domain Extensions (Intel® TDX) platforms: 6 | 7 | 1. An adapter for systems that use the configfs/TSM report subsystem to collect evidence for attestation. Supported platforms include bare-metal Intel TDX hosts and and Google Cloud Platform (GCP) confidential VMs with Intel TDX. The `./tdx` folder contains the Intel Trust Authority Intel TDX adapter for Intel TDX platforms. 8 | 2. An adapter for use with Microsoft Azure\* confidential VMs with Intel TDX. The `./tdx/azure` folder contains a the Intel Trust Authority Intel TDX adapter for Azure confidential VMs. 9 | 10 | This README is for the Microsoft Azure adapter, **azure-tdx-adapter.py**. It is specifically built to work with the Microsoft Azure implementation of the Intel TDX stack. The Azure adapter requires the TPM2 TSS library (specifically TSS2 ESYS APIs) and tpm2-tools to be installed on the TD for quote generation. For more information, see [TPM2 TSS library](https://github.com/tpm2-software/tpm2-tss). 11 | 12 | The TPM2 TSS library must be installed in the build environment to build the adapter. For more information, see [TPM2 TSS library installation steps](https://github.com/tpm2-software/tpm2-tss/blob/master/INSTALL.md). 13 | 14 | ## Requirements 15 | 16 | - Python 3.8 or newer 17 | - TPM2 TSS library 18 | 19 | Install tpm2-tools on the Azure confidential VM with Intel TDX before using the adapter to generate a quote. 20 | 21 | ## Unit Tests 22 | To run the tests, refer [Readme](../../../test/README.md). 23 | 24 | ## Usage 25 | 26 | To Create a new Azure TDX adapter, then use the adapter to collect quote from Azure TDX enabled platform. 27 | 28 | ```python 29 | #Create a new tdx adapter 30 | adapter = AzureTDXAdapter(user_data) 31 | 32 | #Use this adapter to get evidence 33 | evidence = adapter.collect_evidence(nonce) 34 | if evidence == None: 35 | return None #error condition 36 | ``` 37 | 38 | ## License 39 | 40 | This source is distributed under the BSD-style license found in the [LICENSE](../../../LICENSE) 41 | file. 42 | 43 |

44 | --- 45 | 46 | **\*** Other names and brands may be claimed as the property of others. -------------------------------------------------------------------------------- /inteltrustauthorityclient/tdx/azure/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ -------------------------------------------------------------------------------- /inteltrustauthorityclient/tdx/azure/azure_tdx_adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import base64 8 | import requests 9 | import time 10 | import json 11 | import io 12 | import subprocess 13 | import struct 14 | import hashlib 15 | import tempfile 16 | import os 17 | import binascii 18 | 19 | import logging as log 20 | import inteltrustauthorityclient.resources.constants as const 21 | from inteltrustauthorityclient.connector.evidence import Evidence, EvidenceType 22 | from inteltrustauthorityclient.base.evidence_adapter import EvidenceAdapter 23 | 24 | TD_REPORT_OFFSET = 32 25 | TD_REPORT_SIZE = 1024 26 | RUNTIME_DATA_SIZE_OFFSET = 1232 27 | RUNTIME_DATA_OFFSET = 1236 28 | 29 | 30 | class AzureTDXAdapter: 31 | """This class creates adapter which collects TDX Quote from Azure TDX platform.""" 32 | 33 | def __init__(self, user_data: bytearray = None) -> None: 34 | """Initializes azure tdx adapter object 35 | 36 | Args: 37 | user_data (_type_, optional): _description_. Defaults to None. 38 | """ 39 | self.user_data = user_data 40 | 41 | def collect_evidence(self, nonce=None) -> Evidence: 42 | """This Function calls tpm2-tools to get Azure TDX quote. 43 | 44 | Args: 45 | nonce ([]byte]): optional nonce provided 46 | 47 | Returns: 48 | evidence: object to Evidence class 49 | """ 50 | if nonce != None or self.user_data != None: 51 | sha512_hash = hashlib.sha512() 52 | if nonce != None: 53 | sha512_hash.update(nonce) 54 | if self.user_data != None: 55 | sha512_hash.update(self.user_data) 56 | digest = sha512_hash.digest() 57 | else: 58 | digest = bytearray(64) 59 | 60 | # Check if tpm2_nvreadpublic 0x01400002 is defined 61 | # If not then define it 62 | command = ["tpm2_nvreadpublic", "0x01400002"] 63 | try: 64 | subprocess.run(command, check=True, stdout=subprocess.DEVNULL) 65 | except Exception as e: 66 | log.info("Creating nv_index as it is not defined already") 67 | try: 68 | command = ["tpm2_nvdefine", "-C", "o", "0x01400002", "-s", "64"] 69 | subprocess.run(command, check=True) 70 | except subprocess.CalledProcessError as e: 71 | log.error(f"issue in creating nv_index: {e}") 72 | return None 73 | except Exception as e: 74 | log.error(f"issue in creating nv_index: {e}") 75 | return None 76 | 77 | # Write sha512(nonce || user_data) to NVIndex : "tpm2_nvwrite", "-C", "o", "0x1400002", "-i", "-" 78 | try: 79 | with tempfile.NamedTemporaryFile(mode="wb", delete=False) as temp_file: 80 | temp_filename = temp_file.name 81 | temp_file.write(digest) 82 | command = ["tpm2_nvwrite", "-C", "o", "0x01400002", "-i", temp_filename] 83 | result = subprocess.run(command, check=True) 84 | except subprocess.CalledProcessError as e: 85 | log.error(f"issue in writing to nv_index: {e}") 86 | return None 87 | except Exception as e: 88 | log.error(f"issue in writing to nv_index {e}") 89 | return None 90 | finally: 91 | temp_file.close() 92 | 93 | time.sleep(3) 94 | # Read the final report at "0x01400001" 95 | try: 96 | command = ["tpm2_nvread", "-C", "o", "0x01400001"] 97 | result = subprocess.run(command, capture_output=True) 98 | tpm_report = result.stdout 99 | except subprocess.CalledProcessError as e: 100 | log.error(f"error while reading TDReport from NVIndex.: {e}") 101 | return None 102 | except Exception as e: 103 | log.error(f"error while reading TDReport from NVIndex. {e}") 104 | return None 105 | 106 | td_report = tpm_report[TD_REPORT_OFFSET : TD_REPORT_OFFSET + TD_REPORT_SIZE] 107 | 108 | # give the report to azure as input to get the quote 109 | payload = base64.b64encode(td_report).decode("utf-8") 110 | # send report to Azure 111 | url = "http://169.254.169.254/acc/tdquote" 112 | headers = {"Content-Type": "application/json"} 113 | body = {"report": payload} 114 | payload_json = json.dumps(body) 115 | timeout_sec = ( 116 | const.DEFAULT_CLIENT_TIMEOUT_SEC 117 | if os.getenv("CLIENT_TIMEOUT_SEC") is None 118 | else os.getenv("CLIENT_TIMEOUT_SEC") 119 | ) 120 | try: 121 | response = requests.post( 122 | url, data=payload_json, headers=headers, timeout=timeout_sec 123 | ) 124 | response.raise_for_status() 125 | except requests.HTTPError as e: 126 | log.error(f"got http error: {e.response.status_code} {e}") 127 | return None 128 | except Exception as e: 129 | log.error(f"got error in post request: {e}") 130 | return None 131 | resp_quote = response.json() 132 | quote = resp_quote.get("quote") 133 | r_size = struct.unpack( 134 | " None: 21 | """Initializes tdx adapter object 22 | 23 | Args: 24 | user_data (bytearray): contains any user data to be added to Quote 25 | """ 26 | self.user_data = user_data 27 | 28 | def collect_evidence(self, nonce=None) -> Evidence: 29 | """This Function checks the linux subsytem to collect quote using configfs-tsm 30 | 31 | Args: 32 | nonce ([]byte]): optional nonce provided 33 | 34 | Returns: 35 | evidence: object to Evidence class 36 | """ 37 | 38 | evidence = None 39 | td_quote = None 40 | sha512_hash = hashlib.sha512() 41 | # Get hash of nonce and user data 42 | if nonce != None or self.user_data != None: 43 | if nonce != None: 44 | sha512_hash.update(nonce) 45 | if self.user_data != None: 46 | sha512_hash.update(self.user_data) 47 | 48 | digest = sha512_hash.digest() 49 | request_instance = Request(digest, False) 50 | report = Report() 51 | try: 52 | response = report.get_report(request_instance) 53 | if response is None: 54 | log.error(f"Failed to fetch quote using Configfs TSM.") 55 | return None 56 | 57 | td_quote = response.out_blob 58 | except Exception as e: 59 | log.error(f"Failed to fetch quote using Configfs TSM. Error: {e}") 60 | return None 61 | 62 | if td_quote is not None: 63 | quote = base64.b64encode(bytearray(td_quote)).decode("utf-8") 64 | runtime_data = self.user_data 65 | # Create evidence class object to be returned 66 | evidence = Evidence(EvidenceType.TDX, quote, None, runtime_data) 67 | else: 68 | log.error(f"Failed to get response from Configfs TSM.") 69 | return None 70 | return evidence 71 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "applications.security.amber.trustauthority-client-for-python" 3 | version = "1.1.0" 4 | description = "Intel Trust Authority Client in Python" 5 | authors = ["Kamal, Shefali ", "Bala, K Manisai ", "Yoon, Eunjung "] 6 | license = "Copyright (c) 2024 Intel Corporation. All rights reserved." 7 | readme = "README.md" 8 | packages = [{include = "inteltrustauthorityclient"}] 9 | exclude = ["inteltrustauthorityclient/examples"] 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.8" 13 | validators = "^0.22.0" 14 | resources = "^0.0.1" 15 | staty = "^1.2.4" 16 | requests = "^2.32.3" 17 | cryptography = "^43.0.1" 18 | pyOpenSSL = "24.2.1" 19 | pyjwt = "^2.9.0" 20 | tenacity = "^8.2.3" 21 | coverage = "^7.4.2" 22 | pytest = "^8.0.2" 23 | 24 | [build-system] 25 | requires = ["poetry-core"] 26 | build-backend = "poetry.core.masonry.api" 27 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # applications.security.amber.trustauthority-client-for-python 2 | 3 | Intel Trust Authority Python Client unit tests are based on unittest framework. 4 | 5 | ### Supported OS 6 | - Ubuntu 20.04 7 | 8 | 9 | ### Prerequisites 10 | ``` 11 | poetry add coverage 12 | poetry update 13 | ``` 14 | 15 | ### Run unit tests from **/applications.security.amber.trustauthority-client-for-python/test** with coverage enabled: 16 | ``` 17 | poetry run coverage run --source ../inteltrustauthorityclient/ --omit="../inteltrustauthorityclient/examples/*,../inteltrustauthorityclient/resources/*" -m unittest discover -p 'test_*.py' 18 | ``` 19 | 20 | ### Generate coverage report: 21 | ``` 22 | poetry run coverage report -m 23 | ``` -------------------------------------------------------------------------------- /test/test_azure_tdx_adapter.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import subprocess 3 | import requests 4 | from unittest.mock import patch, MagicMock 5 | from inteltrustauthorityclient.tdx.azure.azure_tdx_adapter import AzureTDXAdapter 6 | 7 | 8 | class AzureTDXAdapterTestCase(unittest.TestCase): 9 | """Class AzureTDXAdapterTestCase that inherits from unittest.TestCase""" 10 | 11 | def test_collect_evidence_without_nonce_and_user_data(self): 12 | """Test method to test collect_evidence without nonce and user_data""" 13 | tdx_adapter = AzureTDXAdapter(None) 14 | with patch("subprocess.run") as mock_run, patch( 15 | "requests.post" 16 | ) as mock_post, patch("json.loads") as mock_json_loads, patch( 17 | "binascii.hexlify" 18 | ) as mock_hexlify: 19 | mock_run.return_value = MagicMock(stdout=b"tpm_report") 20 | 21 | mock_post.return_value = MagicMock(json=lambda: {"quote": "quote"}) 22 | mock_json_loads.return_value = {"user-data": "user_data"} 23 | mock_hexlify.return_value = b"user_data" 24 | with patch("struct.unpack") as mock_unpack: 25 | mock_unpack.return_value = [1] 26 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 27 | self.assertIsNotNone(evidence) 28 | 29 | def test_collect_evidence_with_exception_in_tpm2_nvreadpublic(self): 30 | """Test method to test collect_evidence with exception in tpm2_nvreadpublic""" 31 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 32 | with patch("subprocess.run") as mock_run: 33 | mock_run.side_effect = Exception("Error in tpm2_nvreadpublic") 34 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 35 | self.assertIsNone(evidence) 36 | 37 | def test_collect_evidence_with_exception_in_tpm2_nvdefine(self): 38 | """Test method to test collect_evidence with exception in tpm2_nvdefine""" 39 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 40 | with patch("subprocess.run") as mock_run: 41 | mock_run.side_effect = [ 42 | subprocess.CalledProcessError(1, "tpm2_nvdefine"), 43 | None, 44 | ] 45 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 46 | self.assertIsNone(evidence) 47 | 48 | def test_collect_evidence_with_exception_in_tpm2_nvwrite(self): 49 | """Test method to test collect_evidence with exception in tpm2_nvwrite""" 50 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 51 | with patch("subprocess.run") as mock_run: 52 | mock_run.side_effect = [ 53 | None, 54 | subprocess.CalledProcessError(1, "tpm2_nvwrite"), 55 | None, 56 | ] 57 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 58 | self.assertIsNone(evidence) 59 | 60 | def test_collect_evidence_with_exception_in_tpm2_nvread(self): 61 | """Test method to test collect_evidence with exception in tpm2_nvread""" 62 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 63 | with patch("subprocess.run") as mock_run: 64 | mock_run.side_effect = [ 65 | None, 66 | None, 67 | subprocess.CalledProcessError(1, "tpm2_nvread"), 68 | None, 69 | ] 70 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 71 | self.assertIsNone(evidence) 72 | 73 | def test_collect_evidence_with_exception_in_requests_post(self): 74 | """Test method to test collect_evidence with exception in requests.post""" 75 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 76 | with patch("subprocess.run") as mock_run, patch( 77 | "requests.post" 78 | )as mock_post: 79 | mock_run.return_value = MagicMock(stdout=b"tpm_report") 80 | mock_post.side_effect = requests.HTTPError("Mocked HTTP Error") 81 | mock_post.side_effect.response = MagicMock(status_code=500, reason="Not Found") 82 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 83 | self.assertIsNone(evidence) 84 | 85 | def test_collect_evidence_with_successful_execution(self): 86 | """Test method to test collect_evidence with successful execution""" 87 | tdx_adapter = AzureTDXAdapter(user_data=b"user_data") 88 | with patch("subprocess.run") as mock_run, patch( 89 | "requests.post" 90 | ) as mock_post, patch("json.loads") as mock_json_loads, patch( 91 | "binascii.hexlify" 92 | ) as mock_hexlify: 93 | mock_run.return_value = MagicMock(stdout=b"tpm_report") 94 | mock_post.return_value = MagicMock(json=lambda: {"quote": "quote"}) 95 | mock_json_loads.return_value = {"user-data": "user_data"} 96 | mock_hexlify.return_value = b"user_data" 97 | with patch("struct.unpack") as mock_unpack: 98 | mock_unpack.return_value = [1] 99 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 100 | self.assertIsNotNone(evidence) 101 | 102 | 103 | if __name__ == "__main__": 104 | unittest.main() 105 | -------------------------------------------------------------------------------- /test/test_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import unittest 8 | from inteltrustauthorityclient.connector.config import * 9 | 10 | 11 | class ConfigTestCase(unittest.TestCase): 12 | """class ConfigTestCase that inherits from unittest.TestCase""" 13 | 14 | def test_retry_config(self): 15 | """Test method to test retry config object initialisation""" 16 | retryconfig_obj = RetryConfig(2, 2, 2, 2) 17 | self.assertEqual(retryconfig_obj.retry_wait_min_sec, 2) 18 | self.assertEqual(retryconfig_obj.retry_wait_max_sec, 2) 19 | self.assertEqual(retryconfig_obj.retry_max_num, 2) 20 | self.assertEqual(retryconfig_obj.timeout_sec, 2) 21 | 22 | def test_retry_config_default_values(self): 23 | """Test method to test RetryConfig object initialization with custom values""" 24 | retryconfig_obj = RetryConfig(0, 0, 0) 25 | self.assertEqual(retryconfig_obj.retry_wait_min_sec, 2) 26 | self.assertEqual(retryconfig_obj.retry_wait_max_sec, 2) 27 | self.assertEqual(retryconfig_obj.retry_max_num, 2) 28 | self.assertEqual(retryconfig_obj.timeout_sec, 30) 29 | 30 | def test_retry_config_retry_policy(self): 31 | """Test method to test RetryConfig retry_policy method""" 32 | retryconfig_obj = RetryConfig(2, 5, 3) 33 | self.assertTrue(retryconfig_obj.retry_policy(500)) 34 | self.assertTrue(retryconfig_obj.retry_policy(503)) 35 | self.assertTrue(retryconfig_obj.retry_policy(504)) 36 | self.assertFalse(retryconfig_obj.retry_policy(200)) 37 | self.assertFalse(retryconfig_obj.retry_policy(404)) 38 | self.assertFalse(retryconfig_obj.retry_policy(502)) 39 | 40 | def test_config(self): 41 | """Test method to test config object initialisation""" 42 | config_obj = Config( 43 | RetryConfig(2, 2, 2, 2), 44 | "https://custom-base-url-ITA.com", 45 | "https://custom-api-url-ITA.com", 46 | "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3", 47 | ) 48 | self.assertEqual(config_obj.api_key, "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3") 49 | self.assertEqual(config_obj.api_url, "https://custom-api-url-ITA.com") 50 | self.assertEqual(config_obj.base_url, "https://custom-base-url-ITA.com") 51 | self.assertEqual(config_obj.retry_cfg.retry_wait_min_sec, 2) 52 | self.assertEqual(config_obj.retry_cfg.retry_wait_max_sec, 2) 53 | self.assertEqual(config_obj.retry_cfg.retry_max_num, 2) 54 | self.assertEqual(config_obj.retry_cfg.timeout_sec, 2) 55 | 56 | def test_config_default_timeout(self): 57 | """Test method to test config object initialisation with default timeout setting""" 58 | config_obj = Config( 59 | RetryConfig(2, 2, 2), 60 | "https://custom-base-url-ITA.com", 61 | "https://custom-api-url-ITA.com", 62 | "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3", 63 | ) 64 | self.assertEqual(config_obj.api_key, "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3") 65 | self.assertEqual(config_obj.api_url, "https://custom-api-url-ITA.com") 66 | self.assertEqual(config_obj.base_url, "https://custom-base-url-ITA.com") 67 | self.assertEqual(config_obj.retry_cfg.retry_wait_min_sec, 2) 68 | self.assertEqual(config_obj.retry_cfg.retry_wait_max_sec, 2) 69 | self.assertEqual(config_obj.retry_cfg.retry_max_num, 2) 70 | self.assertEqual(config_obj.retry_cfg.timeout_sec, 30) 71 | 72 | def test_config_invalid_baseurl(self): 73 | """Test method to test config object initialisation with Invalid Base URL""" 74 | with self.assertRaises(ValueError): 75 | config_obj = Config( 76 | RetryConfig(2, 2, 2, 2), 77 | "httpa://custom-base-url-ITA.com", 78 | "https://custom-api-url-ITA.com", 79 | "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3", 80 | ) 81 | 82 | def test_config_invalid_apiurl(self): 83 | """Test method to test config object initialisation with Invalid API URL""" 84 | with self.assertRaises(ValueError): 85 | config_obj = Config( 86 | RetryConfig(2, 2, 2, 2), 87 | "https://custom-base-url-ITA.com", 88 | "httpa://custom-api-url-ITA.com", 89 | "095KGgj5Eh8DqqjZwwp6J8HuE8fC2Efo5Z2qSUf3", 90 | ) 91 | 92 | def test_config_invalid_policyID(self): 93 | """Test method to test config object initialisation with Invalid PolicyID""" 94 | uuid_str = "invalid-uuid" 95 | self.assertFalse(validate_uuid(uuid_str)) 96 | 97 | def test_config_valid_policyID(self): 98 | """Test method to test config object initialisation with Invalid PolicyID""" 99 | uuid_str = "123e4567-e89b-12d3-a456-426614174000" 100 | self.assertTrue(validate_uuid(uuid_str)) 101 | 102 | def test_config_invalid_requestID(self): 103 | """Test method to test config object initialisation with Invalid API URL""" 104 | request_id = "@1234" 105 | self.assertFalse(validate_requestid(request_id)) 106 | 107 | def test_config_valid_requestID(self): 108 | """Test method to test config object initialisation with Invalid API URL""" 109 | request_id = "1234" 110 | self.assertTrue(validate_requestid(request_id)) 111 | 112 | 113 | if __name__ == "__main__": 114 | unittest.main() 115 | -------------------------------------------------------------------------------- /test/test_intel_sgx_adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import unittest 8 | from unittest.mock import patch, MagicMock 9 | from inteltrustauthorityclient.sgx.intel.sgx_adapter import * 10 | 11 | 12 | def adapter_object(): 13 | """This method initializes TDX Adapter object""" 14 | mock_report_function = lambda *args, **kwargs: 0 15 | return SGXAdapter("eid", mock_report_function, "user_data") 16 | 17 | 18 | class SGXAdapterTestCase(unittest.TestCase): 19 | """class TDXAdapterTestCase that inherits from unittest.TestCase""" 20 | 21 | def test_adpater(self): 22 | """Test method to test SGX Adapter""" 23 | sgx_adapter = adapter_object() 24 | with patch.object(ctypes, "CDLL") as mock_cdll: 25 | mock_cdll.return_value = None 26 | with patch("ctypes.CDLL") as mock_cdll: 27 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 0 28 | with patch("ctypes.create_string_buffer") as mock_string_buffer: 29 | mock_string_buffer.return_value = b"mock_string_buffer" 30 | mock_cdll.return_value.sgx_qe_get_quote_size.return_value = 0 31 | mock_cdll.return_value.sgx_qe_get_quote.return_value = 0 32 | with patch("base64.b64encode") as mock_encode: 33 | mock_encode.return_value = b"SGVsbG8sIFdvcmxkIQ==" 34 | evidence = sgx_adapter.collect_evidence("nonce") 35 | assert evidence is not None 36 | 37 | def test_adpater_cdll_os_error(self): 38 | """Test method to test SGX Adapter with raising OS Error while loading dcap library""" 39 | sgx_adapter = adapter_object() 40 | 41 | def mock_cdll(arg1): 42 | raise OSError("mock os error") 43 | 44 | with patch.object(ctypes, "CDLL", new=mock_cdll): 45 | # mock_cdll.return_value = OSError("mock os error") 46 | evidence = sgx_adapter.collect_evidence("nonce") 47 | assert evidence is None 48 | 49 | def test_adpater_cdll_exception(self): 50 | """Test method to test SGX Adapter with raising Exception while loading dcap library""" 51 | sgx_adapter = adapter_object() 52 | 53 | def mock_cdll(arg1): 54 | raise Exception("mock Exception") 55 | 56 | with patch.object(ctypes, "CDLL", new=mock_cdll): 57 | # mock_cdll.return_value = OSError("mock os error") 58 | evidence = sgx_adapter.collect_evidence("nonce") 59 | assert evidence is None 60 | 61 | def test_adpater_get_target_info(self): 62 | """Test method to test sgx_qe_get_target_info function""" 63 | sgx_adapter = adapter_object() 64 | with patch.object(ctypes, "CDLL") as mock_cdll: 65 | mock_cdll.return_value = None 66 | with patch("ctypes.CDLL") as mock_cdll: 67 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 1 68 | with self.assertRaises(RuntimeError): 69 | evidence = sgx_adapter.collect_evidence("nonce") 70 | 71 | def test_adpater_report_function(self): 72 | """Test method to test SGX Adapter Report Function""" 73 | mock_report_function = lambda *args, **kwargs: 1 74 | sgx_adapter = SGXAdapter("eid", mock_report_function, "user_data") 75 | with patch.object(ctypes, "CDLL") as mock_cdll: 76 | mock_cdll.return_value = None 77 | with patch("ctypes.CDLL") as mock_cdll: 78 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 0 79 | with patch("ctypes.create_string_buffer") as mock_string_buffer: 80 | mock_string_buffer.return_value = b"mock_string_buffer" 81 | with self.assertRaises(RuntimeError): 82 | evidence = sgx_adapter.collect_evidence("nonce") 83 | 84 | def test_adpater_get_quote_size_function(self): 85 | """Test method to test SGX Adapter sgx_qe_get_quote_size Function""" 86 | sgx_adapter = adapter_object() 87 | with patch.object(ctypes, "CDLL") as mock_cdll: 88 | mock_cdll.return_value = None 89 | with patch("ctypes.CDLL") as mock_cdll: 90 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 0 91 | with patch("ctypes.create_string_buffer") as mock_string_buffer: 92 | mock_string_buffer.return_value = b"mock_string_buffer" 93 | mock_cdll.return_value.sgx_qe_get_quote_size.return_value = 1 94 | with self.assertRaises(RuntimeError): 95 | evidence = sgx_adapter.collect_evidence("nonce") 96 | 97 | def test_adpater_get_quote_function(self): 98 | """Test method to test SGX Adapter sgx_qe_get_quote Function""" 99 | sgx_adapter = adapter_object() 100 | with patch.object(ctypes, "CDLL") as mock_cdll: 101 | mock_cdll.return_value = None 102 | with patch("ctypes.CDLL") as mock_cdll: 103 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 0 104 | with patch("ctypes.create_string_buffer") as mock_string_buffer: 105 | mock_string_buffer.return_value = b"mock_string_buffer" 106 | mock_cdll.return_value.sgx_qe_get_quote_size.return_value = 0 107 | mock_cdll.return_value.sgx_qe_get_quote.return_value = 1 108 | with self.assertRaises(RuntimeError): 109 | evidence = sgx_adapter.collect_evidence("nonce") 110 | 111 | def test_adpater_base64_b64encode(self): 112 | """Test method to test SGX Adapter base64.b64encode Function with unencoded string buffer""" 113 | sgx_adapter = adapter_object() 114 | with patch.object(ctypes, "CDLL") as mock_cdll: 115 | mock_cdll.return_value = None 116 | with patch("ctypes.CDLL") as mock_cdll: 117 | mock_cdll.return_value.sgx_qe_get_target_info.return_value = 0 118 | with patch("ctypes.create_string_buffer") as mock_string_buffer: 119 | mock_string_buffer.return_value = ( 120 | "mock_string_buffer_without_encoding" 121 | ) 122 | mock_cdll.return_value.sgx_qe_get_quote_size.return_value = 0 123 | mock_cdll.return_value.sgx_qe_get_quote.return_value = 0 124 | evidence = sgx_adapter.collect_evidence("nonce") 125 | assert evidence is None 126 | 127 | 128 | if __name__ == "__main__": 129 | unittest.main() 130 | -------------------------------------------------------------------------------- /test/test_tdx_adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2024 Intel Corporation 3 | All rights reserved. 4 | SPDX-License-Identifier: BSD-3-Clause 5 | """ 6 | 7 | import unittest 8 | from unittest.mock import patch, MagicMock, call 9 | 10 | 11 | from inteltrustauthorityclient.tdx.tdx_adapter import TDXAdapter 12 | from inteltrustauthorityclient.connector.evidence import Evidence 13 | 14 | 15 | class TDXAdapterTestCase(unittest.TestCase): 16 | """Class TDXAdapterTestCase that inherits from unittest.TestCase""" 17 | 18 | def test_collect_evidence_with_nonce_and_user_data(self): 19 | """Test method to test collect_evidence with nonce and user_data""" 20 | tdx_adapter = TDXAdapter(user_data=b"user_data") 21 | with patch("os.path.exists") as mock_exists, patch( 22 | "tempfile.TemporaryDirectory" 23 | ) as mock_tempdir, patch("builtins.open", create=True) as mock_open, patch( 24 | "base64.b64encode" 25 | ) as mock_b64encode, patch( 26 | "os.rmdir" 27 | ) as mock_os_remove, patch( 28 | "hashlib.sha512" 29 | ) as mock_sha512: 30 | mock_sha512.return_value.digest.return_value = b"digest" 31 | mock_exists.return_value = True 32 | mock_os_remove.return_value = True 33 | mock_tempdir.return_value.__enter__.return_value = "tempdir" 34 | mock_open.side_effect = [ 35 | MagicMock(write=MagicMock()), 36 | MagicMock(read=MagicMock()), 37 | MagicMock(read=MagicMock()), 38 | MagicMock(read=MagicMock()), 39 | ] 40 | 41 | mock_b64encode.return_value.decode.return_value = "quote" 42 | evidence = tdx_adapter.collect_evidence(nonce=b"nonce") 43 | mock_sha512.assert_called_once_with() 44 | calls = [call(b"nonce"), call(b"user_data")] 45 | mock_sha512.return_value.update.assert_has_calls(calls) 46 | 47 | calls = [call("/sys/kernel/config/tsm/report"), call("tempdir/inblob")] 48 | mock_exists.assert_has_calls(calls) 49 | mock_tempdir.assert_called_once_with( 50 | prefix="entry", dir="/sys/kernel/config/tsm/report" 51 | ) 52 | mock_open.assert_has_calls( 53 | [ 54 | unittest.mock.call("tempdir/inblob", "wb"), 55 | unittest.mock.call("tempdir/outblob", "rb"), 56 | unittest.mock.call("tempdir/provider", "r", encoding="utf-8"), 57 | unittest.mock.call("tempdir/generation", "r", encoding="utf-8"), 58 | ] 59 | ) 60 | self.assertTrue(isinstance(evidence, Evidence)) 61 | 62 | def test_collect_evidence_without_nonce_and_user_data(self): 63 | """Test method to test collect_evidence without nonce and user_data""" 64 | tdx_adapter = TDXAdapter() 65 | with patch("os.path.exists") as mock_exists, patch( 66 | "tempfile.TemporaryDirectory" 67 | ) as mock_tempdir, patch("builtins.open", create=True) as mock_open, patch( 68 | "base64.b64encode" 69 | ) as mock_b64encode, patch( 70 | "os.rmdir" 71 | ) as mock_os_remove, patch( 72 | "hashlib.sha512" 73 | ) as mock_sha512: 74 | mock_sha512.return_value.digest.return_value = b"digest" 75 | mock_exists.return_value = True 76 | mock_os_remove.return_value = True 77 | mock_tempdir.return_value.__enter__.return_value = "tempdir" 78 | mock_open.side_effect = [ 79 | MagicMock(write=MagicMock()), 80 | MagicMock(read=MagicMock()), 81 | MagicMock(read=MagicMock()), 82 | MagicMock(read=MagicMock()), 83 | ] 84 | 85 | mock_b64encode.return_value.decode.return_value = "quote" 86 | evidence = tdx_adapter.collect_evidence() 87 | mock_sha512.assert_called_once_with() 88 | 89 | calls = [call("/sys/kernel/config/tsm/report"), call("tempdir/inblob")] 90 | mock_exists.assert_has_calls(calls) 91 | mock_tempdir.assert_called_once_with( 92 | prefix="entry", dir="/sys/kernel/config/tsm/report" 93 | ) 94 | mock_open.assert_has_calls( 95 | [ 96 | unittest.mock.call("tempdir/inblob", "wb"), 97 | unittest.mock.call("tempdir/outblob", "rb"), 98 | unittest.mock.call("tempdir/provider", "r", encoding="utf-8"), 99 | unittest.mock.call("tempdir/generation", "r", encoding="utf-8"), 100 | ] 101 | ) 102 | self.assertTrue(isinstance(evidence, Evidence)) 103 | 104 | def test_collect_evidence_with_tsm_dir_not_found(self): 105 | """Test method to test collect_evidence when TSM directory is not found""" 106 | tdx_adapter = TDXAdapter() 107 | with patch("hashlib.sha512") as mock_sha512, patch( 108 | "os.path.exists" 109 | ) as mock_exists, patch("logging.error") as mock_error: 110 | mock_sha512.return_value.digest.return_value = b"digest" 111 | mock_exists.return_value = False 112 | evidence = tdx_adapter.collect_evidence() 113 | mock_exists.assert_called_once_with("/sys/kernel/config/tsm/report") 114 | mock_error.assert_called_once_with( 115 | "Failed to fetch quote using Configfs TSM. Error: TSM directory not found." 116 | ) 117 | self.assertIsNone(evidence) 118 | 119 | def test_collect_evidence_with_exception_in_tempdir_creation(self): 120 | """Test method to test collect_evidence with exception in creating report temporary directory""" 121 | tdx_adapter = TDXAdapter() 122 | with patch("hashlib.sha512") as mock_sha512, patch( 123 | "os.path.exists" 124 | ) as mock_exists, patch("tempfile.TemporaryDirectory") as mock_tempdir, patch( 125 | "logging.error" 126 | ) as mock_error: 127 | mock_sha512.return_value.digest.return_value = b"digest" 128 | mock_exists.return_value = True 129 | mock_tempdir.side_effect = Exception("Error creating tempdir") 130 | evidence = tdx_adapter.collect_evidence() 131 | mock_sha512.assert_called_once_with() 132 | mock_exists.assert_called_once_with("/sys/kernel/config/tsm/report") 133 | mock_error.assert_called_once_with( 134 | "Failed to fetch quote using Configfs TSM. Error: Caught exception in collect_evidence(): Error creating tempdir" 135 | ) 136 | self.assertIsNone(evidence) 137 | 138 | def test_collect_evidence_with_report_file_not_present(self): 139 | """Test method to test collect_evidence with exception as report file not found""" 140 | tdx_adapter = TDXAdapter() 141 | with patch("hashlib.sha512") as mock_sha512, patch( 142 | "os.path.exists" 143 | ) as mock_exists, patch("tempfile.TemporaryDirectory") as mock_tempdir, patch( 144 | "logging.error" 145 | ) as mock_error: 146 | mock_sha512.return_value.digest.return_value = b"digest" 147 | mock_exists.return_value = True 148 | exception = FileNotFoundError("File is not present") 149 | exception.filename = "/sys/kernel/config/tsm/report/entry" 150 | mock_tempdir.side_effect = exception 151 | evidence = tdx_adapter.collect_evidence() 152 | mock_sha512.assert_called_once_with() 153 | mock_exists.assert_called_once_with("/sys/kernel/config/tsm/report") 154 | mock_error.assert_called_once_with( 155 | "Failed to fetch quote using Configfs TSM. Error: Caught FileNotFoundError exception in collect_evidence():/sys/kernel/config/tsm/report/entry" 156 | ) 157 | self.assertIsNone(evidence) 158 | 159 | def test_collect_evidence_with_oserror_temp_file_creation(self): 160 | """Test method to test collect_evidence with OSError""" 161 | tdx_adapter = TDXAdapter() 162 | with patch("hashlib.sha512") as mock_sha512, patch( 163 | "os.path.exists" 164 | ) as mock_exists, patch("tempfile.TemporaryDirectory") as mock_tempdir, patch( 165 | "logging.error" 166 | ) as mock_error: 167 | mock_sha512.return_value.digest.return_value = b"digest" 168 | mock_exists.return_value = True 169 | mock_tempdir.side_effect = OSError("Unable to create temp file") 170 | evidence = tdx_adapter.collect_evidence() 171 | mock_sha512.assert_called_once_with() 172 | mock_exists.assert_called_once_with("/sys/kernel/config/tsm/report") 173 | mock_error.assert_called_once_with( 174 | "Failed to fetch quote using Configfs TSM. Error: Caught OSError exception in collect_evidence(): Unable to create temp file" 175 | ) 176 | self.assertIsNone(evidence) 177 | 178 | def test_collect_evidence_with_ValueError_incorrect_filename(self): 179 | """Test method to test collect_evidence with ValueError""" 180 | tdx_adapter = TDXAdapter() 181 | with patch("hashlib.sha512") as mock_sha512, patch( 182 | "os.path.exists" 183 | ) as mock_exists, patch("tempfile.TemporaryDirectory") as mock_tempdir, patch( 184 | "logging.error" 185 | ) as mock_error: 186 | mock_sha512.return_value.digest.return_value = b"digest" 187 | mock_exists.return_value = True 188 | mock_tempdir.side_effect = ValueError("incorrect file name") 189 | evidence = tdx_adapter.collect_evidence() 190 | mock_sha512.assert_called_once_with() 191 | mock_exists.assert_called_once_with("/sys/kernel/config/tsm/report") 192 | mock_error.assert_called_once_with( 193 | "Failed to fetch quote using Configfs TSM. Error: Caught ValueError exception in collect_evidence(): incorrect file name" 194 | ) 195 | self.assertIsNone(evidence) 196 | 197 | def test_collect_evidence_with_no_inblob(self): 198 | """Test method to test collect_evidence with no inblob file created.""" 199 | tdx_adapter = TDXAdapter() 200 | with patch("os.path.exists") as mock_exists, patch( 201 | "tempfile.TemporaryDirectory" 202 | ) as mock_tempdir, patch("builtins.open", create=True) as mock_open, patch( 203 | "base64.b64encode" 204 | ) as mock_b64encode, patch( 205 | "os.rmdir" 206 | ) as mock_os_remove, patch( 207 | "hashlib.sha512" 208 | ) as mock_sha512, patch( 209 | "logging.error" 210 | ) as mock_error: 211 | mock_sha512.return_value.digest.return_value = b"digest" 212 | mock_exists.side_effect = [ 213 | True, 214 | False, 215 | ] 216 | mock_os_remove.return_value = True 217 | mock_tempdir.return_value.__enter__.return_value = "tempdir" 218 | mock_open.side_effect = [ 219 | MagicMock(write=MagicMock()), 220 | MagicMock(read=MagicMock()), 221 | MagicMock(read=MagicMock()), 222 | MagicMock(read=MagicMock()), 223 | ] 224 | 225 | mock_b64encode.return_value.decode.return_value = "quote" 226 | evidence = tdx_adapter.collect_evidence() 227 | mock_sha512.assert_called_once_with() 228 | calls = [call("/sys/kernel/config/tsm/report"), call("tempdir/inblob")] 229 | mock_exists.assert_has_calls(calls) 230 | mock_tempdir.assert_called_once_with( 231 | prefix="entry", dir="/sys/kernel/config/tsm/report" 232 | ) 233 | mock_error.assert_called_once_with( 234 | "Failed to fetch quote using Configfs TSM. Error: Caught exception in collect_evidence(): Inblob file not found under directory: tempdir" 235 | ) 236 | self.assertIsNone(evidence) 237 | 238 | def test_collect_evidence_with_outblob_exception_while_opening(self): 239 | """Test method to test collect_evidence with opening outblob file for reading quote.""" 240 | tdx_adapter = TDXAdapter() 241 | with patch("os.path.exists") as mock_exists, patch( 242 | "tempfile.TemporaryDirectory" 243 | ) as mock_tempdir, patch("builtins.open", create=True) as mock_open, patch( 244 | "os.rmdir" 245 | ) as mock_os_remove, patch( 246 | "hashlib.sha512" 247 | ) as mock_sha512, patch( 248 | "logging.error" 249 | ) as mock_error: 250 | mock_sha512.return_value.digest.return_value = b"digest" 251 | mock_exists.return_value = True 252 | mock_os_remove.return_value = True 253 | mock_tempdir.return_value.__enter__.return_value = "tempdir" 254 | mock_open.side_effect = [ 255 | MagicMock(write=MagicMock()), 256 | Exception("Error in opening outblob file"), 257 | MagicMock(read=MagicMock()), 258 | MagicMock(read=MagicMock()), 259 | ] 260 | 261 | evidence = tdx_adapter.collect_evidence() 262 | mock_sha512.assert_called_once_with() 263 | 264 | calls = [call("/sys/kernel/config/tsm/report"), call("tempdir/inblob")] 265 | mock_exists.assert_has_calls(calls) 266 | mock_tempdir.assert_called_once_with( 267 | prefix="entry", dir="/sys/kernel/config/tsm/report" 268 | ) 269 | mock_error.assert_called_once_with( 270 | "Failed to fetch quote using Configfs TSM. Error: Caught exception in collect_evidence(): Error in opening outblob file: Error in opening outblob file" 271 | ) 272 | self.assertIsNone(evidence) 273 | 274 | 275 | if __name__ == "__main__": 276 | unittest.main() 277 | --------------------------------------------------------------------------------