Attestation Implementation
This section details the implementation of the attestation report generation process inside the Trusted Execution Environment (TEE). Attestation is a critical security function that enables a remote verifier to confirm the integrity and authenticity of the Trusted Application (TA) by verifying cryptographic evidence about its code and state.
—
Overview
The attestation mechanism produces a signed report that includes:
The Trusted Application UUID (universally unique identifier),
A monotonic counter value to prevent replay attacks,
The last counter incremented timestamp,
A nonce (number used once) provided by the verifier to ensure freshness.
The report contains:
The raw data (
UUID+counter+timestamp+nonce),A SHA-256 hash of raw data,
An RSA signature over the hash, proving the TA’s identity and report integrity.
—
Implementation
The attestation report generation is implemented in the function generate_attestation_report().
1TEE_Result get_code_attestation(attestation_report_t *report_out, uint8_t nonce[NONCE_SIZE])
2{
3 if (!nonce || !report_out)
4 return TEE_ERROR_BAD_PARAMETERS;
5
6 TEE_Result res;
7 TEE_ObjectHandle key_handle = TEE_HANDLE_NULL;
8 TEE_OperationHandle sign_op = TEE_HANDLE_NULL;
9 char uuid[UUID_SIZE] = {0};
10 uint32_t flags = TEE_DATA_FLAG_ACCESS_READ;
11 uint64_t counter = 0;
12
13 /* Auxiliary buffers for hash and signature */
14 uint8_t aux_hash[HASH_SIZE];
15 size_t aux_hash_len = sizeof(aux_hash);
16
17 uint8_t aux_signature[RSA_SIGNATURE_SIZE];
18 uint32_t aux_signature_len = sizeof(aux_signature);
19
20 /* Get the TA UUID */
21 TEE_UUID ta_uuid = TA_OFF_CHAIN_SECURE_STORAGE_UUID;
22
23 /* Last counter timestamp */
24 TEE_Time timestamp;
25
26 /* Buffer for nonce hexadecimal representation */
27 char nonce_hex_str[NONCE_SIZE_HEX] = {0};
28 size_t nonce_hex_str_len = sizeof(nonce_hex_str);
29
30 /* Buffer for attestation report hash */
31 char hash_output[HASH_SIZE_HEX + 1] = {0};
32 size_t hash_output_sz = sizeof(hash_output);
33
34 /* Buffer for data to hash */
35 char *data_to_hash = NULL;
36 size_t data_to_hash_sz = 1; /* +1 for null terminator */
37
38 /* Buffer for RSA signature */
39 char signature_output[RSA_SIGNATURE_SIZE_HEX + 1] = {0};
40 size_t signature_output_sz = sizeof(signature_output);
41
42 /* Get current counter value */
43 res = get_counter(&counter);
44 if (res != TEE_SUCCESS)
45 return res;
46
47 /* Get the TA UUID */
48 res = uuid_to_str(uuid, ta_uuid);
49 if (res != TEE_SUCCESS)
50 {
51 EMSG("Failed to get TA UUID, res=0x%08x", res);
52 return res;
53 }
54
55 /* Convert nonce to a hexadecimal string representation */
56 res = convert_to_hex_str(nonce, NONCE_SIZE, nonce_hex_str, nonce_hex_str_len);
57 if (res != TEE_SUCCESS)
58 {
59 EMSG("Failed to convert nonce to hex string, res=0x%08x", res);
60 goto exit;
61 }
62
63 /* Get the last counter timestamp (uint32_t) */
64 res = get_counter_timestamp(×tamp);
65 if (res != TEE_SUCCESS)
66 {
67 EMSG("Failed to get counter timestamp, res=0x%08x", res);
68 return res;
69 }
70
71 /* Prepare data to hash */
72 data_to_hash_sz += snprintf(
73 NULL, 0, /* buffer, buffer_len */
74 "{uuid:%s,counter:%" PRIu64 ",timestamp:%" PRIu32 ",nonce:%s}", /* format string */
75 uuid, counter, timestamp.seconds, nonce_hex_str /* data_to_hash */
76 );
77
78 data_to_hash = TEE_Malloc(data_to_hash_sz, 0);
79 if (!data_to_hash)
80 {
81 EMSG("Failed to allocate memory for data to hash");
82 res = TEE_ERROR_OUT_OF_MEMORY;
83 goto exit;
84 }
85
86 /* Fill the data to hash buffer */
87 snprintf(
88 data_to_hash, data_to_hash_sz, /* buffer, buffer_len */
89 "{uuid:%s,counter:%" PRIu64 ",timestamp:%" PRIu32 ",nonce:%s}", /* format string */
90 uuid, counter, timestamp.seconds, nonce_hex_str /* data_to_hash */
91 );
92
93 /* Compute SHA256 hash of the data */
94 res = compute_sha256(data_to_hash, data_to_hash_sz - 1, aux_hash, &aux_hash_len);
95 if (res != TEE_SUCCESS)
96 {
97 EMSG("Failed to compute SHA256 hash, res=0x%08x", res);
98 goto exit;
99 }
100
101 /* Open the persistent RSA key pair */
102 res = TEE_OpenPersistentObject(
103 TEE_STORAGE_PRIVATE, /* storageID */
104 RSA_KEYPAIR_STORAGE_NAME, /* objectID */
105 strlen(RSA_KEYPAIR_STORAGE_NAME), /* objectIDLen */
106 flags, /* flags */
107 &key_handle /* object */
108 );
109 if (res != TEE_SUCCESS)
110 {
111 EMSG("Failed to open RSA key pair for signing, res=0x%08x", res);
112 goto exit;
113 }
114
115 /* Prepare operation handle for signing:
116 * - TEE_ALG_RSASSA_PKCS1_V1_5_SHA256 - RSA PKCS#1 v1.5 with SHA-256
117 * - TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256 - RSA PSS with MGF1 and SHA-256 (has random salt)
118 */
119 res = TEE_AllocateOperation(
120 &sign_op, /* operation */
121 TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256, /* algorithm */
122 TEE_MODE_SIGN, /* mode */
123 RSA_KEY_SIZE_BITS /* maxKeySize */
124 );
125 if (res != TEE_SUCCESS)
126 {
127 EMSG("Failed to allocate signing operation, res=0x%08x", res);
128 goto exit;
129 }
130
131 /* Set the key for the operation */
132 res = TEE_SetOperationKey(sign_op, key_handle);
133 if (res != TEE_SUCCESS)
134 {
135 EMSG("Failed to set key for signing operation, res=0x%08x", res);
136 goto exit;
137 }
138
139 /* Sign the SHA256 hash of the report */
140 res = TEE_AsymmetricSignDigest(
141 sign_op, /* operation */
142 NULL, 0, /* params, paramsCount */
143 aux_hash, /* digest */
144 aux_hash_len, /* digestLen */
145 aux_signature, /* signature */
146 &aux_signature_len /* signatureLen */
147 );
148 if (res != TEE_SUCCESS)
149 {
150 EMSG("Failed to sign attestation report, res=0x%08x", res);
151 goto exit;
152 }
153
154 /* Convert report hash to a hexadecimal string representation */
155 res = convert_to_hex_str(aux_hash, aux_hash_len, hash_output, hash_output_sz - 1);
156 if (res != TEE_SUCCESS)
157 {
158 EMSG("Failed to convert report hash to hex string, res=0x%08x", res);
159 goto exit;
160 }
161
162 /* Convert signature to a hexadecimal string representation */
163 res = convert_to_hex_str(aux_signature, aux_signature_len, signature_output, signature_output_sz - 1);
164 if (res != TEE_SUCCESS)
165 {
166 EMSG("Failed to convert signature to hex string, res=0x%08x", res);
167 goto exit;
168 }
169
170 /* Fill the report structure */
171 memcpy(report_out->data, data_to_hash, data_to_hash_sz);
172 memcpy(report_out->hash, hash_output, hash_output_sz);
173 memcpy(report_out->signature, signature_output, signature_output_sz);
174
175exit:
176 if (data_to_hash)
177 TEE_Free(data_to_hash);
178 if (sign_op != TEE_HANDLE_NULL)
179 TEE_FreeOperation(sign_op);
180 if (key_handle != TEE_HANDLE_NULL)
181 TEE_CloseObject(key_handle);
182
183 return res;
184}
Function Parameters:
report_out: Pointer to a structure where the attestation report will be stored.nonce: A random nonce provided by the verifier to ensure the report’s freshness.
Return:
TEE_SUCCESSif the operation completes successfully,Otherwise, an appropriate TEE error code indicating failure.
—
Step-by-step Implementation Details
Input Validation
The function begins by checking if the pointers
nonceandreport_outare valid to avoid null dereferences.Retrieve Monotonic Counter
Calls
get_counter()to fetch the current monotonic counter value, which helps prevent replay attacks by ensuring each attestation is unique and sequential.Retrieve the Last Counter Incremented Timestamp
The last counter incremented timestamp is obtained using the
get_counter_timestamp()function. This timestamp indicates when the counter was last incremented, providing a temporal context for the attestation report.Prepare Data for Hashing
The data to be hashed is constructed by concatenating:
The TA’s UUID (
UUID_SIZEbytes),The current counter value (
uint64_t),The current timestamp (
uint32_t),The verifier’s nonce (
NONCE_SIZEbytes).
This concatenated data serves as the core attestation payload.
Compute SHA-256 Hash
The concatenated data is hashed using the SHA-256 algorithm via the
compute_sha256()function. This fixed-length hash summarizes the attestation data in a secure digest form.Load RSA Private Key
The function opens a persistent RSA key pair stored securely in the TEE persistent storage. This key will be used to sign the hash, providing cryptographic proof of the TA’s identity.
Allocate RSA Signing Operation
Allocates a cryptographic operation handle configured for RSA signature generation using RSASSA-PSS padding with SHA-256, which provides probabilistic signature security.
Set Signing Key
The RSA key handle is attached to the operation context.
Sign the Hash
Using the RSA private key and the allocated operation handle, the SHA-256 hash is signed, producing a signature that guarantees the integrity and origin of the attestation report.
Convert Binary Data to Hexadecimal
The nonce, hash, and signature are each converted into hexadecimal string representations for easy transmission and human-readable logging.
Populate Attestation Report Structure
The final report is filled with:
The raw data:
{uuid:<UUID>,counter:<counter>,timestamp:<timestamp>,nonce:<nonce>}The hexadecimal hash of the raw data,
The hexadecimal signature of the hash.
—
Security Considerations
The attestation report generation takes place entirely within the TEE, preventing tampering from the normal world.
Using a monotonic counter prevents replay attacks.
The last incremented timestamp provides temporal context, ensuring the report is current.
The verifier’s nonce ensures freshness of the attestation.
RSA signatures using RSASSA-PSS with SHA-256 provide strong cryptographic guarantees.