OPTEE TA介绍

前言

本文主要介绍OPTEE的TA(Trusted Applications),翻译自官方文档:Trusted Applications — OP-TEE documentation documentation (optee.readthedocs.io)


有两种方法可以实现可信应用程序 (TA):伪 TA 和用户模式 TA。用户模式 TA 是 GlobalPlatform API TEE 规范指定的功能齐全的受信任应用程序,这些只是人们在说“受信任的应用程序”时所指的那些,在大多数情况下,这是编写和使用首选的 TA 类型。

一、伪TA(Pseudo Trusted Applications)

伪TA不是受信任应用程序。伪 TA 不是特定实体。伪 TA 是一个接口。它是 OP-TEE Core 向其外部世界公开的接口:用于保护客户端受信任的应用程序和非安全的客户端实体。

这些直接实现到 OP-TEE 核心树中,例如:core/PTA,并与 OP-TEE core blob 一起构建并静态构建到其中。

OP-TEE中包含的伪TA已经是隐藏在“GlobalPlatform TA Client”API后面的OP-TEE安全特权级别服务。这些伪 TA 用于各种目的,例如特定的安全服务或嵌入式测试服务。

伪 TA 无法从 GlobalPlatform TEE 规范指定的 GlobalPlatform Core Internel API 支持中受益。这些 API 作为静态库提供给 TA,每个 TA 应链接(“libutee”),并通过syscalls调用 OP-TEE 核心服务。由于OP-TEE核心不与libutee链接,伪TA只能使用OP-TEE核心内部API和例程。

由于伪 TA 在与 OP-TEE 核心代码本身相同的特权执行级别上运行,因此根据用例的不同,这可能是可取的,也可能是不可取的。

在大多数情况下,非特权(用户模式)TA 是最佳选择,而不是将代码直接添加到 OP-TEE core。但是,如果您决定最好像这样直接在 OP-TEE core中处理您的应用程序,您可以将 core/pta/stats.c 视为模板,并基于此将伪 TA 添加到同一目录中的 sub.mk 中

二、用户模式TA(User Mode Trusted Applications)

用户模式TA由Secure World中的 OP-TEE core加载(映射到内存中),当富执行环境 (REE) 中的某些内容想要与该特定应用程序 UUID 通信时。它们以比 OP-TEE 核心代码更低的 CPU 权限级别运行。在这方面,它们与在 REE 中运行的常规应用程序非常相似,只是它们在Secure World中执行。

TA受益于全球平台 TEE 规范指定的 GlobalPlatform TEE Internel Core API。有几种类型的用户模式 TA,它们的存储方式不同。

三、TA位置

普通 TA(用户模式)可以驻留且从不同位置加载。OP-TEE 目前支持三种方式。

1、Early TA

所谓的Early TA 实际上与 REE FS TA 相同,但它们不是从正常世界文件系统加载的,而是链接到 TEE core blob 中的特殊数据部分。因此,它们甚至在 tee supplicant和 REE filesystem出现之前就可用。请在early TA summit中找到更多详细信息。

2、REE filesystem TA

它们由一个 ELF 文件组成,经过签名和选择性加密,以 TA 的 UUID 和 .ta后缀命名。它们是独立于 OP-TEE core boot-time blob生成的,尽管在生成它们时它们使用相同的生成系统,并使用原始  OP-TEE core blob 生成的密钥进行签名。

由于 TA 已签名并可选择使用脚本/sign_encrypt.py进行加密,因此它们能够存储在不受信任的 REE filesystem中,并且tee supplicant将负责传递它们以由Secure World OP-TEE core检查和加载。

A.REE-FS TA 回滚保护

OP-TEE core在安全存储中维护一个ta_ver.db文件,以检查从 REE-FS 加载的 REE TA 版本,以防止任何 TA 版本降级。TA 版本可以通过 TA  build option进行配置:CFG_TA_VERSION=<unsigned integer>。

注意:此处的回滚保护仅在 CFG_RPMB_FS=y 时有效

B.REE-FS TA 格式

REE 文件系统 TA 有三种格式:

自版本 3.7.0 起,构建脚本无法再创建已签名且未加密的旧版 TA。

引导 TA使用原始 OP-TEE core blob 生成的密钥进行签名,未加密。

加密的 TA,先签名再加密后 MAC,当 CFG_ENCRYPT_TA=y 时用 TA_ENC_KEY 加密。在 OP-TEE 运行时,用于解密 TA 的对称密钥必须通过覆盖 API 以平台特定的方式提供:

TEE_Result tee_otp_get_ta_enc_key(uint32_t key_type, uint8_t *buffer,
                                  size_t len);

