admin管理员组

文章数量:1662819

这里写目录标题

  • 一、存储机制介绍
  • 二、OPTEE REE文件系统安全存储(OPTEE SFS机制)
    • 2.1 OP-TEE 系统安全文件
    • 2.2 GlobalPlatform 安全存储要求
    • 2.3 Linux 文件系统中的 TEE 文件结构
    • 2.4 密钥管理器
      • 2.4.1 硬件唯一密钥`(Hardware Unique Key, HUK)`
      • 2.4.2 安全存储密钥`(Secure Storage Key, SSK)`
      • 2.4.3 TA存储密钥`(Trusted Application Storage Key, TSK)`
      • 2.4.4 文件加密密钥`(File Encryption Key, FEK)`
        • 2.4.4.1 元数据(Meta data)加密流程
        • 2.4.4.2 块数据(Block Data)加密流程
    • 2.5 安全存储文件格式
      • 2.5.1 安全文件的三个区域
      • 2.5.2 重要结构体的关系框图

一、存储机制介绍

我们知道TEEOS最重要的功能莫过于安全存储了,这是一切安全的前提,根据存储安全性和使用场景GP TEE安全存储分为RPMB安全存储、SFS安全存储和SQLFS安全存储。如下图所示,临时对象、持久化对象、持久化枚举、数据流,分别可以存储在RPMB、SFS、SQLFS三种存储媒介上。

  • RPMB(Replay Protected Memory Block),作用在于存放机密数据。由于访问需要密钥,所以可以防止未授权的访问并且在每次的数据写入时都需要验证Write Counter寄存器值,这个寄存器值每写入成功便会加1,如果是黑客截取写入报文再进行重放攻击,由于counter已经更新了写入会无效。系统开机时在preloader阶段写入RPMB鉴权密钥,为保证密钥的安全性和保密密钥的分散方法,密钥一般由TEEOS提供库接口获取,在preloader跳入ATF执行时把密钥以参数传递方式放入安全SRAM,在TEEOS启动再将该密钥拷贝到安全DRAM中用于后续的RPMB操作。RPMB特点是非安全世界不可见,使用场景是安全性要求高、容量小,可以有效防止回滚和重放攻击。

  • SFS(File Storage Service)文件存储服务。一个SFS安全存储对象在Linux/Android端会生成多个文件,数据块文件和对应的meta文件,SFS特点是非安全世界可见,无法有效防止回滚,但可以同时在SFS和RPMB中写入读写次数检测回滚。使用场景是安全性要求略低、容量大。

  • SQLFS机制与SFS类似,由Android侧提供SQLITE数据库操作接口,一个安全存储对象在Linux/Android下只会生成单个文件,文件会比SFS少,并且SQLITE本身会支持到数据库的原子性操作和容错机制。特点是非安全世界可见,使用场景是安全性要求略低、容量大。

二、OPTEE REE文件系统安全存储(OPTEE SFS机制)

OP-TEE中的安全存储是根据 GlobalPlatform 的 TEE 内部核心 API(这里称为可信存储)中定义的内容来实现的。本规范规定,应该可以存储通用数据和关键材料,以保证所存储数据的机密性和完整性以及修改存储的操作的原子性(这里的原子性意味着整个操作要么成功完成,要么不写)。

目前,OP-TEE中有两种安全存储实现:

  • 第一个依赖于普通世界(REE)文件系统,默认实现。它在编译时通过 CFG_REE_FS=y 启用。
  • 第二种方法利用eMMC设备的重放保护内存块(RPMB)分区,通过设置 CFG_RPMB_FS=y 来启用。

2.1 OP-TEE 系统安全文件

2.2 GlobalPlatform 安全存储要求

以下是规范摘录,列出了最重要的要求:

  1. 只要应用了适当的加密保护,可信存储就可以由非安全资源支持,而加密保护必须与保护TEE代码和数据本身的方法一样强大。
  2. 可信存储必须绑定到特定设备,这意味着数据被创建时只有在同一TEE和同一设备上运行的授权ta才能访问或修改它
  3. TA本身能够隐藏敏感关键数据
  4. 每个TA都可以访问其自己的存储空间,该存储空间在该TA的所有实例之间共享,但与其他TA分离。
  5. 受信任存储必须提供最低级别的保护,以防回滚攻击。可以接受的是,实际的物理存储可能位于不安全的区域,因此容易受到来自TEE外部的操作的影响。通常,一个实现可以依赖于REE来达到这个目的(保护级别100),或者依赖于TEE控制的硬件资产(保护级别1000)。
    (see GP TEE Internal Core API section 2.5 and 5.2)

