OPTEE persistent-object
Table of Contents
Description
PersistentObject顾名思议,是可以持久化保存的对象,下面简单分析一下实现的原理。
Secure storage
首先,看一下optee如何处理secure数据的存储,加密。
optee官方文档:https://optee.readthedocs.io/en/latest/architecture/secure_storage.html#ree-fs-secure-storage
系统框架:

由上图可知,整个流程大致为:TEE File System在保存文件时,首先通过tee中的文件系统接口调用linux内核中的TEE driver,TEE driver再调用内核态的TEE Supplicant来完成文件系统的访问。最终的文件存储操作是通过ree的文件系统来存储,所以只要ree可以读写文件,这条链路就可以正常运行。
Usage (secure storage)
userspace
- create session
write secure object
TEEC_Result write_secure_object(struct test_ctx *ctx, char *id,char *data, size_t data_len){TEEC_Operation op;uint32_t origin;TEEC_Result res;size_t id_len = strlen(id);memset(&op, 0, sizeof(op));op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,TEEC_MEMREF_TEMP_INPUT,TEEC_NONE, TEEC_NONE);op.params[0].tmpref.buffer = id;op.params[0].tmpref.size = id_len;op.params[1].tmpref.buffer = data;op.params[1].tmpref.size = data_len;res = TEEC_InvokeCommand(&ctx->sess,TA_SECURE_STORAGE_CMD_WRITE_RAW,&op, &origin);if (res != TEEC_SUCCESS)printf("Command WRITE_RAW failed: 0x%x / %u\n", res, origin);switch (res) {case TEEC_SUCCESS:break;default:printf("Command WRITE_RAW failed: 0x%x / %u\n", res, origin);}return res;}这里传递了两个参数
- object id
字符串类型id - data
要写入的数据
使用invoke调用TA_SECURE_STORAGE_CMD_WRITE_RAW命令
- object id
delete_secure_object
TEEC_Result delete_secure_object(struct test_ctx *ctx, char *id){TEEC_Operation op;uint32_t origin;TEEC_Result res;size_t id_len = strlen(id);memset(&op, 0, sizeof(op));op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,TEEC_NONE, TEEC_NONE, TEEC_NONE);op.params[0].tmpref.buffer = id;op.params[0].tmpref.size = id_len;res = TEEC_InvokeCommand(&ctx->sess,TA_SECURE_STORAGE_CMD_DELETE,&op, &origin);switch (res) {case TEEC_SUCCESS:case TEEC_ERROR_ITEM_NOT_FOUND:break;default:printf("Command DELETE failed: 0x%x / %u\n", res, origin);}return res;}删除操作传入了object id
ta
TA_SECURE_STORAGE_CMD_WRITE_RAW
static TEE_Result create_raw_object(uint32_t param_types, TEE_Param params[4]){.../** Create object in secure storage and fill with data*/obj_data_flag = TEE_DATA_FLAG_ACCESS_READ | /* we can later read the oject */TEE_DATA_FLAG_ACCESS_WRITE | /* we can later write into the object */TEE_DATA_FLAG_ACCESS_WRITE_META | /* we can later destroy or rename the object */TEE_DATA_FLAG_OVERWRITE; /* destroy existing object of same ID */res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,obj_id, obj_id_sz,obj_data_flag,TEE_HANDLE_NULL,NULL, 0, /* we may not fill it right now */&object);if (res != TEE_SUCCESS) {EMSG("TEE_CreatePersistentObject failed 0x%08x", res);TEE_Free(obj_id);return res;}res = TEE_WriteObjectData(object, data, data_sz);if (res != TEE_SUCCESS) {EMSG("TEE_WriteObjectData failed 0x%08x", res);TEE_CloseAndDeletePersistentObject1(object);} else {TEE_CloseObject(object);}...}- TEE_CreatePersistentObject
- TEE_WriteObjectData
- TEE_CreatePersistentObject
TA_SECURE_STORAGE_CMD_DELETE
static TEE_Result delete_object(uint32_t param_types, TEE_Param params[4]){const uint32_t exp_param_types =TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,TEE_PARAM_TYPE_NONE,TEE_PARAM_TYPE_NONE,TEE_PARAM_TYPE_NONE);TEE_ObjectHandle object;TEE_Result res;char *obj_id;size_t obj_id_sz;/** Safely get the invocation parameters*/if (param_types != exp_param_types)return TEE_ERROR_BAD_PARAMETERS;obj_id_sz = params[0].memref.size;obj_id = TEE_Malloc(obj_id_sz, 0);if (!obj_id)return TEE_ERROR_OUT_OF_MEMORY;TEE_MemMove(obj_id, params[0].memref.buffer, obj_id_sz);/** Check object exists and delete it*/res = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE,obj_id, obj_id_sz,TEE_DATA_FLAG_ACCESS_READ |TEE_DATA_FLAG_ACCESS_WRITE_META, /* we must be allowed to delete it */&object);if (res != TEE_SUCCESS) {EMSG("Failed to open persistent object, res=0x%08x", res);TEE_Free(obj_id);return res;}TEE_CloseAndDeletePersistentObject1(object);TEE_Free(obj_id);return res;}- 打开对象
TEE_OpenPersistentObject - 删除对象
TEE_CloseAndDeletePersistentObject1
- 打开对象
从ta可以看到tee中使用了TEE_*PersistentObject的api
TEE PersistentObject
API optee_os/lib/libutee/include/ltee_api.h
/* Data and Key Storage API - Persistent Object Functions */TEE_Result TEE_OpenPersistentObject(uint32_t storageID, const void *objectID,uint32_t objectIDLen, uint32_t flags,TEE_ObjectHandle *object);TEE_Result TEE_CreatePersistentObject(uint32_t storageID, const void *objectID,uint32_t objectIDLen, uint32_t flags,TEE_ObjectHandle attributes,const void *initialData,uint32_t initialDataLen,TEE_ObjectHandle *object);void TEE_CloseAndDeletePersistentObject(TEE_ObjectHandle object);TEE_Result TEE_CloseAndDeletePersistentObject1(TEE_ObjectHandle object);TEE_Result TEE_RenamePersistentObject(TEE_ObjectHandle object,const void *newObjectID,uint32_t newObjectIDLen);TEE_Result TEE_AllocatePersistentObjectEnumerator(TEE_ObjectEnumHandle *objectEnumerator);void TEE_FreePersistentObjectEnumerator(TEE_ObjectEnumHandle objectEnumerator);void TEE_ResetPersistentObjectEnumerator(TEE_ObjectEnumHandle objectEnumerator);TEE_Result TEE_StartPersistentObjectEnumerator(TEE_ObjectEnumHandleobjectEnumerator,uint32_t storageID);TEE_Result TEE_GetNextPersistentObject(TEE_ObjectEnumHandle objectEnumerator,TEE_ObjectInfo *objectInfo,void *objectID, uint32_t *objectIDLen);
这里共有10个api提供了open,new,enum,rename的功能

