5.7.13. 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 pre-provisioned EC/RSA keypair as the attestation key and a binary object which will be attested. The pre-provisioned attestation key for Se05x A & C EC keypair is at 0xF0000012 and for Se05x B RSA keypair is at 0xF0000010

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

Build the project with the following configurations.

  • Build Plug & Trust middleware stack. (Refer Building / Compiling)

  • Project: se05x_ReadWithAttestation

5.7.13.2. Running

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

App   :INFO :Key att data (Len=28)
      00 F5 EF FA    0B 01 00 00    00 00 00 00    00 00 08 00
      00 00 00 00    34 00 00 01    00 00 00 00
App   :INFO :Object Id 0xF5EFFA
App   :INFO :Object Type  0xB
App   :INFO :Type:
App   :INFO :   BINARY_FILE
App   :INFO :Object Auth Attribute  0x1
App   :INFO :Auth:
App   :INFO :   Not Set
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 :tagLen for AEAD:0x00
App   :INFO :RFU bytes:0x00
App   :INFO :Owner:0x0000
App   :INFO :Object origin : 0x1
App   :INFO :Origin:
App   :INFO :   EXTERNAL
App   :INFO :Object Version : 0x0000
App   :INFO :se05x_ReadWithAttestation 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.7.13.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(sss_se05x_key_store_t *keyStore,
    sss_se05x_object_t *keyObject,
    uint8_t *key,
    size_t *keylen,
    size_t *pKeyBitLen,
    sss_algorithm_t algorithm_attst,
    uint8_t *random_attst,
    size_t randomLen_attst,
    sss_se05x_attst_data_t *attst_data,
    sss_object_t *verification_object)
{
    AX_UNUSED_ARG(pKeyBitLen);
    AX_UNUSED_ARG(algorithm_attst);
    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                  = ATTESTATION_KEY_ID;
    SE05x_AttestationAlgo_t attestAlgo = kSE05x_AttestationAlgo_EC_SHA_256;

    /* Variables for verification */
    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 SSS_HAVE_SE05X_VER_GTE_07_02
    uint8_t *pData        = &plainData[0];
    uint8_t cmdHashed[32] = {0};
    size_t cmdHashedLen   = sizeof(cmdHashed);
    int tlvRet            = 0;
    uint8_t timestamp[32] = {0};
#endif

    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);
#if !SSS_HAVE_SE05X_VER_GTE_07_02
        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);
#else

        attst_data->data[0].cmdLen       = sizeof(attst_data->data[0].cmd);
        attst_data->data[0].objSizeLen   = sizeof(attst_data->data[0].objSize);
        attst_data->data[0].attributeLen = sizeof(attst_data->data[0].attribute);
        attst_data->data[0].chipIdLen    = sizeof(attst_data->data[0].chipId);
        pData                            = &plainData[0];
        status                           = Se05x_API_ReadObject_W_Attst_V2(&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].chipId,
            &(attst_data->data[0].chipIdLen),
            attst_data->data[0].cmd,
            &(attst_data->data[0].cmdLen),
            attst_data->data[0].objSize,
            &(attst_data->data[0].objSizeLen),
            attst_data->data[0].signature,
            &signatureLen);
#endif

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

        ENSURE_OR_GO_CLEANUP(status == SM_OK);
        //#if !SSS_HAVE_SE05X_VER_GTE_07_02
        /* 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);
#if !SSS_HAVE_SE05X_VER_GTE_07_02
        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;
#else
        /* Perform verification operation here on the following data
        *      Perform hash on capdu +
        *      Tag(0x41) + Length + data +
        *      Tag(0x42) + Length +att_data.data[0].chipId +
        *      Tag(0x43) + Length +att_data.data[0].attribute +
        *      Tag(0x44) + Length +dataLen +
        *      Tag(0x4F) + Length +att_data.data[0].timestamp
        * We perform signature verification on host.
        * First we digest the data then pass it to verify API with recv signature
        */

        sss_status = sss_digest_context_init(
            &digest_ctx, verification_object->keyStore->session, digest_algorithm, kMode_SSS_Digest);
        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        LOG_MAU8_I("Cmd ", attst_data->data[0].cmd, attst_data->data[0].cmdLen);

        sss_status = sss_digest_one_go(
            &digest_ctx, attst_data->data[0].cmd, (attst_data->data[0].cmdLen), cmdHashed, &cmdHashedLen);
        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        LOG_MAU8_I("Cmd Hashed ", cmdHashed, cmdHashedLen);
        sss_digest_context_free(&digest_ctx);
        plainDataLen = 0;

        memcpy(pData, cmdHashed, cmdHashedLen);
        plainDataLen = plainDataLen + cmdHashedLen;
        pData        = pData + plainDataLen;

        tlvRet = add_taglength_to_data(&pData, &plainDataLen, kSE05x_TAG_1, (key + offset), max_buffer, true);
        ENSURE_OR_GO_CLEANUP(tlvRet == 0);
        tlvRet = add_taglength_to_data(
            &pData, &plainDataLen, kSE05x_TAG_2, attst_data->data[0].chipId, attst_data->data[0].chipIdLen, true);
        ENSURE_OR_GO_CLEANUP(tlvRet == 0);
        tlvRet = add_taglength_to_data(
            &pData, &plainDataLen, kSE05x_TAG_3, attst_data->data[0].attribute, attst_data->data[0].attributeLen, true);
        ENSURE_OR_GO_CLEANUP(tlvRet == 0);
        tlvRet = add_taglength_to_data(
            &pData, &plainDataLen, kSE05x_TAG_4, attst_data->data[0].objSize, attst_data->data[0].objSizeLen, true);
        ENSURE_OR_GO_CLEANUP(tlvRet == 0);

        memcpy(timestamp, &(attst_data->data[0].timeStamp), attst_data->data[0].timeStampLen);

        tlvRet = add_taglength_to_data(
            &pData, &plainDataLen, kSE05x_TAG_TIMESTAMP, timestamp, attst_data->data[0].timeStampLen, true);
        ENSURE_OR_GO_CLEANUP(tlvRet == 0);

        LOG_MAU8_I("plainData ", plainData, plainDataLen);

#endif
        sss_status = sss_digest_context_init(
            &digest_ctx, verification_object->keyStore->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_status = sss_asymmetric_context_init(
            &verify_ctx, verification_object->keyStore->session, verification_object, algorithm, kMode_SSS_Verify);
        ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);

        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;
}