├── CODEOWNERS
├── .gitignore
├── inteltrustauthorityclient
├── examples
│ ├── sgx_sample_app
│ │ ├── minimal-enclave
│ │ │ ├── .config_HW_RELEASE_x64
│ │ │ ├── README.md
│ │ │ ├── utils.o
│ │ │ ├── Enclave.o
│ │ │ ├── Enclave_t.o
│ │ │ ├── Enclave_u.o
│ │ │ ├── libutils.so
│ │ │ ├── enclave.signed.so
│ │ │ ├── Enclave.lds
│ │ │ ├── Enclave.config.xml
│ │ │ ├── utils.h
│ │ │ ├── Enclave_t.h
│ │ │ ├── Enclave_u.h
│ │ │ ├── utils.cpp
│ │ │ ├── Enclave_u.c
│ │ │ ├── Enclave.edl
│ │ │ ├── Enclave_private_sample.pem
│ │ │ ├── Enclave.cpp
│ │ │ ├── Enclave_t.c
│ │ │ └── Makefile
│ │ ├── __init__.py
│ │ ├── docker-compose.yml
│ │ ├── Dockerfile
│ │ ├── sgx_sample_app.py
│ │ └── README.md
│ ├── __init__.py
│ ├── tdx_sample_app
│ │ ├── __init__.py
│ │ ├── docker-compose.yml
│ │ ├── Dockerfile
│ │ ├── tdx_sample_app.py
│ │ └── README.md
│ └── .env
├── __init__.py
├── base
│ ├── __init__.py
│ └── evidence_adapter.py
├── sgx
│ ├── __init__.py
│ └── intel
│ │ ├── README.md
│ │ └── sgx_adapter.py
├── resources
│ ├── __init__.py
│ ├── logger.py
│ └── constants.py
├── tdx
│ ├── azure
│ │ ├── __init__.py
│ │ ├── README.md
│ │ └── azure_tdx_adapter.py
│ ├── README.md
│ └── tdx_adapter.py
├── connector
│ ├── evidence.py
│ ├── README.md
│ └── config.py
├── nvgpu
│ ├── README.md
│ └── gpu_adapter.py
├── configfs_tsm
│ └── report.py
└── cli
│ ├── README.md
│ ├── test
│ └── test_trustauthority-pycli.py
│ └── trustauthority-pycli
│ └── trustauthority-pycli.py
├── SECURITY.md
├── test
├── README.md
├── test_azure_tdx_adapter.py
├── test_config.py
├── test_intel_sgx_adapter.py
└── test_tdx_adapter.py
├── pyproject.toml
├── .github
└── workflows
│ ├── security-scans.yml
│ ├── onmergerelease.yml
│ └── onpullrequest.yml
├── LICENSE
├── CONTRIBUTING.md
├── README.md
└── CODE_OF_CONDUCT.md
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | /python-client k.manisai.bala@intel.com
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | py-api/__pycache__/
3 | *.pyc
4 | *__pycache__
5 | dist/*
6 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/.config_HW_RELEASE_x64:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/README.md:
--------------------------------------------------------------------------------
1 | Based from SGXDataPrimitives/SampleCode/QuoteGenerationSample
--------------------------------------------------------------------------------
/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/sgx/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2024 Intel Corporation
3 | All rights reserved.
4 | SPDX-License-Identifier: BSD-3-Clause
5 | """
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2024 Intel Corporation
3 | All rights reserved.
4 | SPDX-License-Identifier: BSD-3-Clause
5 | """
--------------------------------------------------------------------------------
/inteltrustauthorityclient/resources/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2024 Intel Corporation
3 | All rights reserved.
4 | SPDX-License-Identifier: BSD-3-Clause
5 | """
--------------------------------------------------------------------------------
/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/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/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/sgx_sample_app/minimal-enclave/utils.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/utils.o
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave.o
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_t.o
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/Enclave_u.o
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/libutils.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/libutils.so
--------------------------------------------------------------------------------
/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/enclave.signed.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/trustauthority-client-for-python/HEAD/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave/enclave.signed.so
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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=22.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-jammy1
16 | PSW_VERSION=2.22.100.3
17 |
18 | # Adapter selection
19 | # Change this to sgx/aztdx/tdx/nvgpu as per the need to build the correct image.
20 | ADAPTER_TYPE=tdx
--------------------------------------------------------------------------------
/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 | ```
--------------------------------------------------------------------------------
/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_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/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 | cap_drop:
13 | - ALL
14 | security_opt:
15 | - no-new-privileges:true
16 | read_only: true
17 | build:
18 | context: ../../..
19 | dockerfile: inteltrustauthorityclient/examples/tdx_sample_app/Dockerfile
20 | args:
21 | - UBUNTU_VERSION=${UBUNTU_VERSION}
22 | - ADAPTER_TYPE=${ADAPTER_TYPE}
23 | env_file:
24 | - ../.env
25 |
--------------------------------------------------------------------------------
/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/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 | security_opt:
12 | - no-new-privileges:true
13 | cap_drop:
14 | - ALL
15 | container_name: trust_authority_python_client_sgx_sample_app
16 | read_only: true
17 | build:
18 | context: ../../..
19 | dockerfile: inteltrustauthorityclient/examples/sgx_sample_app/Dockerfile
20 | args:
21 | - UBUNTU_VERSION=${UBUNTU_VERSION}
22 | - DCAP_VERSION=${DCAP_VERSION}
23 | - PSW_VERSION=${PSW_VERSION}
24 | - ADAPTER_TYPE=${ADAPTER_TYPE}
25 | env_file:
26 | - ../.env
27 |
--------------------------------------------------------------------------------
/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-2025 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.9"
13 | validators = "^0.22.0"
14 | resources = "^0.0.1"
15 | staty = "^1.2.4"
16 | requests = "^2.32.4"
17 | cryptography = {version = "^44.0.1", python = ">=3.8,<3.9.0 || >3.9.1,<4.0"}
18 | pyjwt = "^2.9.0"
19 | tenacity = "^8.2.3"
20 | coverage = "^7.4.2"
21 | pytest = "^8.0.2"
22 | nvidia-ml-py = "^12.550.52"
23 |
24 | [build-system]
25 | requires = ["poetry-core"]
26 | build-backend = "poetry.core.masonry.api"
27 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/sgx/intel/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust AuthorityClient for Python Intel SGX Adapter
2 |
3 | · 05/21/2025 ·
4 |
5 | The **sgx/intel** adapter enables a confidential confidential computing client to collect a quote from an Intel SGX enclave for attestation by Intel® Tiber™ Trust Authority. This Intel 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/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/tdx/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust Authority Intel TDX Adapter
2 |
3 | · 05/21/2025 ·
4 |
5 | There are two types of Intel® Tiber™ 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.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/.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 | setup:
15 | runs-on: ubuntu-latest
16 | outputs:
17 | runner: ${{ steps.step1.outputs.runner }}
18 | steps:
19 | - name: Check repository
20 | id: step1
21 | run: |
22 | if [ ${{ github.repository }} == 'intel/trustauthority-client-for-python' ]; then
23 | echo "runner=ubuntu-latest" >> $GITHUB_OUTPUT
24 | else
25 | echo "runner=self-hosted" >> $GITHUB_OUTPUT
26 | fi
27 |
28 | build-test-scan:
29 | needs: [setup]
30 | runs-on: ${{ needs.setup.outputs.runner }}
31 | env:
32 | http_proxy: ${{ secrets.HTTP_PROXY }}
33 | https_proxy: ${{ secrets.HTTPS_PROXY }}
34 | no_proxy: ${{ secrets.NO_PROXY }}
35 | PYTHONPATH: ${{ github.workspace }}:$PYTHONPATH
36 |
37 | steps:
38 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
39 | with:
40 | fetch-depth: 0
41 |
42 | - name: Setup Python
43 | uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
44 | with:
45 | python-version: '3.9'
46 |
47 | - name: Install Poetry
48 | run: pip install poetry==1.7.1
49 |
50 | - name: Install dependencies via Poetry
51 | run: poetry install
52 |
53 | - name: Run UT with Coverage enabled
54 | run: cd test && poetry run coverage run --source ../inteltrustauthorityclient/ --omit="../inteltrustauthorityclient/nvgpu/*,../inteltrustauthorityclient/examples/*,../inteltrustauthorityclient/resources/*" -m unittest discover -p 'test_*.py'
55 |
56 | - name: Check coverage percentage
57 | run: |
58 | cd test
59 | coverage_percentage=$(poetry run coverage report --format=total)
60 | if [ $coverage_percentage -lt 80 ]; then
61 | echo "Coverage is less than 80 %: $coverage_percentage %"
62 | exit 1
63 | else
64 | echo "Coverage is greater than or equal to 80 : $coverage_percentage %"
65 | fi
66 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/tdx/azure/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust AuthorityPython Adapter for Azure Confidential VMs with Intel® TDX
2 |
3 | · 05/21/2025 ·
4 |
5 | There are two types of Intel® Tiber™ 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/nvgpu/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust Authority Python NVIDIA\* H100\* GPU Adapter
2 |
3 | · 05/21/2025 ·
4 |
5 | The Intel® Tiber™ Trust Authority Client for NVIDIA\* H100 GPU is a Python package for collecting evidence for attestation from a NVIDIA H100 GPU. 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/articles/ita/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.9 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 Management Library (NVML). Install NVML by running the following command on the CVM after Python is installed: `pip install nvidia-ml-py`.
21 |
22 | ## Usage
23 |
24 | To create a new NVIDIA GPU adapter and use the adapter to collect evidence from Intel TDX and NVIDIA H100 Condidential Computing enabled platform.
25 |
26 | ```python
27 | #Create a new GPU adapter
28 | adapter = GPUAdapter()
29 |
30 | #Use this adapter to get evidence
31 | evidence = adapter.collect_evidence(nonce)
32 | if evidence == None:
33 | return None #error condition
34 | ```
35 |
36 | ## License
37 |
38 | This source is distributed under the BSD-style license found in the [LICENSE](../../LICENSE)
39 | file.
40 |
41 |
42 | ---
43 |
44 | **\*** Other names and brands may be claimed as the property of others.
45 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ### License
4 |
5 | Intel® Tiber™ Trust Authority Client for Python is licensed under the terms in [LICENSE](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 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/tdx/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 hashlib
9 | import logging as log
10 |
11 |
12 | from inteltrustauthorityclient.connector.evidence import Evidence, EvidenceType
13 | from inteltrustauthorityclient.base.evidence_adapter import EvidenceAdapter
14 | from inteltrustauthorityclient.configfs_tsm.report import *
15 |
16 |
17 | class TDXAdapter(EvidenceAdapter):
18 | """This class creates adapter which collects quote using configfs-tsm."""
19 |
20 | def __init__(self, user_data: bytearray = None) -> 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 |
--------------------------------------------------------------------------------
/.github/workflows/onpullrequest.yml:
--------------------------------------------------------------------------------
1 | name: OnPullRequest
2 |
3 | on:
4 | pull_request:
5 |
6 | permissions: read-all
7 |
8 | jobs:
9 | setup:
10 | runs-on: ubuntu-latest
11 | outputs:
12 | runner: ${{ steps.step1.outputs.runner }}
13 | steps:
14 | - name: Check repository
15 | id: step1
16 | run: |
17 | if [ ${{ github.repository }} == 'intel/trustauthority-client-for-python' ]; then
18 | echo "runner=ubuntu-latest" >> $GITHUB_OUTPUT
19 | else
20 | echo "runner=self-hosted" >> $GITHUB_OUTPUT
21 | fi
22 |
23 | security-file-check:
24 | needs: [setup]
25 | runs-on: ${{ needs.setup.outputs.runner }}
26 | steps:
27 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
28 | with:
29 | fetch-depth: 0
30 |
31 | - name: Check Security.md file
32 | run: |
33 | if [ ! -f ./SECURITY.md ]; then
34 | echo "Security.md file is missing"
35 | exit 1
36 | fi
37 |
38 | build-test-scan:
39 | needs: [setup]
40 | runs-on: ${{ needs.setup.outputs.runner }}
41 | env:
42 | http_proxy: ${{ secrets.HTTP_PROXY }}
43 | https_proxy: ${{ secrets.HTTPS_PROXY }}
44 | no_proxy: ${{ secrets.NO_PROXY }}
45 | PYTHONPATH: ${{ github.workspace }}:$PYTHONPATH
46 |
47 | steps:
48 | - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
49 | with:
50 | fetch-depth: 0
51 |
52 | - name: Setup Python
53 | uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
54 | with:
55 | python-version: '3.9'
56 |
57 | - name: Install Poetry
58 | run: pip install poetry==1.7.1
59 |
60 | - name: Install dependencies via Poetry
61 | run: poetry install
62 |
63 | - name: Run UT with Coverage enabled
64 | run: cd test && poetry run coverage run --source ../inteltrustauthorityclient/ --omit="../inteltrustauthorityclient/nvgpu/*,../inteltrustauthorityclient/examples/*,../inteltrustauthorityclient/resources/*" -m unittest discover -p 'test_*.py'
65 |
66 | - name: Check coverage percentage
67 | run: |
68 | cd test
69 | coverage_percentage=$(poetry run coverage report --format=total)
70 | if [ $coverage_percentage -lt 80 ]; then
71 | echo "Coverage is less than 80 %: $coverage_percentage %"
72 | exit 1
73 | else
74 | echo "Coverage is greater than or equal to 80 : $coverage_percentage %"
75 | fi
76 |
--------------------------------------------------------------------------------
/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/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 jammy
43 | RUN echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu jammy main' | tee /etc/apt/sources.list.d/intel-sgx.list && \
44 | wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key && apt-key add intel-sgx-deb.key && cat intel-sgx-deb.key | tee /etc/apt/keyrings/intelsgx-keyring.asc > /dev/null
45 |
46 | WORKDIR /opt/intel
47 | # Download SGX SDK binary
48 | RUN /bin/bash -c "set -o pipefail && wget -q https://download.01.org/intel-sgx/sgx-linux/2.22/distro/ubuntu22.04-server/sgx_linux_x64_sdk_${PSW_VERSION}.bin && \
49 | chmod +x sgx_linux_x64_sdk_${PSW_VERSION}.bin && \
50 | echo 'yes' | ./sgx_linux_x64_sdk_${PSW_VERSION}.bin"
51 |
52 | # Installing Linux SGX SDK
53 | RUN apt-get update && apt-get install -y --no-install-recommends \
54 | libsgx-urts=${PSW_VERSION}-jammy1 \
55 | libsgx-qe3-logic=${DCAP_VERSION} \
56 | libsgx-pce-logic=${DCAP_VERSION} \
57 | libsgx-dcap-ql=${DCAP_VERSION} \
58 | libsgx-dcap-ql-dev=${DCAP_VERSION} \
59 | libcurl4-openssl-dev \
60 | libsgx-dcap-default-qpl=${DCAP_VERSION} \
61 | libsgx-quote-ex=${PSW_VERSION}-jammy1
62 |
63 | COPY --from=build_base /app/dist/applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl .
64 | RUN pip install applications_security_amber_trustauthority_client_for_python-1.1.0-py3-none-any.whl
65 | WORKDIR /app
66 | COPY . .
67 |
68 | # Change permissions for intel user to run make command in /minimal-enclave
69 | RUN chown -R $USERNAME:$USERNAME /app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave
70 |
71 | # Change current user to intel inside the container
72 | USER $USERNAME
73 | WORKDIR /app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave
74 | RUN make clean all
75 |
76 | # Set LD_LIBRARY_PATH
77 | ENV LD_LIBRARY_PATH=/app/inteltrustauthorityclient/examples/sgx_sample_app/minimal-enclave:/usr/local/lib/:/usr/lib/
78 |
79 | # Set the working directory inside the container
80 | WORKDIR /app/inteltrustauthorityclient/examples/sgx_sample_app
81 | ENTRYPOINT ["python3", "sgx_sample_app.py"]
--------------------------------------------------------------------------------
/inteltrustauthorityclient/connector/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust Authorityy Client Connector for Python
2 |
3 | · 05/21/2025 ·
4 |
5 | The Intel® Tiber™ 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/articles/ita/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/articles/ita/concept-gpu-attestation.html) in the Intel Trust Authority documentation.
12 |
13 | [**`get_nonce`**](https://docs.trustauthority.intel.com/main/articles/articles/ita/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/articles/ita/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/articles/ita/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/articles/ita/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/articles/ita/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
143 | def validate_policymustmatch(policy_must_match):
144 | # policy_must_match should be a boolean value
145 | if policy_must_match is None:
146 | return False
147 | if isinstance(policy_must_match, str):
148 | if policy_must_match.lower() in {"true", "false"}:
149 | return True if policy_must_match.lower() == "true" else False
150 | log.error("Unsupported Policy Must Match value provided, supported values are true/false")
151 | return -1
152 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Intel® Tiber™ Trust Authority Client for Python
2 |
3 | · 05/21/2025 ·
4 |
5 | The Intel® Tiber™ 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.9 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.
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/articles/ita/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | " 1:
55 | log.warning("There are more than one NVGPU found, but only the first one used")
56 | gpu_handle = nvmlDeviceGetHandleByIndex(0)
57 |
58 | try:
59 | attestation_report_struct = nvmlDeviceGetConfComputeGpuAttestationReport(gpu_handle,
60 | evidence_nonce)
61 | length_of_attestation_report = attestation_report_struct.attestationReportSize
62 | attestation_report = attestation_report_struct.attestationReport
63 | attestation_report_data = list()
64 |
65 | for i in range(length_of_attestation_report):
66 | attestation_report_data.append(attestation_report[i])
67 |
68 | bin_attestation_report_data = bytes(attestation_report_data)
69 | except Exception as err:
70 | log.error(err)
71 | err_msg = "Something went wrong while fetching the attestation report from the gpu."
72 | raise PynvmlError(err_msg)
73 |
74 | try:
75 | cert_struct = nvmlDeviceGetConfComputeGpuCertificate(gpu_handle)
76 | # fetching the attestation cert chain.
77 | length_of_attestation_cert_chain = cert_struct.attestationCertChainSize
78 | attestation_cert_chain = cert_struct.attestationCertChain
79 | attestation_cert_data = list()
80 |
81 | for i in range(length_of_attestation_cert_chain):
82 | attestation_cert_data.append(attestation_cert_chain[i])
83 |
84 | bin_attestation_cert_data = bytes(attestation_cert_data)
85 |
86 | except Exception as err:
87 | log.error(err)
88 | err_msg = "Something went wrong while fetching the certificate chains from the gpu."
89 | raise PynvmlError(err_msg)
90 |
91 | gpu_evidence = {'certChainBase64Encoded': base64.b64encode(bin_attestation_cert_data),
92 | 'attestationReportHexStr': bin_attestation_report_data.hex()}
93 | evidence_list.append(gpu_evidence)
94 | nvmlShutdown()
95 | except Exception as error:
96 | log.error(error)
97 | finally:
98 | return evidence_list
99 |
100 | class GPUAdapter(EvidenceAdapter):
101 | def __init__(self):
102 | """Initializes GPU adapter object
103 | """
104 |
105 | def collect_evidence(self, nonce):
106 | if nonce != None:
107 | # 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 )
108 | gpu_nonce = hashlib.sha256(nonce).hexdigest()
109 | else:
110 | # If nonce is not provided, generate random nonce in size of 32byte hex string
111 | gpu_nonce = secrets.token_bytes(32).hex()
112 | try:
113 | evidence_list = generate_nvgpu_evidence(gpu_nonce)
114 | # Only single GPU attestaton is supported for now.
115 | raw_evidence = evidence_list[0]
116 | log.debug("Collected GPU Evidence Successfully")
117 | log.debug("GPU Nonce : {gpu_nonce}")
118 | log.info(f"GPU Evidence : {raw_evidence}")
119 | except Exception as e:
120 | log.exception(f"Caught Exception: {e}")
121 | return None
122 |
123 | # Build GPU evidence payload to be sent to Intel Trust Authority Service
124 | evidence_payload = self.build_payload(gpu_nonce, raw_evidence['attestationReportHexStr'], raw_evidence['certChainBase64Encoded'])
125 | if evidence_payload is None:
126 | log.error("GPU Evidence not returned")
127 | return None
128 |
129 | gpu_evidence = Evidence(EvidenceType.NVGPU, evidence_payload, None, None)
130 | return gpu_evidence
131 |
132 | def build_payload(self, nonce, evidence, cert_chain):
133 | data = dict()
134 | data['nonce'] = nonce
135 |
136 | try:
137 | encoded_evidence_bytes = evidence.encode("ascii")
138 | encoded_evidence = base64.b64encode(encoded_evidence_bytes)
139 | encoded_evidence = encoded_evidence.decode('utf-8')
140 | except Exception as exc:
141 | log.error(f"Error while encoding data :{exc}")
142 | return None
143 |
144 | data['evidence'] = encoded_evidence
145 | data['arch'] = 'HOPPER'
146 | data['certificate'] = cert_chain.decode('utf-8')
147 |
148 | try:
149 | payload = json.dumps(data)
150 | except TypeError as exc:
151 | log.error(f"Unable to serialize the data: {exc}")
152 | return None
153 | return payload
154 |
155 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/inteltrustauthorityclient/cli/README.md:
--------------------------------------------------------------------------------
1 |
2 | # IIntel® Tiber™ Trust Authority CLI for Intel TDX and NVIDIA H100 GPU
3 |
4 | · 05/21/2025 ·
5 |
6 | Intel® Tiber™ 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/articles/ita/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.9 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/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/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 and not config.validate_requestid(trust_authority_request_id):
48 | log.error(f"Invalid Request ID: {trust_authority_request_id}")
49 | exit(1)
50 |
51 | policy_ids = []
52 | trust_authority_policy_id = os.getenv("TRUSTAUTHORITY_POLICY_ID")
53 | if trust_authority_policy_id != None:
54 | try:
55 | policy_ids = json.loads(trust_authority_policy_id)
56 | except json.JSONDecodeError as exc:
57 | log.error(f"Invalid TRUSTAUTHORITY_POLICY_ID: {trust_authority_policy_id}")
58 | exit(1)
59 | if len(policy_ids) > const.POLICY_IDS_MAX_LEN:
60 | log.error("policy count in request must be between 1 - 10")
61 | exit(1)
62 | for uuid_str in policy_ids:
63 | if not config.validate_uuid(uuid_str):
64 | log.error(f"Invalid policy UUID: \"{uuid_str}\"")
65 | exit(1)
66 |
67 | retry_max = os.getenv("RETRY_MAX")
68 | if retry_max is None:
69 | log.debug("RETRY_MAX is not provided. Hence, setting default value.")
70 | retry_max = const.DEFAULT_RETRY_MAX_NUM
71 | else:
72 | if not retry_max.isnumeric():
73 | log.error("Invalid RETRY_MAX format: RETRY_MAX must be an Integer.")
74 | exit(1)
75 |
76 | retry_wait_time_min = os.getenv("RETRY_WAIT_TIME_MIN")
77 | if retry_wait_time_min is None:
78 | log.debug("RETRY_WAIT_TIME is not provided. Hence, setting default value.")
79 | retry_wait_time_min = const.DEFAULT_RETRY_WAIT_MIN_SEC
80 | else:
81 | if not retry_wait_time_min.isnumeric():
82 | log.error(
83 | "Invalid RETRY_WAIT_TIME_MIN format: RETRY_WAIT_TIME_MIN must be an Integer."
84 | )
85 | exit(1)
86 |
87 | retry_wait_time_max = os.getenv("RETRY_WAIT_TIME_MAX")
88 | if retry_wait_time_max is None:
89 | log.debug("RETRY_WAIT_TIME_MAX is not provided. Hence, setting default value.")
90 | retry_wait_time_max = const.DEFAULT_RETRY_WAIT_MAX_SEC
91 | else:
92 | if not retry_wait_time_max.isnumeric():
93 | log.error(
94 | "Invalid RETRY_WAIT_TIME_MAX format: RETRY_WAIT_TIME_MAX must be an Integer."
95 | )
96 | exit(1)
97 |
98 | timeout_second = os.getenv("CLIENT_TIMEOUT_SEC")
99 | if timeout_second is None:
100 | log.debug(
101 | "CLIENT_TIMEOUT_SEC is not provided. Hence, setting to default value."
102 | )
103 | timeout_second = const.DEFAULT_CLIENT_TIMEOUT_SEC
104 | else:
105 | if not timeout_second.isnumeric():
106 | log.error("Invalid CLIENT_TIMEOUT_SEC format: CLIENT_TIMEOUT_SEC must be an Integer.")
107 | exit(1)
108 |
109 | token_signing_algorithm = os.getenv("TOKEN_SIGNING_ALGORITHM")
110 | policy_must_match = os.getenv("POLICY_MUST_MATCH")
111 | policy_must_match = config.validate_policymustmatch(policy_must_match)
112 | if policy_must_match == -1:
113 | exit(1)
114 |
115 | try:
116 | # Populate config object
117 | config_obj = config.Config(
118 | config.RetryConfig(
119 | int(retry_wait_time_min),
120 | int(retry_wait_time_max),
121 | int(retry_max),
122 | int(timeout_second),
123 | ),
124 | trustauthority_base_url,
125 | trustAuthority_api_url,
126 | trust_authority_api_key,
127 | )
128 | except ValueError as exc:
129 | log.error(f"Value Error in config object creation : {exc}")
130 | exit(1)
131 |
132 | if config_obj == None:
133 | log.error("Error in config() instance initialization")
134 | exit(1)
135 | ita_connector = connector.ITAConnector(config_obj)
136 | # Create TDX Adapter
137 | user_data = b"data generated inside tee"
138 | adapter_type = os.getenv("ADAPTER_TYPE")
139 | if adapter_type is None:
140 | log.error("ADAPTER_TYPE is not set.")
141 | exit(1)
142 | adapter = None
143 | if adapter_type == str(EvidenceType.TDX):
144 | adapter = TDXAdapter(user_data)
145 | elif adapter_type == str(EvidenceType.AZTDX):
146 | adapter = AzureTDXAdapter(user_data)
147 | else:
148 | log.error(f"Invalid Adapter Type Provided: {adapter_type}.")
149 | exit(1)
150 | log.debug(f"adapter type: {adapter_type}")
151 | attest_args = connector.AttestArgs(
152 | adapter,
153 | token_signing_algorithm,
154 | policy_must_match,
155 | trust_authority_request_id,
156 | policy_ids,
157 | )
158 | # Fetch Attestation Token from ITA
159 | attestation_token = ita_connector.attest(attest_args)
160 | if attestation_token is None:
161 | log.error("Attestation Token is not returned.")
162 | exit(1)
163 | token = attestation_token.token
164 | log.info(f"Attestation token : {token}")
165 | token_headers_json = json.loads(attestation_token.headers.replace("'", '"'))
166 | log.info(
167 | "Request id and Trace id are: %s, %s",
168 | token_headers_json.get("request-id"),
169 | token_headers_json.get("trace-id"),
170 | )
171 | # verify token- recieved from connector
172 | try:
173 | verified_token = ita_connector.verify_token(token)
174 | except Exception as exc:
175 | log.error(f"Token verification returned exception : {exc}")
176 | if verified_token is not None:
177 | log.info("Token Verification Successful")
178 | log.info(f"Verified Attestation Token : {verified_token}")
179 | else:
180 | log.info("Token Verification failed")
181 |
182 |
183 | # main for function call.
184 | if __name__ == "__main__":
185 | main()
--------------------------------------------------------------------------------
/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/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 and not config.validate_requestid(trust_authority_request_id):
102 | log.error(f"Invalid Request ID: {trust_authority_request_id}")
103 | exit(1)
104 |
105 | policy_ids = []
106 | trust_authority_policy_id = os.getenv("TRUSTAUTHORITY_POLICY_ID")
107 | if trust_authority_policy_id != None:
108 | try:
109 | policy_ids = json.loads(trust_authority_policy_id)
110 | except json.JSONDecodeError as exc:
111 | log.error(f"Invalid TRUSTAUTHORITY_POLICY_ID: {trust_authority_policy_id}")
112 | exit(1)
113 | if len(policy_ids) > const.POLICY_IDS_MAX_LEN:
114 | log.error("policy count in request must be between 1 - 10")
115 | exit(1)
116 | for uuid_str in policy_ids:
117 | if not config.validate_uuid(uuid_str):
118 | log.error(f"Invalid policy UUID: \"{uuid_str}\"")
119 | exit(1)
120 |
121 | retry_max = os.getenv("RETRY_MAX")
122 | if retry_max is None:
123 | log.debug("RETRY_MAX is not provided. Hence, setting default value.")
124 | retry_max = const.DEFAULT_RETRY_MAX_NUM
125 | else:
126 | if not retry_max.isnumeric():
127 | log.error("Invalid RETRY_MAX format: RETRY_MAX must be an Integer.")
128 | exit(1)
129 |
130 | retry_wait_time_min = os.getenv("RETRY_WAIT_TIME_MIN")
131 | if retry_wait_time_min is None:
132 | log.debug("RETRY_WAIT_TIME_MIN is not provided. Hence, setting default value.")
133 | retry_wait_time_min = const.DEFAULT_RETRY_WAIT_MIN_SEC
134 | else:
135 | if not retry_wait_time_min.isnumeric():
136 | log.error(
137 | "Invalid RETRY_WAIT_TIME_MIN format: RETRY_WAIT_TIME_MIN must be an Integer."
138 | )
139 | exit(1)
140 |
141 | retry_wait_time_max = os.getenv("RETRY_WAIT_TIME_MAX")
142 | if retry_wait_time_max is None:
143 | log.debug("RETRY_WAIT_TIME_MAX is not provided. Hence, setting default value.")
144 | retry_wait_time_max = const.DEFAULT_RETRY_WAIT_MAX_SEC
145 | else:
146 | if not retry_wait_time_max.isnumeric():
147 | log.error(
148 | "Invalid RETRY_WAIT_TIME_MAX format: RETRY_WAIT_TIME_MAX must be an Integer."
149 | )
150 | exit(1)
151 |
152 | timeout_second = os.getenv("CLIENT_TIMEOUT_SEC")
153 | if timeout_second is None:
154 | log.debug(
155 | "CLIENT_TIMEOUT_SEC is not provided. Hence, setting to default value."
156 | )
157 | timeout_second = const.DEFAULT_CLIENT_TIMEOUT_SEC
158 | else:
159 | if not timeout_second.isnumeric():
160 | log.error("Invalid CLIENT_TIMEOUT_SEC format: CLIENT_TIMEOUT_SEC must be an Integer.")
161 | exit(1)
162 |
163 | token_signing_algorithm = os.getenv("TOKEN_SIGNING_ALGORITHM")
164 | policy_must_match = os.getenv("POLICY_MUST_MATCH")
165 | policy_must_match = config.validate_policymustmatch(policy_must_match)
166 | if policy_must_match == -1:
167 | exit(1)
168 |
169 | # enclave related work
170 | enclave_path = "./minimal-enclave/enclave.signed.so"
171 | eid = create_sgx_enclave(enclave_path)
172 | pub_bytes = loadPublicKey(eid)
173 | try:
174 | # Populate config object
175 | config_obj = config.Config(
176 | config.RetryConfig(
177 | int(retry_wait_time_min),
178 | int(retry_wait_time_max),
179 | int(retry_max),
180 | int(timeout_second),
181 | ),
182 | trustauthority_base_url,
183 | trustAuthority_api_url,
184 | trust_authority_api_key,
185 | )
186 | except ValueError as exc:
187 | log.error(f"Value Error in config object creation : {exc}")
188 | exit(1)
189 |
190 | ita_connector = connector.ITAConnector(config_obj)
191 | adapter_type = os.getenv("ADAPTER_TYPE")
192 | if adapter_type is None:
193 | log.error("ADAPTER_TYPE is not set.")
194 | exit(1)
195 | c_lib = ctypes.CDLL("./minimal-enclave/libutils.so")
196 | adapter = SGXAdapter(eid, c_lib.enclave_create_report, pub_bytes)
197 | attest_args = connector.AttestArgs(
198 | adapter,
199 | token_signing_algorithm,
200 | policy_must_match,
201 | trust_authority_request_id,
202 | policy_ids,
203 | )
204 | # Fetch Attestation Token from ITA
205 | attestation_token = ita_connector.attest(attest_args)
206 | if attestation_token is None:
207 | log.error("Attestation Token is not returned.")
208 | exit(1)
209 | token = attestation_token.token
210 | log.info(f"Attestation token : {token}")
211 | token_headers_json = json.loads(attestation_token.headers.replace("'", '"'))
212 | log.info(
213 | "Request id and Trace id are: %s, %s",
214 | token_headers_json.get("request-id"),
215 | token_headers_json.get("trace-id"),
216 | )
217 | # verify token- recieved from connector
218 | try:
219 | verified_token = ita_connector.verify_token(token)
220 | except Exception as exc:
221 | log.error(f"Token verification returned exception : {exc}")
222 | if verified_token is not None:
223 | log.info("Token Verification Successful")
224 | log.info(f"Verified Attestation Token : {verified_token}")
225 | else:
226 | log.info("Token Verification failed")
227 |
228 |
229 | # main for function call.
230 | if __name__ == "__main__":
231 | main()
232 |
--------------------------------------------------------------------------------
/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 logging as log
11 | import uuid
12 | import sys
13 | import os
14 | import base64
15 | from inteltrustauthorityclient.resources import constants as const
16 | from inteltrustauthorityclient.connector import config, connector
17 | from inteltrustauthorityclient.tdx.tdx_adapter import TDXAdapter
18 | from inteltrustauthorityclient.nvgpu.gpu_adapter import GPUAdapter
19 |
20 | def cmd_evidence(args):
21 | if args.nonce != None:
22 | try:
23 | nonce_bytes = base64.b64decode(args.nonce, validate=True)
24 | except Exception as err:
25 | print(f"Error while base64 decoding of nonce: : {err}")
26 | exit(1)
27 | else:
28 | nonce_bytes = None
29 |
30 | # Collect Intel TDX evidence(quote)
31 | if args.attest_type == 'tdx':
32 | if args.user_data != None:
33 | try:
34 | user_data_bytes = base64.b64decode(args.user_data, validate=True)
35 | except Exception as err:
36 | print(f"Error while base64 decoding of user data: : {err}")
37 | exit(1)
38 | else:
39 | user_data_bytes = b""
40 |
41 | tdx_adapter = TDXAdapter(user_data_bytes)
42 | evidence = tdx_adapter.collect_evidence(nonce_bytes)
43 | if evidence is None:
44 | print("TDX Quote is not returned")
45 | return None
46 | print(f"TDX Quote : {evidence.evidence}")
47 |
48 | # Collect NVGPU evidence
49 | elif args.attest_type == 'nvgpu':
50 | if args.user_data != None:
51 | print("User Data (-u) is used in 'tdx' or 'tdx+nvgpu' attestaion")
52 | exit(1)
53 | gpu_adapter = GPUAdapter()
54 | evidence = gpu_adapter.collect_evidence(nonce_bytes)
55 | if evidence is None:
56 | print("GPU Evidence is not returned")
57 | return None
58 | print(f"GPU evidence : {evidence.evidence}")
59 |
60 |
61 | def cmd_attest(args):
62 | # Check if request id is valid
63 | if args.request_id != None:
64 | if len(args.request_id) > const.REQUEST_ID_MAX_LEN:
65 | print("Request ID should be atmost 128 characters long.")
66 | exit(1)
67 | for req_char in args.request_id:
68 | if req_char != '-' and req_char.isalnum() == False:
69 | print("Request ID should contain only a-z, A-Z, 0-9, and - (hyphen), Special characters are not allowed.")
70 | exit(1)
71 |
72 | # Check if policy uuid is valid and the number of policy counts not exceeded.
73 | if args.policy_ids != None:
74 | policyIds = args.policy_ids.split(",")
75 | if len(policyIds) > const.POLICY_IDS_MAX_LEN:
76 | print("policy count in request must be between 1 - 10")
77 | exit(1)
78 | for pId in policyIds:
79 | if not config.validate_uuid(pId):
80 | print(f"Invalid policy UUID :{pId}")
81 | exit(1)
82 | else:
83 | policyIds = None
84 |
85 | # Check if the token signing algorithm is supported
86 | if args.token_sign_alg != None:
87 | tsa = args.token_sign_alg
88 | if tsa not in ["RS256", "PS384"]:
89 | print(f"Unsupported token signing algorithm {tsa}, refer help section for supported algorithms")
90 | exit(1)
91 |
92 | if args.user_data != None:
93 | if args.attest_type =='nvgpu':
94 | print("User Data (-u) is used in 'tdx' or 'tdx+nvgpu' attestaion")
95 | exit(1)
96 | try:
97 | user_data_bytes = base64.b64decode(args.user_data)
98 | except Exception as err:
99 | print(f"Error while base64 decoding of user data: : {err}")
100 | exit(1)
101 | else:
102 | user_data_bytes = b""
103 |
104 | # Read ITA Env configuration from json file
105 | with open(args.config, 'r') as cf:
106 | cf_dict = json.load(cf)
107 |
108 | trustauthority_base_url = cf_dict['trustauthority_base_url']
109 | if trustauthority_base_url is None:
110 | print("TRUSTAUTHORITY_BASE_URL is not set.")
111 | exit(1)
112 |
113 | trustauthority_api_url = cf_dict['trustauthority_api_url']
114 | if trustauthority_api_url is None:
115 | print("TRUSTAUTHORITY_API_URL is not set.")
116 | exit(1)
117 |
118 | trustauthority_api_key = cf_dict['trustauthority_api_key']
119 | if trustauthority_api_key is None:
120 | print("TRUSTAUTHORITY_API_KEY is not set.")
121 | exit(1)
122 |
123 | cf.close()
124 |
125 | try:
126 | config_obj = config.Config(
127 | config.RetryConfig(
128 | int(const.DEFAULT_RETRY_WAIT_MIN_SEC),
129 | int(const.DEFAULT_RETRY_WAIT_MAX_SEC),
130 | int(const.DEFAULT_RETRY_MAX_NUM),
131 | int(const.DEFAULT_CLIENT_TIMEOUT_SEC),
132 | ),
133 | trustauthority_base_url,
134 | trustauthority_api_url,
135 | trustauthority_api_key,
136 | )
137 | except ValueError as exc:
138 | log.error(f"Value Error in config object creation : {exc}")
139 | exit(1)
140 |
141 | if config_obj == None:
142 | log.error("Error in config() instance initialization")
143 | exit(1)
144 |
145 | ita_connector = connector.ITAConnector(config_obj)
146 |
147 | if args.attest_type == 'tdx':
148 | tdx_adapter = TDXAdapter(user_data_bytes)
149 | tdx_attest_args = connector.AttestArgs(tdx_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds)
150 | gpu_attest_args = None
151 | elif args.attest_type =='nvgpu':
152 | gpu_adapter = GPUAdapter()
153 | gpu_attest_args = connector.AttestArgs(gpu_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds)
154 | tdx_attest_args = None
155 | elif args.attest_type =='tdx+nvgpu':
156 | tdx_adapter = TDXAdapter(user_data_bytes)
157 | gpu_adapter = GPUAdapter()
158 | tdx_attest_args = connector.AttestArgs(tdx_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds)
159 | gpu_attest_args = connector.AttestArgs(gpu_adapter, args.token_sign_alg, args.policy_must_match, args.request_id, policyIds)
160 | else:
161 | print("Attestation Type %s is unknown.", args.attest_type)
162 | exit(1)
163 |
164 | response = ita_connector.attest_v2(tdx_attest_args, gpu_attest_args)
165 |
166 | if response is None:
167 | print("Attestation Token is not returned.")
168 | return None
169 |
170 | if response.headers:
171 | response_headers_json = json.loads(response.headers.replace("'", '"'))
172 | trace_id = response_headers_json.get("trace-id")
173 | print(f"Trace Id: {trace_id}")
174 | request_id = response_headers_json.get("request-id")
175 | if request_id != None:
176 | print(f"Request Id: {request_id}")
177 |
178 | token = response.token
179 | print(f"Attestation token : {token}")
180 |
181 |
182 | def cmd_verify(args):
183 | if args.token == None:
184 | print("Token is not provided")
185 | exit(1)
186 |
187 | with open(args.config, 'r') as cf:
188 | cf_dict = json.load(cf)
189 |
190 | trustauthority_base_url = cf_dict['trustauthority_base_url']
191 | if trustauthority_base_url is None:
192 | print("TRUSTAUTHORITY_BASE_URL is not set.")
193 | exit(1)
194 |
195 | trustauthority_api_url = cf_dict['trustauthority_api_url']
196 | if trustauthority_api_url is None:
197 | print("TRUSTAUTHORITY_API_URL is not set.")
198 | exit(1)
199 |
200 | trustauthority_api_key = cf_dict['trustauthority_api_key']
201 | if trustauthority_api_key is None:
202 | print("TRUSTAUTHORITY_API_KEY is not set.")
203 | exit(1)
204 |
205 | cf.close()
206 |
207 | try:
208 | config_obj = config.Config(
209 | config.RetryConfig(
210 | int(const.DEFAULT_RETRY_WAIT_MIN_SEC),
211 | int(const.DEFAULT_RETRY_WAIT_MAX_SEC),
212 | int(const.DEFAULT_RETRY_MAX_NUM),
213 | int(const.DEFAULT_CLIENT_TIMEOUT_SEC),
214 | ),
215 | trustauthority_base_url,
216 | trustauthority_api_url,
217 | trustauthority_api_key,
218 | )
219 | except ValueError as exc:
220 | log.error(f"Value Error in config object creation : {exc}")
221 | exit(1)
222 |
223 | if config_obj == None:
224 | log.error("Error in config() instance initialization")
225 | exit(1)
226 |
227 | ita_connector = connector.ITAConnector(config_obj)
228 |
229 | try:
230 | verified_token = ita_connector.verify_token(args.token)
231 | except Exception as exc:
232 | verified_token = None
233 | print(f"Token verification returned exception : {exc}")
234 | if verified_token != None:
235 | print("Token Verification Successful")
236 | print(f"Verified Attestation Token : {verified_token}")
237 | else:
238 | print("Token Verification failed")
239 | exit(1)
240 |
241 |
242 | def main():
243 | parser = argparse.ArgumentParser(description="Trust Authority Python CLI")
244 | subparsers = parser.add_subparsers(title="Commands", dest="command", help="Command to execute")
245 |
246 | # evidence command
247 | parser_evidence = subparsers.add_parser("evidence", help="Evidence command")
248 | parser_evidence.add_argument('-a', '--attest-type', choices=['tdx', 'nvgpu'], required=True, help='Attestation Type selection')
249 | parser_evidence.add_argument('-n', '--nonce', help="Nonce in base64 encoded format")
250 | parser_evidence.add_argument('-u', '--user-data', help="User Data in base64 encoded format (*Use for 'tdx' or 'tdx+nvgpu' attestation)")
251 | parser_evidence.set_defaults(func=cmd_evidence)
252 |
253 | # attest command
254 | parser_attest = subparsers.add_parser("attest", help="Attest command")
255 | parser_attest.add_argument('-a', '--attest-type', choices=['tdx','nvgpu','tdx+nvgpu'], required=True, help="Attestation Type selection")
256 | parser_attest.add_argument('-c', '--config', type=str, required=True, help="Trust Authority config in JSON format")
257 | parser_attest.add_argument('-u', '--user-data', help="User Data in base64 encoded format (*Use for 'tdx' or 'tdx+nvgpu' attestation)")
258 | parser_attest.add_argument('-p', '--policy-ids', help="Trust Authority Policy Ids, comma separated without space")
259 | parser_attest.add_argument('-r', '--request-id', help="Request id to be associated with request")
260 | parser_attest.add_argument('-s', '--token-sign-alg', choices=["RS256","PS384"], help="Token Signing Algorithm to be used, support PS384 and RS256")
261 | parser_attest.add_argument('--policy-must-match', default=False, action="store_true", help="Enforce policies match during attestation")
262 | parser_attest.set_defaults(func=cmd_attest)
263 |
264 | # verify command
265 | parser_verify = subparsers.add_parser("verify", help="Verify command")
266 | parser_verify.add_argument('-c', '--config', type=str, required=True, help="ITA environment argument")
267 | parser_verify.add_argument('-t', '--token', type=str, required=True, help="Token in JWT format")
268 | parser_verify.set_defaults(func=cmd_verify)
269 |
270 | if len(sys.argv)==1:
271 | parser.print_help(sys.stderr)
272 | sys.exit(1)
273 | else:
274 | args = parser.parse_args()
275 | args.func(args)
276 |
277 |
278 | if __name__ == '__main__':
279 | main()
280 |
--------------------------------------------------------------------------------
/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® Tiber™ 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/articles/ita/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/articles/ita/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.9 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/sgx_sample_app/README.md:
--------------------------------------------------------------------------------
1 | # Intel SGX Attestation Sample Application
2 |
3 | · 05/21/2025 ·
4 |
5 | The Intel® Software Guard Extensions (Intel® SGX) attestation sample app is a Python application that uses the Intel® Tiber™ 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/articles/ita/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/articles/ita/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.9 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------