Intel Trust Authority Python Connector
· 05/16/2024 ·
Intel® Trust Authority Client provides a set of Python libraries for attesting TEEs. Users can import the Python libraries into their application and make REST calls to Intel Trust Authority to get an attestation token containing information about the TEE attestation and verification.
The Intel Trust Authority Python client modules, build, and installation instructions are on GitHub at intel/trustauthority-client-for-python.
ITAConnector class and evidence adapters
The Python client ITAConnector class includes the following methods for attestation and verification:
attest
Collects evidence and requests an attestation token from Intel Trust Authority for clients using a Passport validation model.
get_nonce
Gets a nonce and parses it to JSON.
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.
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.
verify_token
Verifies that an Intel Trust Authority attestation token is properly formatted and signed.
A connector requires a TEE adapter (adapter: EvidenceAdapter in AttestArgs) to collect evidence from the attesting platform. For more information about which TEE adapters are currently supported by the Python connector, see TEE Adapters in the Integration Overview article.
A relying party can use the Python connector without a TEE adapter. The only method that requires a TEE adapter is attest.
API reference
Before you can use the API methods, you must set attributes of the Config object. The following example is from the Intel TDX attestation sample app:
# Populate config object
config_obj = config.Config(
config.RetryConfig(
int(retry_wait_time_min), int(retry_wait_time_max), int(retry_max)
),
trustauthority_base_url, # https://portal.trustauthority.intel.com
trustAuthority_api_url, # https://api.trustauthority.intel.com
trust_authority_api_key, # An attestation API key
)
Note
The portal and API URLs for Intel Trust Authority differ by your region (US vs EU). Use the right URLs for your region:
| Region | BaseUrl | ApiUrl |
|---|---|---|
| World/US | https://portal.trustauthority.intel.com |
https://api.trustauthority.intel.com |
| EU | https://portal.eu.trustauthority.intel.com |
https://api.eu.trustauthority.intel.com |
RetryConfig is optional. If you don't set RetryConfig, the default values defined in inteltrustauthorityclient/resources/constants.py are used.
attest
attest is an all-in-one attestation method that supports the Passport attestation model.
def attest(self, args: AttestArgs) -> AttestResponse
attest gets a nonce, collects evidence from the TEE, and (if validation is successful) returns an attestation token. Attest allows you to supply a list of appraisal policies and a request ID to track the request. If you need to supply user_data with your attestation request you should use get_token directly.
| Parameter/return | Comment |
|---|---|
| AttestArgs.adapter | Required. A reference to an EvidenceAdapter abstract base class, which represents the TEE adapter module. |
| AttestArgs.request_id | An optional request ID for tracking. The request_id is a maximum of 128 characters, including a-z, A-Z, 0-9, and - (hyphen). Special characters are not allowed. |
| AttestArgs.policy_ids | An optional array of one to ten attestation policy IDs. |
| AttestResponse.headers | An array of one or more HTTP response headers. |
| AttestResponse.token | If the attestation request is successful, token contains a JWT in base-64 encoded format. |
get_nonce
Gets a nonce from Intel Trust Authority.
def get_nonce(self, args: GetNonceArgs) -> GetNonceResponse
| Parameter/return | Comment |
|---|---|
| GetNonceResponse.headers | HTTP response headers. |
| GetNonceResponse.nonce | A signed nonce in JSON format. The nonce has three fields: "val", "iat", and "signature". |
Example GetNonceResponse.nonce:
{
"val": "g9QC7VxV0n8dID0zSJeVLSULqYCJuv4iMepby91xukrhXgKrKscGXB5lxmT2s3POjxVOG+fSPCYpOKYWRRWAyQ==",
"iat": "MjAyMi0wOC0yNCAxMjozNjozMi45Mjk3MjIwNzUgKzAwMDAgVVRD",
"signature": "WswVG3rOPJIuVmMNG2GZ6IF4hD+QfuJ/PigIRaHtQitGAHRCRzgtW8+8UbXe9vJfjnapjw7RQyzpT+vPGVpxRSoiBaj54RsedI38K9ubFd3gPvsMlYltgFRSAtb1ViWZxMhL0yA9+xzgv0D+11mpNEz8nt3HK4oALV5EAxqJYCmKZRzi3/LJe842AY8DVcV9eUZQ8RBx7gNe72Ex1fU3+qF9A9MuOgKqJ41/7HFTY0rCpcBS8k6E1VBSatk4XTj5KNcluI3LoAOvBuiwObgmNKT8Nyc4JAEc+gmf9e9taIgt7QNFEtl3nwPQuiCLIh0FHdXPYumiQ0mclU8nfQL8ZUoe/GqgOd58+fZoHeGvFoeyjQ7Q0Ini1rWEzwOY5gik9yH57/JTEJTI8Evc0L8ggRO4M/sZ2ZTyIq5yRUISB2eDh6qTfbKgSr5LpxW8IRl0y9fp8CEuzhFxKcOeld9p61yb040P+QhemhP/O1E5tf4y4Pz/ISASiKUBFSTh4yYx",
}
get_token
Gets an attestation token (JWT) from Intel Trust Authority.
def get_token(self, args: GetTokenArgs) -> GetTokenResponse
| Parameter/return | Comment |
|---|---|
| GetTokenArgs.nonce | Optional. A VerifierNonce object (JSON format) that contains a nonce received from Intel Trust Authority. |
| GetTokenArgs.evidence | Required. An Evidence object returned by a TEE adapter's collect_evidence() method, or (in background-check attestation mode) evidence received from the attester. |
| GetTokenArgs.policy_ids | An optional array of one to ten attestation policy IDs. |
| GetTokenArgs.request_id | An optional request ID for tracking. The request_id is a maximum of 128 characters, including a-z, A-Z, 0-9, and - (hyphen). Special characters are not allowed. |
| GetTokenResponse.token | Contains a JWT in base-64 encoded format. |
| GetTokenResponse.headers | An array of one or more HTTP response headers. |
get_token calls the Intel Trust Authority REST API to request an attestation token. get_token can be used in passport or background-check attestation models, with or without a TEE adapter. The attest method provides an example of the steps your code should take before calling get_token in passport mode. Unlike attest, get_token doesn't need a TEE adapter, which allows a relying party operating in background-check mode to request an attestation token by using evidence provided by an attester.
To include user data, such as a public key for secure key release or similar use case, you must pass the user_data to the evidence adapter when you create a new adapter object.
get_token_signing_certificates
Gets the list of attestation token signing certificates from https://<base_url>/certs.
def get_token_signing_certificates(self)
The following example attempts to retrieve the JWKS from Intel Trust Authority. In a later step (not shown here), the kid retrieved from the attestation token header is used to search the JWKS for the certificate that was used to sign the token.
jwks_data = self.get_token_signing_certificates()
if jwks_data == None:
log.error(
"Unable to get token signing certificates from Intel Trust Authority."
)
return None
verify_token
Verifies that the token is well-formed and the signing certificate can be traced to the issuing authority.
def verify_token(self, token)
The following example attempts to verify an attestation token received from the attest method. If verification fails, the error is logged. If verification is successful, the verified token is saved to the log.
# verify the attestation token received from Intel Trust Authority
try:
verified_token = ita_connector.verify_token(token)
except Exception as exc:
log.error(f"Token verification returned an exception : {exc}")
if verified_token != None:
log.info("Token verification succeeded")
log.info(f"Verified attestation token : {verified_token}")
else:
log.info("Token verification failed")
TEE adapters
As mentioned in the introduction, there are currently four Python language TEE adapters available. All of the TEE adapters present an identical API interface to the connector, so that several platforms can be supported by the same code base.
The Intel TDX attestation sample application includes code to instantiate the correct adapter for the current platform at run time. This allows the sample to run on all four Intel TDX platforms without any modifications, except for setting an environment variable. The following code sample from the Intel TDX example shows how to create the appropriate adapter and pass user data to include in the attestation token.
# Create TDX Adapter
user_data = "data generated inside tee"
adapter_type = os.getenv("ADAPTER_TYPE")
if adapter_type is None:
log.error("ADAPTER_TYPE is not set.")
exit(1)
adapter = None
if(adapter_type == const.INTEL_TDX_ADAPTER):
adapter = TDXAdapter(user_data)
elif(adapter_type == const.AZURE_TDX_ADAPTER):
adapter = AzureTDXAdapter(user_data)
elif(adapter_type == const.GCP_TDX_ADAPTER):
adapter = GCPTDXAdapter(user_data)
else:
log.error("Invalid Adapter Type Selected.")
exit(1)
As shown in the preceding code, you have the option to provide user_data when creating a new adapter object. User data can be binary or JSON, and must always be encoded as either Base64 or Base64url. The maximum size allowed is 1Mb. The user data is concatenated with the nonce (if provided), hashed, and the resulting extended hash is saved in the report_data attribute in the evidence, and then output in the JWT
It’s important to note that user_data is known by several names, and clarification is necessary. The following terms all refer to the data provided to the evidence adapter during object instantiation:
- In the REST API request body as the runtime_data parameter. Evidence.user_data maps to the attestation request runtime_data field.
- In the client connector source code (across all languages) it's referred to as user_data or uData, depending on language convention.
- In the attestation token attester_held_data output claim if user data is provided in binary forma, and as attester_user_data output claim if the user data is provided in JSON format.
Note that user data will appear in only one output claim in the attestation token. A single claim can't output both binary and JSON due to data rules, which is why there are two output claims for user_data/runtime_data.
collect_evidence
The collect_evidence method is the same for all adapters. A nonce is optional, however, it's a good practice to always include a nonce if your use case allows.
def collect_evidence(self, nonce=None) -> Evidence
collect_evidence returns an Evidence object. The ITAConnector.get_token make_request() method shows how the evidence can be used to construct the REST API attestation request body.