2.3 Linux 文件系统中的 TEE 文件结构

默认情况下,OP-TEE 使用 /data/tee/ 作为 Linux 文件系统中的安全存储空间。每个持久化对象都分配了一个内部标识符。它是一个整数,在 Linux 文件系统中显示为 /data/tee/<file number>

目录文件 /data/tee/dirf.db,列出安全存储中的所有对象。所有正常世界的文件都是完整性保护和加密的。

2.4 密钥管理器

密钥管理器是 TEE 文件系统中的一个组件,负责处理数据的加密和解密以及对敏感密钥的管理。密钥管理器使用三种类型的密钥:安全存储密钥(Secure Storage Key, SSK)、TA存储密钥(Trusted Application Storage Key, TSK)和文件加密密钥(File Encryption Key, FEK)

2.4.1 硬件唯一密钥(Hardware Unique Key, HUK)

大多数设备都有某种硬件唯一密钥(HUK),HUK 的重要之处在于它需要得到很好的保护,并且在最好的情况下,HUK 永远不应该直接从软件读取,甚至不应该从安全方面读取。一般存储在硬件Fuse介质中,熔断后无法修改,软件一般无法直接读取到,一般设计由硬件加速器访问,主要用于派生其他密钥。例如,当派生密钥用于安全存储等时,可以使用 HUK 派生。

在 OP-TEE 中,HUK 只是一个存根,您将看到在core/include/kernel/tee_common_otp.h 中名为 tee_otp_get_hw_unique_key的函数中。

struct tee_hw_unique_key {
	uint8_t data[HW_UNIQUE_KEY_LENGTH];
};

TEE_Result tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey);

在真正安全的产品中,必须用其他东西替换它。如果你的设备缺少对HUK的硬件支持,那么你至少必须将其更改为除零之外的其他内容。但是,请记住,在软件中存储密钥不是一个好的安全做法,尤其是不能将密钥作为其他所有内容的根,因此我们建议您不要这样做。

2.4.2 安全存储密钥(Secure Storage Key, SSK)

SSK是每个设备的密钥,在OP-TEE启动时生成并存储在安全内存中。SSK用于派生TA存储密钥(TSK)。

SSK = HMACSHA256 (HUK, Chip ID ||static string”)

获取硬件唯一密钥(HUK)和芯片ID的功能取决于平台实现。目前,OP-TEE 系统中每台设备只有一把 SSK,用于安全存储子系统。但是,为了将来,我们可能需要为每台设备使用生成 SSK 的相同算法为不同的子系统创建不同的密钥。为不同子系统生成不同的密钥的简单方法是使用不同的静态生成密钥的字符串。

static TEE_Result tee_fs_init_key_manager(void)
{
	int res = TEE_SUCCESS;
	struct tee_hw_unique_key huk;
	uint8_t chip_id[TEE_FS_KM_CHIP_ID_LENGTH];
	uint8_t message[sizeof(chip_id) + sizeof(string_for_ssk_gen)];
 
	/* Secure Storage Key Generation:
	 *
	 *     SSK = HMAC(HUK, message)
	 *     message := concatenate(chip_id, static string)
	 * */
	/* 获取HUK的值(该接口的实现与平台有关,不同的芯片具有不同的读取HUK值的方式) */
	tee_otp_get_hw_unique_key(&huk);
 
	/*  获取chip ID的值(不同的芯片具有不同的读取chip id值的方式)*/
	tee_otp_get_die_id(chip_id, sizeof(chip_id));
 
	/* 将chip id + string_for_ssk_gen连接后的值保存到message中,string_for_ssk_gen是一个
	 * 静态的字符串,该值被hard code在代码中 
	 */
	memcpy(message, chip_id, sizeof(chip_id));
	memcpy(message + sizeof(chip_id), string_for_ssk_gen,
			sizeof(string_for_ssk_gen));
 
	/* 使用huk的值对message的内容做HMAC运算,将获取到数据作为SSK,保存到tee_fs_ssk
	 * 变量的key成员中 
	 */
	res = do_hmac(tee_fs_ssk.key, sizeof(tee_fs_ssk.key),
			huk.data, sizeof(huk.data),
			message, sizeof(message));
 
	/* 标记ssk已经生产 */
	if (res == TEE_SUCCESS)
		tee_fs_ssk.is_init = 1;
 
	return res;
}

