37. OP-TEE中secure stroage------安全存储使用的key的产生

    历经一年多时间的系统整理合补充,《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 》一书得以出版,书中详细介绍了TEE以及系统安全中的所有内容,全书按照从硬件到软件,从用户空间到内核空间的顺序对TEE技术详细阐述,读者可从用户空间到TEE内核一步一步了解系统安全的所有内容,同时书中也提供了相关的示例代码,读者可根据自身实际需求开发TA。

为方便和及时的回复读者对书中或者TEE相关的问题的疑惑(每天必看一次),也为了大家能有一个统一的交流平台。我搭建了一个简单的论坛,网址如下:

https://www.huangtengxq.com/discuz/forum.php

关于您的疑问可在“相关技术讨论“”中发帖,我会逐一回复。也欢迎大家发帖,一起讨论TEE相关的一些有意思的feature。共同交流。同时该论坛中也会添加关于移动端虚拟化的相关技术的板块,欢迎各位共同交流学习

非常感谢在此期间大家的支持以及各位友人的支持和帮助!!!。

若觉得书中内容有错误的地方,欢迎大家指出,私信或者在博文中留言联系方式亦可发邮件至:shuaifengyun@126.com,多谢各位了!!!!我会第一时间处理

在OP-TEE中使用secure storage功能保存的数据都是经过AES加密之后被保存在文件系统或者是RPMB中的,使用AES算法进行加密或者解密的时候需要提供加密使用的key和初始化向量IV值。每个TA在使用secure storage功能保存数据的时候都会生成一个随机数作为IV值,使用FEK的值作为AES的key。而FEK的值是由一系列HMAC操作得到的。FEK值的生成牵扯到SSK和TSK,本文将介绍这些key的使用和生成,key之间的关系如下图所示:

1. SSK(Secure Storage Key)

在每台设备中的SSK的值不一样,在OP-TEE启动的时候会使用chip ID和HUK经过HMAC算法计算获取SSK的值,并见SSK的值保存在一般该值在结构体变量tee_fs_ssk的key成员成,以备生成其他key使用。工厂生产的时候会将HUK写入到OTP/efuse中,并且在normal world端是无法读取到HUK的值的,而chip ID在芯片出厂之后就会被写入到芯片中。

在OP-TEE启动的时候会执行tee_fs_init_key_manager函数,该函数就是用来根据SSK = HMAC(HUK, message)的方式来生成SSK,并保存在tee_fs_ssk的key成员中。该函数的内容如下:

 

1static TEE_Result tee_fs_init_key_manager(void) 2{ 3 int res = TEE_SUCCESS; 4 struct tee_hw_unique_key huk; 5 uint8_t chip_id[TEE_FS_KM_CHIP_ID_LENGTH]; 6 uint8_t message[sizeof(chip_id) + sizeof(string_for_ssk_gen)]; 7 8 /* Secure Storage Key Generation: 9 * 10 * SSK = HMAC(HUK, message) 11 * message := concatenate(chip_id, static string) 12 * */ 13/* 获取HUK的值(该接口的实现与平台有关,不同的芯片具有不同的读取HUK值的方式) */ 14 tee_otp_get_hw_unique_key(&huk); 15 16/* 获取chip ID的值(不同的芯片具有不同的读取chip id值的方式)*/ 17 tee_otp_get_die_id(chip_id, sizeof(chip_id)); 18 19/* 将chip id + string_for_ssk_gen连接后的值保存到message中,string_for_ssk_gen是一个 20静态的字符串,该值被hard code在代码中 */ 21 memcpy(message, chip_id, sizeof(chip_id)); 22 memcpy(message + sizeof(chip_id), string_for_ssk_gen, 23 sizeof(string_for_ssk_gen)); 24 25/* 使用huk的值对message的内容做HMAC运算,将获取到数据作为SSK,保存到tee_fs_ssk 26变量的key成员中 */ 27 res = do_hmac(tee_fs_ssk.key, sizeof(tee_fs_ssk.key), 28 huk.data, sizeof(huk.data), 29 message, sizeof(message)); 30 31/* 标记ssk已经生产 */ 32 if (res == TEE_SUCCESS) 33 tee_fs_ssk.is_init = 1; 34 35 return res; 36} 37

 

2. TSK(Trusted Applicant Storage Key)

 

