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(&timestamp);
 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_SUCCESS if the operation completes successfully,

  • Otherwise, an appropriate TEE error code indicating failure.

Step-by-step Implementation Details

  1. Input Validation

    The function begins by checking if the pointers nonce and report_out are valid to avoid null dereferences.

  2. 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.

  3. 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.

  4. Prepare Data for Hashing

    The data to be hashed is constructed by concatenating:

    • The TA’s UUID (UUID_SIZE bytes),

    • The current counter value (uint64_t),

    • The current timestamp (uint32_t),

    • The verifier’s nonce (NONCE_SIZE bytes).

    This concatenated data serves as the core attestation payload.

  5. 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.

  6. 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.

  7. 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.

  8. Set Signing Key

    The RSA key handle is attached to the operation context.

  9. 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.

  10. Convert Binary Data to Hexadecimal

The nonce, hash, and signature are each converted into hexadecimal string representations for easy transmission and human-readable logging.

  1. 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.