5.27. Read object with Attestation

This example demonstrates how to read an object with attestation and parse the attested data to check various object attributes.

In this example, we use an EC NIST-P 256 keypair as the attestation key and a binary object which will be attested.

Note

The maximum size of a binary object that can be attested at a time is 500 bytes. The API available will only work on binary objects with size up to 500 bytes. To perform attestation on an object of greater size, we need to call corresponding Se05x API in a loop, verifying the obtained signature every time.

A reference implementation is available at Reading large binary objects with attestation

5.27.1. Building

Build the project with the following configurations.

se05x_ReadWithAttestation

  • Project = se05x_ReadWithAttestation

5.27.2. Running

On running the example, you would be able to see object attributes logged on the screen like:

App   :INFO :Running example se05x_ReadWithAttestation
App   :INFO :Type:
App   :INFO :   BINARY_FILE
App   :INFO :Auth:
App   :INFO :   Not Set
App   :INFO :Neg auth count:
App   :INFO :   0x0000
App   :INFO :Owner:
App   :INFO :   0x0000
App   :INFO :Neg auth count max:
App   :INFO :   0x0000
App   :INFO :Auth Object:
App   :INFO :   No authentication required
App   :INFO :Policies:
App   :INFO :   POLICY_OBJ_ALLOW_READ
App   :INFO :   POLICY_OBJ_ALLOW_WRITE
App   :INFO :   POLICY_OBJ_ALLOW_DELETE
App   :INFO :Origin:
App   :INFO :   EXTERNAL
App   :INFO :Example success
App   :INFO :ex_sss Finished

You can see the various attributes associated with the object such as object type, authentication mechanism, origin and policies.

An example of how to perform read with attestation is given below

/* Prepare/init attestation data structure */

sss_se05x_attst_comp_data_t comp_data[2] = {0};
sss_se05x_attst_data_t att_data          = {.valid_number = 2};
/* Random data from the host to check if SE
 * answers to current attestation request and
 * not an older response is used */

/* clang-format off */
uint8_t freshness[16] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };

The data received in att_data variable can be parsed to read the object attributes.

5.27.3. Reading large binary objects with attestation

Following is an example code on how to read a large binary file with attestation.

Note

This is required only when reading binary objects of size larger than 500 bytes. For any other case, you should use SSS API as above

