44. TA镜像加载时的验证
历经一年多时间的系统整理合补充,《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 》一书得以出版,书中详细介绍了TEE以及系统安全中的所有内容,全书按照从硬件到软件,从用户空间到内核空间的顺序对TEE技术详细阐述,读者可从用户空间到TEE内核一步一步了解系统安全的所有内容,同时书中也提供了相关的示例代码,读者可根据自身实际需求开发TA。目前该书已在天猫、京东、当当同步上线,链接如下(麻烦书友购书时能给予评论,多谢多谢):
非常感谢在此期间大家的支持以及各位友人的支持和帮助!!!。若觉得书中内容有错误的地方,欢迎大家指出,私信或者在博文中留言联系方式亦可发邮件至:aaa@qq.com,多谢各位了!!!!我会第一时间处理
当TA镜像文件已经被加载到共享内存之后,OP-TEE会对该份数据进行合法性检查。对TA镜像文件合法性的检查是通过调用检查镜像文件中的hash值,magic值,flag等值是否合法并对镜像文件中的signature部分做Verify操作是否成功来判定读取到共享内存中的TA镜像文件内容是否合法。整个验证过程如下:
1. 验证使用的RSApublic key的产生和获取
编译整个工程的时候会产生一个ta_pub_key.c文件,该文件中存放的就是在验证TA镜像文件合法性的时候使用到的RSA public key的内容,该文件是在编译gensrcs-y目标中的ta_pub_key成员来产生的,该部分的内容定义在optee_os/core/sub.mk文件中,其内容如下:
subdirs-y += kernel
subdirs-y += tee
subdirs-y += drivers
ifeq ($(CFG_WITH_USER_TA)-$(CFG_REE_FS_TA),y-y)
gensrcs-y += ta_pub_key
produce-ta_pub_key = ta_pub_key.c
depends-ta_pub_key = $(TA_SIGN_KEY)
recipe-ta_pub_key = scripts/pem_to_pub_c.py --prefix ta_pub_key \
--key $(TA_SIGN_KEY) --out $(sub-dir-out)/ta_pub_key.c
cleanfiles += $(sub-dir-out)/ta_pub_key.c
endif
编译是在处理ta_pub_key目标时调用recipe-ta_pub_key来产生ta_pub_key.c文件,该文件将会被保存在optee_os/out/arm/core/目录中。在recipe-ta_pub_key中通过调用pem_to_pub_c.py脚本解析存放在optee_os/keys目录中的rsa key获取public key的相关内容并保存到ta_pub_key.c文件中,pem_to_pub_c.py脚本的内容如下:
#输入参数解析函数
def get_args():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--prefix', required=True, \
help='Prefix for the public key exponent and modulus in c file')
parser.add_argument('--out', required=True, \
help='Name of c file for the public key')
parser.add_argument('--key', required=True, help='Name of key file')
return parser.parse_args()
#生成ta_pub_key.c文件的主要函数
def main():
import array
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes
#解析输入参数
args = get_args();
#打开输入的pem格式的RSA key并读取内容
f = open(args.key, 'r')
key = RSA.importKey(f.read())
f.close
#创建ta_pub_key.c文件
f = open(args.out, 'w')
#将include语句的内容写入到ta_pub_key.c文件中
f.write("#include <stdint.h>\n");
f.write("#include <stddef.h>\n\n");
#写入ta_pub_key_exponent变量的内容和值
f.write("const uint32_t " + args.prefix + "_exponent = " +
str(key.publickey().e) + ";\n\n")
#写入ta_pub_key_modulus变量的内容和值
f.write("const uint8_t " + args.prefix + "_modulus[] = {\n")
i = 0;
for x in array.array("B", long_to_bytes(key.publickey().n)):
f.write("0x" + '{0:02x}'.format(x) + ",")
i = i + 1;
if i % 8 == 0:
f.write("\n");
else:
f.write(" ");
f.write("};\n");
#写入ta_pub_key_modulus_size变量的值
f.write("const size_t " + args.prefix + "_modulus_size = sizeof(" + \
args.prefix + "_modulus);\n")
f.close()
if __name__ == "__main__":
main()
生成的ta_pub_key.c文件中将定义三个全局变量并赋值,这三个变量就是RSA public key的内容,作用和内容分别为:
ta_pub_key_exponent //RSA public key中的E值
ta_pub_key_modulus //RSA public key中的N值
ta_pub_key_modulus_size //RSA key的长度,再次该值为256也即表示该RSA key为RSA2048
这三个变量作为全局变量,在对TA镜像文件进行验签的时候用来做RSA2048的verify操作。
2. TA镜像文件的合法性检查的实现
对TA镜像文件内容合法性的检查是通过调用check_shdr函数来实现的,该函数中除了会对TA镜像文件中的signature内容做verify操作之外,还对校验TA镜像文件中shdr部分中的内容,check_shdr代码内容如下:
static TEE_Result check_shdr(struct shdr *shdr)
{
struct rsa_public_key key;
TEE_Result res;
//将全局变量ta_pub_key_exponent转成成RSA public key的E值
uint32_t e = TEE_U32_TO_BIG_ENDIAN(ta_pub_key_exponent);
size_t hash_size;
/* 校验shdr中的magic值和img_type值 */
if (shdr->magic != SHDR_MAGIC || shdr->img_type != SHDR_TA)
return TEE_ERROR_SECURITY;
/* 检查shdr中的algo成员指定的算法类型是否合法 */
if (TEE_ALG_GET_MAIN_ALG(shdr->algo) != TEE_MAIN_ALGO_RSA)
return TEE_ERROR_SECURITY;
/* 获取verify操作时需要使用的digest的大小 */
res = tee_hash_get_digest_size(TEE_DIGEST_HASH_TO_ALGO(shdr->algo),
&hash_size);
if (res != TEE_SUCCESS)
return res;
/* 检查shdr中的hash_size是否正确 */
if (hash_size != shdr->hash_size)
return TEE_ERROR_SECURITY;
/* 检查OP-TEE中提供的算法接口crypto_ops中的成员是否有效 */
if (!crypto_ops.acipher.alloc_rsa_public_key ||
!crypto_ops.acipher.free_rsa_public_key ||
!crypto_ops.acipher.rsassa_verify ||
!crypto_ops.bignum.bin2bn)
return TEE_ERROR_NOT_SUPPORTED;
/* 分配RSA public key在算法接口中的存储空间 */
res = crypto_ops.acipher.alloc_rsa_public_key(&key, shdr->sig_size);
if (res != TEE_SUCCESS)
return res;
/* 将RSA public key中的E值转换成bignumber */
res = crypto_ops.bignum.bin2bn((uint8_t *)&e, sizeof(e), key.e);
if (res != TEE_SUCCESS)
goto out;
/* 将ta_pub_key_modulus变量的值作为RSA public key中的N值并转换成bignumber */
res = crypto_ops.bignum.bin2bn(ta_pub_key_modulus,
ta_pub_key_modulus_size, key.n);
if (res != TEE_SUCCESS)
goto out;
/* 使用TA镜像文件中的digest部分和signature部分做RSA的verify操作 */
res = crypto_ops.acipher.rsassa_verify(shdr->algo, &key, -1,
SHDR_GET_HASH(shdr), shdr->hash_size,
SHDR_GET_SIG(shdr), shdr->sig_size);
out:
crypto_ops.acipher.free_rsa_public_key(&key);
if (res != TEE_SUCCESS)
return TEE_ERROR_SECURITY;
return TEE_SUCCESS;
}
在进行verify操作时使用的RSA public key的内容就是在编译时生成ta_public_key.c文件中的那两个全局变量ta_pub_key_exponent和ta_pub_key_modulus
上一篇: 4. OP-TEE+qemu的编译--bios.bin镜像的编译
下一篇: optee学习笔记_3
推荐阅读