2.4.3 TA存储密钥(Trusted Application Storage Key, TSK)

TSK是每个受信任的应用程序密钥,由SSK和TA的标识符(UUID)生成。它被用来保护FEK,换句话说,用来加密/解密FEK

TSK = HMACSHA256 (SSK, TA_UUID)

2.4.4 文件加密密钥(File Encryption Key, FEK)

当一个新的TEE文件被创建时,密钥管理器将通过 PRNGpesudo随机数生成器)或者TRNG为TEE文件生成一个新的 FEK,并将加密的 FEK 存储在 meta 文件中。FEK 用于对存储在 meta 文件中的TEE文件信息或块文件中的数据进行加密/解密。

TEE_Result tee_fs_fek_crypt(const TEE_UUID *uuid, TEE_OperationMode mode,
			    const uint8_t *in_key, size_t size,
			    uint8_t *out_key)
{
	TEE_Result res;
	uint8_t *ctx = NULL;
	size_t ctx_size;
	uint8_t tsk[TEE_FS_KM_TSK_SIZE];
	uint8_t dst_key[size];
 
	/* 检查输入的用于生成FEK的随机数in_key和用于存放生成的out_key地址是否合法 */
	if (!in_key || !out_key)
		return TEE_ERROR_BAD_PARAMETERS;
 
	/* 检查in_key长度 */
	if (size != TEE_FS_KM_FEK_SIZE)
		return TEE_ERROR_BAD_PARAMETERS;
 
	/* 判定SSK是否已经被初始化 */
	if (tee_fs_ssk.is_init == 0)
		return TEE_ERROR_GENERIC;
 
	/* 如果调用的时候参数uuid不为0,则调用HMAC算法生成TSK。如果UUID的值为0,则
	 * 默认生成TSK使用的原始数据为0 
	 */
	if (uuid) {
		res = do_hmac(tsk, sizeof(tsk), tee_fs_ssk.key,
			      TEE_FS_KM_SSK_SIZE, uuid, sizeof(*uuid));
		if (res != TEE_SUCCESS)
			return res;
	} else {
		/*
		 * Pick something of a different size than TEE_UUID to
		 * guarantee that there's never a conflict.
		 */
		uint8_t dummy[1] = { 0 };
 
		res = do_hmac(tsk, sizeof(tsk), tee_fs_ssk.key,
			      TEE_FS_KM_SSK_SIZE, dummy, sizeof(dummy));
		if (res != TEE_SUCCESS)
			return res;
	}
 
	/* 获取调用AEC_CBC操作需要的context的大小 */
	res = crypto_ops.cipher.get_ctx_size(TEE_FS_KM_ENC_FEK_ALG, &ctx_size);
	if (res != TEE_SUCCESS)
		return res;
 
	/* 分配一份进行AES_CBC操作时需要的context空间 */
	ctx = malloc(ctx_size);
	if (!ctx)
		return TEE_ERROR_OUT_OF_MEMORY;
 
	/* 使用TSK作为进行AES_CBC计算使用的key,而IV值默认为0 */
	res = crypto_ops.cipher.init(ctx, TEE_FS_KM_ENC_FEK_ALG, mode, tsk,
				     sizeof(tsk), NULL, 0, NULL, 0);
	if (res != TEE_SUCCESS)
		goto exit;
 
	/* 将输入的in_key填充到context中,做完AES_CBC操作之后,输出的数据将会被保存到
	 * dst_key中 
	 */
	res = crypto_ops.cipher.update(ctx, TEE_FS_KM_ENC_FEK_ALG,
			mode, true, in_key, size, dst_key);
	if (res != TEE_SUCCESS)
		goto exit;
 
	/* 执行AES_CBC的加密运算,生成FEK */
	crypto_ops.cipher.final(ctx, TEE_FS_KM_ENC_FEK_ALG);
 
	/* 将生成的FEK的值拷贝到输出参数中 */
	memcpy(out_key, dst_key, sizeof(dst_key));
 
exit:
	free(ctx);
 
	return res;
}
2.4.4.1 元数据(Meta data)加密流程

tee_fs_htree_image区域中的数据按照meta data方式加密生产,该加密的过程如下图所示:
上述加密操作过程中相关元素说明如下:

FEK: 安全文件和dirf.db文件在执行加密操作时使用的key,该值在文件创建的时候使用随机数的方式生成,对已经创建好的文件进行操作时,该值会从tee_fs_htree_image中的enc_fek成员中使用TSK解密获得。

TSK: TA Applicant storage key, 使用sskUUID执行HMAC计算得到