C.REE-FS TA 头结构

所有 REE 文件系统 TA 都有通用的头,struct shdr,定义为:

enum shdr_img_type {
        SHDR_TA = 0,
        SHDR_BOOTSTRAP_TA = 1,
        SHDR_ENCRYPTED_TA = 2,
};

#define SHDR_MAGIC      0x4f545348

/**
 * struct shdr - 签名头
 * @magic:      magic number必须与SHDR_MAGIC匹配
 * @img_type:   image type, values 通过enum shdr_img_type定义
 * @img_size:   图像大小(以字节为单位)
 * @algo:       算法,由 TEE 内部 API 规范形如TEE_ALG_XXX定义的公钥算法
 * @hash_size:  签名哈希的大小
 * @sig_size:   签名的大小
 * @hash:       图像的哈希
 * @sig:        hash签名
 */
struct shdr {
        uint32_t magic;
        uint32_t img_type;
        uint32_t img_size;
        uint32_t algo;
        uint16_t hash_size;
        uint16_t sig_size;
        /*
         * 注释掉的元素用于可视化结构体的动态布局部分。
         * 哈希通过宏SHDR_GET_HASH访问,签名通过宏SHDR_GET_SIG
         *
         * uint8_t hash[hash_size];
         * uint8_t sig[sig_size];
         */
};

#define SHDR_GET_SIZE(x)        (sizeof(struct shdr) + (x)->hash_size + \
                                 (x)->sig_size)
#define SHDR_GET_HASH(x)        (uint8_t *)(((struct shdr *)(x)) + 1)
#define SHDR_GET_SIG(x)         (SHDR_GET_HASH(x) + (x)->hash_size)

字段img_type为 TA 的类型,如果它是SHDR_TA(0),则它是旧版 TA。如果是SHDR_BOOTSTRAP_TA(1),则为引导 TA。

字段algo为使用的算法。用于对 TA 进行签名的脚本当前使用TEE_ALG_RSASSA_PKCS1_V1_5_SHA256 (0x70004830)。这意味着RSA具有PKCS#1v1.5填充和SHA-256哈希函数。OP-TEE 接受任何 TEE_ALG_RSASSA_PKCS1_* 算法。

对于引导 TA,结构 shdr 后跟一个子标题,结构体shdr_bootstrap_ta定义为:

/**
 * struct shdr_bootstrap_ta - bootstrap TA subheader
 * @uuid:       TA的UUID
 * @ta_version: TA的版本
 */
struct shdr_bootstrap_ta {
        uint8_t uuid[sizeof(TEE_UUID)];
        uint32_t ta_version;
};

字段 uuid 和 ta_version 允许在加载 TA 时执行额外的检查。目前仅检查 uuid 字段。

对于加密的 TA,结构 shdr 后跟一个子标题,结构shdr_bootstrap_ta后跟另一个子标题,结构shdr_encrypted_ta定义为:

/**
 * struct shdr_encrypted_ta - encrypted TA header
 * @enc_algo:   经过身份验证的加密算法,由 TEE 内部 API 规范中的对称密钥算法 TEE_ALG_* 定义
 * @flags:      经过身份验证的加密标志
 * @iv_size:    初始化向量的大小
 * @tag_size:   身份验证标记的大小
 * @iv:         初始化向量
 * @tag:        身份验证标记
 */
struct shdr_encrypted_ta {
        uint32_t enc_algo;
        uint32_t flags;
        uint16_t iv_size;
        uint16_t tag_size;
        /*
         * 注释掉的元素用于可视化 struct.iv 的布局动态部分通过宏SHDR_ENC_GET_IV访问,标记通过宏SHDR_ENC_GET_TAG访问
         *
         * uint8_t iv[iv_size];
         * uint8_t tag[tag_size];
         */
};

字段enc_algo告诉所使用的算法。用于加密 TA 的脚本当前使用 TEE_ALG_AES_GCM (0x40000810)。OP-TEE内核也接受TEE_ALG_AES_CCM算法。

字段flags支持单个标志来告知加密密钥类型,该类型定义为:

#define SHDR_ENC_KEY_TYPE_MASK  0x1

enum shdr_enc_key_type {
        SHDR_ENC_KEY_DEV_SPECIFIC = 0,
        SHDR_ENC_KEY_CLASS_WIDE = 1,
};

D.REE-FS TA 二进制格式

TA 二进制文件遵循 ELF 文件,该文件通常被剥离为附加符号等,加载 TA 时将被忽略。

旧版 TA 二进制文件的格式为

hash = H(<struct shdr> || <stripped ELF>)
signature = RSA-Sign(hash)
legacy_binary = <struct shdr> || <hash> || <signature> || <stripped ELF>

 引导 TA 二进制文件的格式为:

