Secure storage

Applications need to store and read data, trusted applications are no different. The Trustonic TEE supports secure storage.  Using functions similar to fopen, fclose, read, write, and seek, a Trusted Application can put object data into files.

The individual file size is exposed to the normal world, but the object contents and identifier are encrypted. Integrity checking is provided - the normal world may be able to modify the object store, but any such modification is detected and reported to the application via the error code TEE_ERROR_CORRUPT_OBJECT.

This gives the application developer a safe place to store data, a cryptographic key, sensitive configuration or user private data.

In this blog, we show how to use secure storage, and builds apon our hello secure world example.

Normal world

For the purposes of illustrating secure storage, our normal world client application is largely unchanged, we just add a new command to tell the trusted application to perform some secure storage operations:

caSkel.c:

TEEC_Result caDo()
{
    TEEC_Result    nError;
    TEEC_Operation sOperation;

    LOG_D("[CASkel] %s", __func__);

    memset(&sOperation, 0, sizeof(TEEC_Operation));
    sOperation.paramTypes = TEEC_PARAM_TYPES(
                                TEEC_MEMREF_TEMP_INPUT,
                                TEEC_MEMREF_TEMP_OUTPUT,
                                TEEC_NONE,
                                TEEC_NONE);

    nError = TEEC_InvokeCommand(session,
                                CMD_SKEL_SECURE_STORAGE,
                                &sOperation,       /* IN OUT operation */
                                NULL               /* OUT returnOrigin, optional */
                               );

    if (nError != TEEC_SUCCESS) {
        LOG_E("[CASkel] %s: TEEC_InvokeCommand failed (%08x), %d", __func__, nError, __LINE__);
    }

    return nError;
}

 

and we add this new command to the service-specific command identifiers in the trusted application's public header file, taSkel.h:

#define CMD_SKEL_SECURE_STORAGE 2

 

Secure world

For the secure world application, we'll modify the hello secure world functionality to write that string to a file, read it back and display it.

Firstly, we need our TA to understand the new CMD_SKEL_SECURE_STORAGE command from the normal world. So we modify ta.c and add:

TEE_Result TA_EXPORT TA_InvokeCommandEntryPoint(IN OUT void* pSessionContext,
                                                uint32_t nCommandID,
                                                uint32_t nParamTypes,
                                                TEE_Param pParams[4])
{
    TEE_Result ret;
    TEE_DbgPrintLnf(TATAG "TA_InvokeCommandEntryPoint");

    switch(nCommandID)
    {
        ...
        case CMD_SKEL_SECURE_STORAGE:
            return ssHelloWorld();
        default:
        TEE_DbgPrintLnf(TATAG "invalid command ID: 0x%X", nCommandID);
        return TEE_ERROR_BAD_PARAMETERS;
    }
}

 

and we add a new function:

static TEE_Result ssHelloWorld(void) { ... }

 

Using secure storage

The cosmetic changes have been made to our client and trusted application. It's time to add the secure storage code to the ssHelloWorld function.

We want to:

  • Save the data "hello secure world" to the secure file system
  • Read this data back
  • Display the message

To do this, we implement the ssHelloWorld function that's called on CMD_SKEL_SECURE_STORAGE commands from the normal world.

static TEE_Result ssHelloWorld(void)
{
    TEE_Result nResult = TEE_SUCCESS;
    TEE_ObjectHandle object = TEE_HANDLE_NULL;
    unsigned char initialData[] = "hello secure world";
    unsigned char IDNameObject[] = "myfile";
    uint32_t IDNameObjectsize = 6;
    char dataRead[18];
    uint32_t count;

    TEE_DbgPrintLnf(TATAG "-- Create persistent file --");
    nResult = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,
                                         IDNameObject,
                                         IDNameObjectsize,
                                         TEE_HANDLE_FLAG_PERSISTENT,
                                         NULL,
                                         initialData,
                                         sizeof(initialData),
                                         &object);
    if (nResult != TEE_SUCCESS) {
        TEE_DbgPrintLnf(TATAG "TEE_CreatePersistentObject (%08x).", nResult);
        return nResult;
    }

    TEE_DbgPrintLnf(TATAG "-- Close persistent file --");
    TEE_CloseObject(object);

    TEE_DbgPrintLnf(TATAG " -- Open persistent file --");
    object = TEE_HANDLE_NULL;
    nResult = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE,
                                       IDNameObject, IDNameObjectsize,
                                       TEE_HANDLE_FLAG_PERSISTENT | TEE_DATA_FLAG_ACCESS_READ,
                                       &object);
    if (nResult != TEE_SUCCESS) {
        TEE_DbgPrintLnf(TATAG "TEE_OpenPersistentObject (%08x).", nResult);
        return nResult;
    }

    TEE_DbgPrintLnf(TATAG "-- Read persistent file --");
    nResult = TEE_ReadObjectData(object, dataRead, sizeof(initialData), &count);
    if (nResult != TEE_SUCCESS) {
        TEE_DbgPrintLnf(TATAG "TEE_ReadObjectData (%08x).", nResult);
    }
    else {
        TEE_DbgPrintLnf(TATAG "Secure storage says \"%s\"", dataRead);
    }

    TEE_CloseObject(object);

    return nResult;
}

 

Our trusted application is complete, so we can cross compile it and deploy to our device. I can see debug traces from the trusted application using dmesg, or via putty on a development board.