sss_status_t read_large_object_with_attestation(ex_sss_boot_ctx_t *pCtx,
    sss_se05x_key_store_t *keyStore,
    sss_se05x_object_t *keyObject,
    uint8_t *key,
    size_t *keylen,
    size_t *pKeyBitLen,
    sss_se05x_object_t *keyObject_attst,
    sss_algorithm_t algorithm_attst,
    uint8_t *random_attst,
    size_t randomLen_attst,
    sss_se05x_attst_data_t *attst_data)
{
    smStatus_t status       = SM_NOT_OK;
    sss_status_t sss_status = kStatus_SSS_Fail;
    uint16_t rem_data       = 0;
    uint16_t offset         = 0;
    uint16_t size           = 0;
    size_t max_buffer       = 0;
    size_t signatureLen     = 0;
    uint32_t attestID;
    SE05x_AttestationAlgo_t attestAlgo = kSE05x_AttestationAlgo_EC_SHA_256;
    attestID                           = keyObject_attst->keyId;
    /* Variables for verification */
    sss_object_t verification_object = {0};
    uint8_t plainData[2500]          = {0};
    size_t plainDataLen              = sizeof(plainData);
    uint8_t digest[64]               = {0};
    size_t digestLen                 = sizeof(digest);
    sss_digest_t digest_ctx;
    sss_algorithm_t algorithm        = kAlgorithm_SSS_ECDSA_SHA256;
    sss_algorithm_t digest_algorithm = kAlgorithm_SSS_SHA256;
    sss_asymmetric_t verify_ctx;

    if (kStatus_SSS_Success != create_host_public_key(pCtx, attestID, EC_KEY_BIT_LEN, &verification_object)) {
        goto cleanup;
    }

    status = Se05x_API_ReadSize(&keyStore->session->s_ctx, keyObject->keyId, &size);
    ENSURE_OR_GO_CLEANUP(status == SM_OK);

    if (*keylen < size) {
        LOG_E("Insufficient buffer ");
        goto cleanup;
    }

    rem_data = size;
    *keylen  = size;
    while (rem_data > 0) {
        uint16_t chunk = (rem_data > BINARY_WRITE_MAX_LEN) ? BINARY_WRITE_MAX_LEN : rem_data;
        rem_data       = rem_data - chunk;
        max_buffer     = chunk;

        signatureLen                     = attst_data->data[0].signatureLen;
        attst_data->data[0].timeStampLen = sizeof(SE05x_TimeStamp_t);
        status                           = Se05x_API_ReadObject_W_Attst(&keyStore->session->s_ctx,
            keyObject->keyId,
            offset,
            chunk,
            attestID,
            attestAlgo,
            random_attst,
            randomLen_attst,
            (key + offset),
            &max_buffer,
            attst_data->data[0].attribute,
            &(attst_data->data[0].attributeLen),
            &(attst_data->data[0].timeStamp),
            attst_data->data[0].outrandom,
            &(attst_data->data[0].outrandomLen),
            attst_data->data[0].chipId,
            &(attst_data->data[0].chipIdLen),
            attst_data->data[0].signature,
            &signatureLen);

        attst_data->data[0].signatureLen -= signatureLen;
        attst_data->valid_number = 1;

        ENSURE_OR_GO_CLEANUP(status == SM_OK);

        /* Perform verification operation here on the following data
         *      (key + offset) +
         *      attst_data->data[0].attribute +
         *      attst_data->data[0].timestamp +
         *      attst_data->data[0].outrandom +
         *      attst_data->data[0].chipId
         * with signature
         *      attst_data->data[0].signature
         *
         * We perform signature verification on host.
         * First we digest the data then pass it to verify API
         */

        memcpy(plainData, (key + offset), max_buffer);
        memcpy(plainData + max_buffer, attst_data->data[0].attribute, attst_data->data[0].attributeLen);
        memcpy(plainData + max_buffer + attst_data->data[0].attributeLen,
            &(attst_data->data[0].timeStamp),
            attst_data->data[0].timeStampLen);
        memcpy(plainData + max_buffer + attst_data->data[0].attributeLen + attst_data->data[0].timeStampLen,
            attst_data->data[0].outrandom,
            attst_data->data[0].outrandomLen);
        memcpy(plainData + max_buffer + attst_data->data[0].attributeLen + attst_data->data[0].timeStampLen +
                   attst_data->data[0].outrandomLen,
            attst_data->data[0].chipId,
            attst_data->data[0].chipIdLen);
        plainDataLen = max_buffer + attst_data->data[0].attributeLen + attst_data->data[0].timeStampLen +
                       attst_data->data[0].outrandomLen + attst_data->data[0].chipIdLen;

        sss_status = sss_digest_context_init(&digest_ctx, &pCtx->host_session, digest_algorithm, kMode_SSS_Digest);
        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        sss_status = sss_digest_one_go(&digest_ctx, plainData, plainDataLen, digest, &digestLen);
        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        sss_digest_context_free(&digest_ctx);

        /* Verify signature */
        sss_asymmetric_context_init(
            &verify_ctx, &pCtx->host_session, &verification_object, algorithm, kMode_SSS_Verify);

        sss_status =
            sss_asymmetric_verify_digest(&verify_ctx, digest, digestLen, attst_data->data[0].signature, signatureLen);

        sss_asymmetric_context_free(&verify_ctx);

        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        offset = offset + chunk;
    }

cleanup:
    return sss_status;
}