hash = H(<struct shdr> || <struct shdr_bootstrap_ta> || <stripped ELF>)
signature = RSA-Sign(<hash>)
bootstrap_binary = <struct shdr> || <hash> || <signature> ||
                   <struct shdr_bootstrap_ta> || <stripped ELF>

加密的 TA 二进制文件的格式为:

nonce = <unique random value>
ciphertext, tag = AES_GCM(<stripped ELF>)
hash = H(<struct shdr> || <struct shdr_bootstrap_ta> ||
         <struct shdr_encrypted_ta> || <nonce> || <tag> || <stripped ELF>)
signature = RSA-Sign(<hash>)
encrypted_binary = <struct shdr> || <hash> || <signature> ||
                   <struct shdr_bootstrap_ta> ||
                   <struct shdr_encrypted_ta> || <nonce> || <tag> ||
                   <ciphertext>

E.使用subkeys进行验证

可以使用subkeys或subkeys链来验证 TA。这允许在不分发根密钥的情况下委派 TA 签名。使用子项签名的 TA 仅限于子项的 UUID-V5 命名空间,以避免 TA UUID 与不同的子项发生冲突。

SHDR_SUBKEY是一种启用公钥链的标头。公钥根密钥用于验证第一个公钥subkey,然后用于验证下一个公钥subkeys,依此类推。

最后使用最后一个子项验证 TA。所有这些标头都添加到 TA 二进制文件的前面,因此验证 TA 所需的所有内容在加载到内存中时都可用。

F.加载REE-FS TA

REE TA 使用将 REE TA 加载到不安全共享内存中的系列或 RPC 加载到共享内存中。有效负载内存通过 TEE 请求方分配,稍后在释放先前分配的非安全共享内存中将 TA 加载到安全内存中时释放。

 将 REE TA 加载到不安全的共享内存中

 释放以前分配的不安全共享内存

3、Secure Storage TA

这些存储在安全存储中。元数据存储在所有已安装 TA 的数据库中,实际的二进制文件作为不受信任的 REE 文件系统(flash)中的单独文件进行加密和完整性保护。在加载这些 TA 之前,必须先安装它们,这是可以在初始部署期间或稍后阶段完成的操作。

出于测试目的,测试程序xtest可以使用以下命令将TA安装到安全存储中:

$ xtest --install-ta

存储在安全存储中的 TA 保存在 TA 数据库中。TA 数据库由一个名为 dirf 的单个文件组成.db该文件存储在基于 REE filesystem的安全存储或 RPMB 中。该文件与安全存储中的任何其他对象一样加密和完整性保护。TA 本身不存储在 dirf.db 中,而是存储在加密和完整性保护的 REE 文件系统中。其中一个原因是 TA 可能非常大,只有几兆字节,而安全存储旨在仅容纳以千字节为单位的小对象。

DIRF.db结构tadb_entry数组的组成,定义为:

/*
 * struct tee_tadb_property
 * @uuid:       可信应用程序 (TA) 或安全域 (SD) 的 UUID
 * @version:    TA 或 SD 的版本
 * @custom_size:自定义属性的大小,附加到加密的 TA 二进制文件前面
 * @bin_size:   二进制 TA 的大小
 */
struct tee_tadb_property {
        TEE_UUID uuid;
        uint32_t version;
        uint32_t custom_size;
        uint32_t bin_size;
};

#define TADB_IV_SIZE            TEE_AES_BLOCK_SIZE
#define TADB_TAG_SIZE           TEE_AES_BLOCK_SIZE
#define TADB_KEY_SIZE           TEE_AES_MAX_KEY_SIZE

/*
 * struct tadb_entry - TA database entry
 * @prop:        TA的属性
 * @file_number: 加密的 TA 存储在 <file_number>.ta 中
 * @iv:          身份验证加密的初始化向量
 * @tag:         用于验证身份验证加密 TA 的标记
 * @key:         用于解密 TA 的密钥
 */
struct tadb_entry {
        struct tee_tadb_property prop;
        uint32_t file_number;
        uint8_t iv[TADB_IV_SIZE];
        uint8_t tag[TADB_TAG_SIZE];
        uint8_t key[TADB_KEY_SIZE];
};

UUID 仅由零组成的条目无效,将被忽略。file_number字段表示存储在 REE 文件系统中的文件的名称。文件名由附加 .ta 的file_number的十进制字符串表示形式组成,或者如果要打印:printf(“%u.ta”, file_number)。

TA 使用 iv 和密钥字段初始化的身份验证加密算法 AES-GCM 进行解密,完成解密时使用标签字段。