AES_ECB: 将FEK使用TSK进行AESECB模式进行加密操作生成enc_fek

Encrypted FEK: 使用TSK加密FEK得到,保存在tee_fs_htree_imageenc_fek中,最终会被写入到安全文件或者dirf.db文件头的head

Meta IV: 使用secure storage创建文件或者将tee_fs_htree_image写入到文件中是都会被随机生成,最终会被写入到安全文件或者dirf.db文件头的head

Meta Data: /data/tee目录下每个文件中存放的tee_fs_htree_node_Image的个数相关的数据

AES_GCM: 将enc_fek+meta iv+meta data使用FEKmeta IV进行AESGCM模式进行加密操作生成TagEncryption Meta Data数据

Tag: 加密enc_fek+meta iv+meta data时生成的tag值,数据会被保存在tee_fs_htree_image中的tag成员中

Encryptoed Meta Data: 加密enc_fek+meta iv+meta data时生成的imeta值,数据会被保存在tee_fs_htree_image中的imeta成员中

2.4.4.2 块数据(Block Data)加密流程

data blocktee_fs_htree_node_image中的数据按照block data encryption的方式加密数据块生成,block data encryption方式的加密过程如下:
上述加密操作过程中相关元素说明如下:

Encrypted FEK: 使用TSK加密FEK得到,保存在tee_fs_htree_imageenc_fek中,最终会被写入到安全文件或者dirf.db文件头的head

TSK: TA Applicant storage key, 使用sskUUID执行HMAC计算得到

AES_ECB: 将Encrypted FEK使用TSK进行AESECB模式进行解密操作生成FEK

FEK: 解密Encrypted FEK之后生成的FEK,用于加密需要被保存的数据block

Block IV: 每次加密数据区域中每个block是都会随机生成,然后被保存到tee_fs_htree_node_image变量的iv成员中

Block Data: 将需要被保存的数据更新到对应的block的数据的正确位置之后生成的新的block的完成明文数据

AES_GCM: 将Block IV+Block data使用FEKBlock IV进行AESGCM模式进行加密操作生成TagEncryption Block Data数据

Tag: 加密Block IV+Block data时生成的tag值,数据会被保存在tee_fs_htree_node_image中的tag成员中

Encryption Block Data: 加密Block IV+Block data时生成的Encryption Block Data值,数据会被保存在文件中的数据区域对应的block中。

2.5 安全存储文件格式

2.5.1 安全文件的三个区域

使用安全存储功能生成的文件都会使用相同的格式被保存,而且dirf.db文件与安全文件的格式也相同。

安全文件中的内容分为三个区域,分别用于保存文件头、结点、数据,文件的内容

OPTEE使用哈希树结构负责处理安全存储文件的数据加密和解密。哈希树被实现为二叉树,树中的每个节点(struct tee_fs_htree_node_image)保护其两个子节点和一个数据块。元数据(meta data)存储在一个头(struct tee_fs_htree_image)中,它也保护顶层节点。

所有字段(头、节点和块)都使用01两个版本进行复制,以确保原子更新。有关详细信息,请参见core/tee/fs_htree.c。

  • tee_fs_htree_node_image:用于保存文件的节点node信息,通过节点可找到对应文件的头部或数据块信息,包含hash、iv、tag以及flags
  • tee_fs_htree_image:用于保存安全文件的头部数据,从头部数据中可获取安全文件的加密密钥和加密头部时使用的IV值。
  • tee_fs_fd:安全存储操作时使用的重要结构体,存放对文件操作时使用的fd、dir、TAUUID等信息。
  • 每个Phys block的大小为4K

结构体定义如下:

#define TEE_FS_HTREE_HASH_SIZE		TEE_SHA256_HASH_SIZE
#define TEE_FS_HTREE_IV_SIZE		U(16)
#define TEE_FS_HTREE_FEK_SIZE		U(16)
#define TEE_FS_HTREE_TAG_SIZE		U(16)