TSK是用来生成FEK使用到的key,TSK的值由TA的UUID使用SSK作为key,经过HMAC计算获得,类似于HMAC(SSK, UUID)的方式得到TSK的值,在调用tee_fs_fek_crypt函数的时候就会去计算TSK的值。TSK最终会被用来生成FEK,FEK将会在使用secure storage功能保存数据的时候被用来加密数据。

3. FEK(File Encryption Key)

FEK是secure storage用来对数据进行加密使用的AES key,该key在生成文件的时候会使用PRNG来随机产生,产生的FEK会使用TSK进行加密,然后保存到head.enc_fek变量中,一个TA每次在使用secure storage创建一个安全文件时就生成一个随机数作为FEK,也即是每个TA中的每个安全文件都有一个FEK用于加密对应的文件数据。关于FEK值的产生可以简单理解为如下公式,使用的初始化向量IV值为0:

AES_CBC(in_key, TSK)

通过调用tee_fs_fek_crypt函数就能生成一个FEK的值,该函数代码如下:

1TEE_Result tee_fs_fek_crypt(const TEE_UUID *uuid, TEE_OperationMode mode, 2 const uint8_t *in_key, size_t size, 3 uint8_t *out_key) 4{ 5 TEE_Result res; 6 uint8_t *ctx = NULL; 7 size_t ctx_size; 8 uint8_t tsk[TEE_FS_KM_TSK_SIZE]; 9 uint8_t dst_key[size]; 10 11/* 检查输入的用于生成FEK的随机数in_key和用于存放生成的out_key地址是否合法 */ 12 if (!in_key || !out_key) 13 return TEE_ERROR_BAD_PARAMETERS; 14 15/* 检查in_key长度 */ 16 if (size != TEE_FS_KM_FEK_SIZE) 17 return TEE_ERROR_BAD_PARAMETERS; 18 19/* 判定SSK是否已经被初始化 */ 20 if (tee_fs_ssk.is_init == 0) 21 return TEE_ERROR_GENERIC; 22 23/* 如果调用的时候参数uuid不为0,则调用HMAC算法生成TSK。如果UUID的值为0,则 24默认生成TSK使用的原始数据为0 */ 25 if (uuid) { 26 res = do_hmac(tsk, sizeof(tsk), tee_fs_ssk.key, 27 TEE_FS_KM_SSK_SIZE, uuid, sizeof(*uuid)); 28 if (res != TEE_SUCCESS) 29 return res; 30 } else { 31 /* 32 * Pick something of a different size than TEE_UUID to 33 * guarantee that there's never a conflict. 34 */ 35 uint8_t dummy[1] = { 0 }; 36 37 res = do_hmac(tsk, sizeof(tsk), tee_fs_ssk.key, 38 TEE_FS_KM_SSK_SIZE, dummy, sizeof(dummy)); 39 if (res != TEE_SUCCESS) 40 return res; 41 } 42 43/* 获取调用AEC_CBC操作需要的context的大小 */ 44 res = crypto_ops.cipher.get_ctx_size(TEE_FS_KM_ENC_FEK_ALG, &ctx_size); 45 if (res != TEE_SUCCESS) 46 return res; 47 48/* 分配一份进行AES_CBC操作时需要的context空间 */ 49 ctx = malloc(ctx_size); 50 if (!ctx) 51 return TEE_ERROR_OUT_OF_MEMORY; 52 53/* 使用TSK作为进行AES_CBC计算使用的key,而IV值默认为0 */ 54 res = crypto_ops.cipher.init(ctx, TEE_FS_KM_ENC_FEK_ALG, mode, tsk, 55 sizeof(tsk), NULL, 0, NULL, 0); 56 if (res != TEE_SUCCESS) 57 goto exit; 58 59/* 将输入的in_key填充到context中,做完AES_CBC操作之后,输出的数据将会被保存到 60dst_key中 */ 61 res = crypto_ops.cipher.update(ctx, TEE_FS_KM_ENC_FEK_ALG, 62 mode, true, in_key, size, dst_key); 63 if (res != TEE_SUCCESS) 64 goto exit; 65 66/* 执行AES_CBC的加密运算,生成FEK */ 67 crypto_ops.cipher.final(ctx, TEE_FS_KM_ENC_FEK_ALG); 68 69/* 将生成的FEK的值拷贝到输出参数中 */ 70 memcpy(out_key, dst_key, sizeof(dst_key)); 71 72exit: 73 free(ctx); 74 75 return res; 76} 77

 

 

 

 

 

 

 

 

 

代码交流 2021