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_ObjectEnumHandle
objectEnumerator,
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有机会去窜改数据。