通过打开 dirf 并在结构tadb_entry类型元素中扫描 TA .db直到找到匹配的 UUID 来查找 TA。

四、加载和准备 TA 以执行

用户模式 TA 使用用户模式 ELF 加载器 ldelf 以相同的方式加载到最终内存中。不同的 TA 位置具有指向 ldelf 的通用接口,这使得用户模式操作与 TA 的存储方式相同。

TA 将加载到准备 TA 以执行中的安全内存中。

准备 TA 执行

在 ldelf 返回准备执行的 TA 后,如果使用 dlopen() 和朋友,它仍然保留在内存中以服务于 TA。如果 TA 通过中止终止,ldelf 还用于转储堆栈跟踪和详细的内存映射。

Linux 用户空间中客户端应用程序的整个流的高级视图,其中会话被打开到 TA,在打开会话到 TA 中。

向 TA 打开会话

五、TA属性

1、GP属性

标准 TA 属性必须通过宏中的属性标志定义 TA_FLAGS user_ta_header_defines.h

A.单实例

“gpd.ta.singleInstance” 是 TA 的bool属性。此属性定义是必须创建 TA 的一个实例并接收所有打开的会话请求,还是必须为每个传入的打开会话请求创建新的特定 TA 实例。OP-TEE TA 标志TA_FLAG_SINGLE_INSTANCE设置为此属性的配置。如果TA_FLAGS设置位TA_FLAG_SINGLE_INSTANCE,则bool属性设置为 true,否则bool属性设置为 false。

B.多会话

“gpd.ta.multiSession” 是 TA 的bool属性。此属性定义 TA 实例是否可以处理多个会话。如果禁用,则 TA 实例仅支持一个会话。在这种情况下,如果 TA 已经打开了会话,则任何打开的会话请求都将返回繁忙错误状态。

注意:如果 TA 不是单实例 TA,则此属性毫无意义。

OP-TEE TA 标志TA_FLAG_MULTI_SESSION设置为此属性的配置。如果TA_FLAGS设置位TA_FLAG_MULTI_SESSION,则bool属性设置为 true,否则bool属性设置为 false。

C.Keep Alive

“gpd.ta.instanceKeepAlive” 是 TA 的bool属性。此属性定义当关闭朝 TA 打开的所有会话时,是否必须销毁创建的 TA 实例。如果启用了该属性,则 TA 实例一旦创建(在第一个打开会话请求时),将永远不会删除,除非重新启动 TEE 本身(启动/重新启动)。

注意:如果 TA 不是单实例 TA,则此属性毫无意义。

OP-TEE TA 标志TA_FLAG_INSTANCE_KEEP_ALIVE设置为此属性的配置。如果 TA_FLAGS 位设置为 TA_FLAG_INSTANCE_KEEP_ALIVE,则bool属性设置为 true,否则bool属性设置为 false。

D.堆大小

“gpd.ta.dataSize” 是 TA 的 32 位整数属性。此属性定义 TA 分配池的大小(以字节为单位),TEE_Malloc() 和友元在其中分配内存。属性的值必须由 user_ta_header_defines.h 中的宏TA_DATA_SIZE定义。

E.栈大小

“gpd.ta.stackSize” 是 TA 的 32 位整数属性。此属性定义用于 TA 执行的堆栈的大小(以字节为单位)。属性的值必须由 user_ta_header_defines.h 中的宏TA_STACK_SIZE定义。

2、属性扩展

A.安全数据路径标志

TA_FLAG_SECURE_DATA_PATH是TA_FLAGS支持的位标志。此属性标志声明来自 OP-TEE OS 的安全数据支持。请参阅 OP-TEE OS 以获取安全数据路径支持。未在 TA_FLAGS 值中设置TA_FLAG_SECURE_DATA_PATH的 TA 将无法处理与安全数据路径缓冲区相关的内存引用调用参数。

B.缓存维护标志

TA_FLAG_SECURE_DATA_PATH是TA_FLAGS支持的位标志。此属性标志声明来自 OP-TEE OS 的安全数据支持。请参阅 OP-TEE OS 以获取安全数据路径支持。未在 TA_FLAGS 值中设置TA_FLAG_SECURE_DATA_PATH的 TA 将无法处理与安全数据路径缓冲区相关的内存引用调用参数。

C.已弃用的属性标志

旧版本的 OP-TEE 用于定义扩展属性标志,这些标志已弃用且对当前 OP-TEE 毫无意义。这些是TA_FLAG_USER_MODE的,TA_FLAG_EXEC_DDR的和TA_FLAG_REMAP_SUPPORT的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

趣多多代言人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值