/* Internal struct provided to let the rpc callbacks know the size if needed */
struct tee_fs_htree_node_image {
	/* Note that calc_node_hash() depends on hash first in struct */
	uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; /* 保存节点的hash值,用于在操作文件的时候找到该文件的head */
	uint8_t iv[TEE_FS_HTREE_IV_SIZE]; /* 加密安全文件数据区域中某一个block时使用的iv值,block数据的每次写入都会使用随机数更新 */
	uint8_t tag[TEE_FS_HTREE_TAG_SIZE]; /* 加密安全数据区域中够一个block数据时生成的ta */
	uint16_t flags; /* 用于计算使用block中的那个ver */
};
/* Internal struct provided to let the rpc callbacks know the size if needed */
struct tee_fs_htree_image {
	uint8_t iv[TEE_FS_HTREE_IV_SIZE]; /* 加密iv+enc_fek时使用的iv值,每次保存head时会使用随机数更新 */
	uint8_t tag[TEE_FS_HTREE_TAG_SIZE]; /* 加密iv+Enc_fek生成的数据的tag部分 */
	uint8_t enc_fek[TEE_FS_HTREE_FEK_SIZE]; /* 使用TSK加密一个安全文件的fek生成的 */
	uint8_t imeta[sizeof(struct tee_fs_htree_imeta)]; /* 加密iv+Enc_fek生成的数据的imeta部分 */
	uint32_t counter; /* 用于计算在保存tee_fs_htree_image的时候是存到ver0还是ver1 */
};
struct tee_fs_fd {
	struct tee_fs_htree *ht; /* 哈希树头 */
	int fd; /* 文件句柄 */
	struct tee_fs_dirfile_fileh dfh; /* dirfile_entry结构体来表示每个安全文件的基本信息 */
	const TEE_UUID *uuid; /* TA UUID */
};

2.5.2 重要结构体的关系框图

这些重要的结构体的关系框图如下:
补充剩余几个结构体描述:

  • tee_fs_htree_storage 结构体定义如下,包含了读写RPC调用的函数指针。
/**
 * struct tee_fs_htree_storage - storage description supplied by user of
 * this interface
 * @block_size:		size of data blocks
 * @rpc_read_init:	initialize a struct tee_fs_rpc_operation for an RPC read
 *			operation
 * @rpc_write_init:	initialize a struct tee_fs_rpc_operation for an RPC
 *			write operation
 *
 * The @idx arguments starts counting from 0. The @vers arguments are either
 * 0 or 1. The @data arguments is a pointer to a buffer in non-secure shared
 * memory where the encrypted data is stored.
 */
struct tee_fs_htree_storage {
	size_t block_size;
	TEE_Result (*rpc_read_init)(void *aux, struct tee_fs_rpc_operation *op,
				    enum tee_fs_htree_type type, size_t idx,
				    uint8_t vers, void **data);
	TEE_Result (*rpc_read_final)(struct tee_fs_rpc_operation *op,
				     size_t *bytes);
	TEE_Result (*rpc_write_init)(void *aux, struct tee_fs_rpc_operation *op,
				     enum tee_fs_htree_type type, size_t idx,
				     uint8_t vers, void **data);
	TEE_Result (*rpc_write_final)(struct tee_fs_rpc_operation *op);
};

  • tee_fs_htree_meta 元数据长度
/*
 * This struct is not interpreted by the hash tree, 
 * it's up to the user of
 * the interface to update etc if needed.
 */
struct tee_fs_htree_meta {
	uint64_t length;
};
  • tee_fs_dirfile_dirh 表示每个安全文件的基本信息,包含文件读写函数指针,
/**
 * struct tee_fs_dirfile_operations - file interface supplied by user of this
 * interface
 * @open:		opens a file
 * @close:		closes a file, changes are discarded unless
 *			@commit_writes is called before
 * @read:		reads from an open file
 * @write:		writes to an open file
 * @commit_writes:	commits changes since the file was opened
 */
struct tee_fs_dirfile_operations {
	TEE_Result (*open)(bool create, uint8_t *hash, const TEE_UUID *uuid,
			   struct tee_fs_dirfile_fileh *dfh,
			   struct tee_file_handle **fh);
	void (*close)(struct tee_file_handle *fh);
	TEE_Result (*read)(struct tee_file_handle *fh, size_t pos,
			   void *buf, size_t *len);
	TEE_Result (*write)(struct tee_file_handle *fh, size_t pos,
			    const void *buf, size_t len);
	TEE_Result (*commit_writes)(struct tee_file_handle *fh, uint8_t *hash);
};
struct tee_fs_dirfile_dirh {
	const struct tee_fs_dirfile_operations *fops; /* 文件操作函数指针 */
	struct tee_file_handle *fh;
	int nbits;
	bitstr_t *files;
	size_t ndents;
};

参考:
奔人之旅的文章-【OP-TEE】安全存储
OP-TEE中secure stroage------安全文件数据格式和操作过程

本文标签: 密钥内核学习笔记结构文件