TEE_CreatePersistentObject
这个函数是对optee_os中系统调用的封装
从上个章节可知最终调用了optee_os中的syscall_storage_obj_create
- 根据storage_id获取文件系统接口 `struct tee_file_operations`
- 根据object_id获取tee_pobj
- 生成新的tee_obj并将其与tee_pobj绑定
- 调用tee_svc_storage_init_file初始化文件
- 将tee_obj返回给用户空间程序(TA)
重点内容:
- TODO pobj
- tee_svc_storage_init_file
- 将tee_obj_attr保存在文件中
...if (attr_o) {res = tee_obj_set_type(o, attr_o->info.objectType,attr_o->info.maxKeySize);if (res)return res;res = tee_obj_attr_copy_from(o, attr_o);if (res)return res;o->have_attrs = attr_o->have_attrs;o->info.objectUsage = attr_o->info.objectUsage;o->info.keySize = attr_o->info.keySize;res = tee_obj_attr_to_binary(o, NULL, &attr_size);if (res)return res;if (attr_size) {attr = malloc(attr_size);if (!attr)return TEE_ERROR_OUT_OF_MEMORY;res = tee_obj_attr_to_binary(o, attr, &attr_size);if (res != TEE_SUCCESS)goto exit;}} else {res = tee_obj_set_type(o, TEE_TYPE_DATA, 0);if (res != TEE_SUCCESS)goto exit;}...o->ds_pos = sizeof(struct tee_svc_storage_head) + attr_size;/* write head */head.attr_size = attr_size;head.keySize = o->info.keySize;head.maxKeySize = o->info.maxKeySize;head.objectUsage = o->info.objectUsage;head.objectType = o->info.objectType;head.have_attrs = o->have_attrs;res = fops->create(o->pobj, !!(o->flags & TEE_DATA_FLAG_OVERWRITE),&head, sizeof(head), attr, attr_size, data, len,&o->fh);if (!res)o->info.dataSize = len;代码中调用了两次tee_obj_attr_to_binary,第一次为了获取大小,第二次保存
调用fops->create创建对象
fops table: tee_svc_storage_file_ops
storage_id fops TEE_STORAGE_PRIVATE ree_fs_ops/rpmb_fs_ops TEE_STORAGE_PRIVATE_REE ree_fs_ops CFG_RPMB_FS rpmb_fs_ops
- 将tee_obj_attr保存在文件中
All Operation flow

从上面的流程可以看到,系统在访问文件时,需要通过一系列的流程,最后通过ree中的tee_supplicant服务来完成最终的文件访问操作,系统会在TEE中对文件进行加解密的操作,这样ree就无法获取tee中保存的文件的内容,从而安全的存储我们所需要的数据。
在此基础上,TEE还对数据进行了加密,hash校验,防止ree有机会去窜改数据。
