当前位置: 首页 > news >正文

OPTEE:TA镜像的签名和加载-上(五)

0、前言

在前面知道了关于TA的基础知识,在书里面还有关于调用、内存、线程等知识,但是因为这次的任务是关于TA动态加载功能的验证,所以就先暂时跳过,我的学习一直都是建立关联。

前面讲了TA的用处,以及和CA的配合使用。这一节就来讲讲TA镜像本身的东西。

使用OP-TEE实现特定功能需求则需要开发一个特定的TA, TA调用GP规范定义的接口实现该功能需求。

(这篇文章为了连续性,就长得比较长,不一定一次性看完,建议慢慢享用,体会这个TA编译签名加载使用的逻辑,我分为两篇讲完)
在这里插入图片描述
TA镜像文件会被保存在REE侧的文件系统中并以动态TA的方式运行于OP-TEE中,当用户需要调用该TA的功能时,通过在CA中调用libteec库中的接口,完成创建会话的操作,将REE侧文件系统中的TA镜像文件加载到OP-TEE的用户空间运行。

为防止该TA镜像文件被篡改或被破坏,在加载TA镜像文件的过程中会对该TA镜像文件的合法性进行检查,只有校验通过的TA镜像文件才允许运行于OP-TEE的用户空间。(这就是目的)

编译TA镜像文件过程中会对TA镜像文件做电子签名操作。

接下来就讲讲TA镜像文件的编译、签名以及加载。

1、TA镜像文件的编译和签名

TA镜像文件在OP-TEE工程编译过程中生成,也可通过单独调用TA目录下的脚本来进行编译但前提是OP-TEE工程被完整编译过。

编译过程会先生成原始的TA镜像文件,然后使用签名脚本对该文件进行电子签名,并最终生成.ta文件,即最终会被加载到OP-TEE中的TA镜像文件。(这里感觉应该知道OPTEE的工程编译会好点~~~嘤嘤嘤,先讲讲什么是电子签名和验签)

电子签名(数字签名):利用公钥加密系统

常用的签名算法有:
+ RSA,基于大整数分解问题
+ DSA,基于离散对数问题
+ ECDSA,属于DSA的一个变种,基于椭圆曲线上的离散对数问题

其中RSA是实现数字签名最简单的公钥加密方法。
RSA加密算法是一种非对称加密算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。这样就可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。

具体的原理可以看看这个峰峰老师的文章:http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

加密和签名都是为了安全性考虑,但略有不同。
常有人问加密和签名是用私钥还是公钥?其实都是对加密和签名的作用有所混淆。

简单的说,加密是为了防止信息被泄露而签名是为了防止信息被篡改

总结:公钥加密、私钥解密。
私钥签名、公钥验签。

举个栗子:

第一个场景:战场上,B要给A传递一条消息,内容为某一指令。

RSA的加密过程如下:

(1)A生成一对密钥(公钥和私钥),私钥不公开,A自己保留。公钥为公开的,任何人可以获取。

(2)A传递自己的公钥给B,B用A的公钥对消息进行加密。

(3)A接收到B加密的消息,利用A自己的私钥对消息进行解密。

在这个过程中,只有2次传递过程,第一次是A传递公钥给B,第二次是B传递加密消息给A,即使都被敌方截获,也没有危险性,因为只有A的私钥才能对消息进行解密,防止了消息内容的泄露。(公钥加密、私钥解密。)

第二个场景:A收到B发的消息后,需要进行回复“收到”。

RSA签名的过程如下:

(1)A生成一对密钥(公钥和私钥),私钥不公开,A自己保留。公钥为公开的,任何人可以获取。

(2)A用自己的私钥对消息加签,形成签名,并将加签的消息和消息本身一起传递给B。

(3)B收到消息后,在获取A的公钥进行验签,如果验签出来的内容与消息本身一致,证明消息是A回复的。

在这个过程中,只有2次传递过程,第一次是A传递加签的消息和消息本身给B,第二次是B获取A的公钥,即使都被敌方截获,也没有危险性,因为只有A的私钥才能对消息进行签名,即使知道了消息内容,也无法伪造带签名的回复给B,防止了消息内容的篡改。

但是,综合两个场景你会发现,第一个场景虽然被截获的消息没有泄露,但是可以利用截获的公钥,将假指令进行加密,然后传递给A。(就是发送假消息)

第二个场景虽然截获的消息不能被篡改,但是消息的内容可以利用公钥验签来获得,并不能防止泄露。(消息的内容暴露了)

实际使用的时候:要根据情况使用,也可以同时使用加密和签名,比如A和B都有一套自己的公钥和私钥,当A要给B发送消息时,先用B的公钥对消息加密,再对加密的消息使用A的私钥加签名,达到既不泄露也不被篡改,更能保证消息的安全性。

下面开始整体

1.2、TA镜像文件的编译

前面知道在TA镜像文件在OP-TEE工程编译过程中生成,也可通过单独调用TA目录下的脚本来进行编译,但前提是OP-TEE工程被完整编译过。

编译过程会先生成原始的TA镜像文件,然后使用签名脚本对该文件进行电子签名,并最终生成.ta文件,即最终会被加载到OP-TEE中的TA镜像文件。

首先TA镜像文件的编译,对某个TA源代码目录中的Makefile文件执行make指令可触发编译生成TA镜像文件的操作,该Makefile文件将会包含optee_os/ta/mk/ta_dev_kit.mk文件,该文件中会定义各种目标依赖关系和Object

**编译完目标和object后,编译器将会按照optee_os/ta/arch/arm/link.mk文件中的依赖关系将目标和object链接成xxx.ta文件,**其中xxx是该TA UUID的值。

make–>Makefile文件–>optee_os/ta/mk/ta_dev_kit.mk—>optee_os/ta/arch/arm/link.mk–>xxx.ta(UUID的值)

link.mk中的链接依赖关系如下:
在这里插入图片描述
( l i n k − o u t − d i r ) / (link-out-dir)/ (linkoutdir)/(binary).stripped.elf目标会删除TA镜像文件中的调试信息。

在原始TA镜像文件的头部有一个ta_head段,该段中存放该TA的基本信息以及被调用到的入口地址,该段的内容将会在加载TA镜像到OP-TEE时和调用TA执行特定命令时被使用到。

存放在该段中的内容定义在optee_os/ta/arch/arm/user_ta_header.c文件中,其内容如下:
在这里插入图片描述

1.3、对TA镜像文件的签名

生成原始的TA镜像文件后,编译系统会对该镜像文件进行签名生成最终的xxx.ta文件,该文件会被保存在REE侧的文件系统中。

对原始TA镜像文件的签名操作是使用optee_os/scripts/sign.py文件来实现,使用的私钥是optee_os/keys目录下的RSA2048密钥(default_ta.pem)。当该TA需要被正式发布时,应该使用OEM厂商自有的私钥替换掉该密钥。
sign.py文件的内容如下:

在这里插入图片描述
在这里插入图片描述
签名完成后的TA镜像文件中的内容如图18-1所示。

在这里插入图片描述

签名后的TA镜像文件在被加载到OP-TEE内存中之前,会使用签名信息对该TA镜像文件进行合法性检查。(这个过程是我灰常想知道的。)

2、TA镜像的加载

前面讲了TA和CA建立会话,但是对这个TA的加载一笔带过,这里在着重讲一下TA的镜像加载。

我们知道TA分为静态和动态,静态是一开始和TEE一起编译进镜像,动态的话是放在REE侧的文件系统中。当CA第一次调用libteec库中的创建会话操作时,如果被调用的TA是动态TA,则会触发OP-TEE加载该动态TA镜像文件的操作。

在加载过程中,OP-TEE会发送PRC请求通知tee_supplicant从文件系统中将UUID对应的TA镜像文件传递到OP-TEE

在这里插入图片描述

如果验证通过则将相关段中的内容保存到OP-TEE用户空间分配的TA内存中。

加载TA镜像的整体流程如图所示。
在这里插入图片描述

2.1 REE侧获取TA镜像文件的内容

OP-TEE通过调用rpc_load函数发送PRC请求,将TA镜像文件的内容从REE侧加载到OP-TEE的共享内存中。

该函数会触发两次RPC请求:

  • 第一次RPC请求用于获取TA镜像文件的大小

  • 第二次RPC请求是将TA镜像文件加载到OP-TEE的共享内存中

触发第二次RPC请求之前,OP-TEE会在用户空间先分配与TA镜像文件的大小相等的共享内存区域,该区域用于存放TA镜像文件的内容。(这里我们知道TA静态在内核态,动态在用户态,都是指的TEE环境中)

rpc_load函数的内容如下:
在这里插入图片描述
在这里插入图片描述
对TA镜像文件内容的合法性检查将TA加载到OP-TEE用户空间TA内存操作都是在共享内存中完成的。(这个共享内存区域就是在REE的内核态创建的。REE和TEE的很多操作都是通过共享内存实现的,所以在ATF中也有调用到copy_from_user这些函数的调用)

2.2 加载TA镜像的RPC请求

加载TA过程中,ta_open函数会调用rpc_load函数,该函数会调用thread_rpc_cmd来发送OPTEE_MSG_RPC_CMD_LOAD_TA的RPC请求,rpc_load函数会组合该类请求的相关数据结构变量,然后通过调用thread_rpc函数向REE发送RPC请求。thread_rpc_cmd函数的内容和介绍如下:
在这里插入图片描述

在整个TA的加载过程中会发送两次RPC请求,第一次是用于获取TA镜像文件的大小,第二次RPC请求是通知tee_supplicant将TA镜像文件的内容加载到OP-TEE提供的共享内存中。

2.3 RPC请求的发送

RPC请求的发送是通过触发安全监控模式调用(smc)来实现的,在触发安全监控模式调用(smc)之前会将当前的线程挂起,并保存该线程的运行上下文。该函数以汇编的形式实现内容如下:

在这里插入图片描述
当REE处理完RPC请求后,会发送标准安全监控模式调用(std smc)重新进入到OP-TEE中,OP-TEE根据返回的安全监控模式调用(smc)的类型判定当前的安全监控模式调用(smc)是RPC的返回还是普通的安全监控模式调用(smc)。

如果该安全监控模式调用(smc)是返回RPC请求的处理结果,则会进入到thread_resume_from_rpc分支恢复之前被挂起的线程。在thread_rpc函数中已经指定了恢复该线程之后程序执行的入口函数——thread_rpc_return,到此一次完整的RPC请求也就被处理完毕。
(其实你很难第一次就把这些知识吸收掉,尽管前辈的书写的很好很详细,等到你需要在这个方面去做点东西的时候,你再来看,会找到答案。)

2.4 读取TA镜像文件内容到共享内存

rpc_load函数发起第二次RPC请求时才会将TA镜像文件的内容读取到OP-TEE提供的共享内存中。

共享内存的分配是在rpc_load函数中调用thread_rpc_alloc_payload函数来实现的。

分配的共享内存的地址将会被保存到ta_handle变量的nw_ta成员中,读取到的TA镜像文件的内容将会被加载到OP-TEE用户空间TA运行的内存中。(代码内容解释见16.2.1节。《手机安全和可信应用开发指南》)

到这里休息一下啦小少爷,不累的话,那就继续看下一篇吧。

相关文章:

  • mysql在django中开启事务,实现悲观锁和乐观锁
  • 极限多标签学习之-PLT
  • MMDetection系列 | 5. MMDetection运行配置介绍
  • java实现顺序表
  • 【英语:基础进阶_核心词汇扩充】E5.常见词根拓词
  • 命令执行漏洞——系统命令执行
  • 【数据结构与算法】List接口栈队列
  • 将cookie字符串转成editthiscookie插件的json格式
  • SpringAOP总结
  • python--数据容器--列表
  • Roson的Qt之旅 #119 QNetworkAddressEntry详细介绍
  • Mybatis -- 使用
  • C语言双链表,循环链表,静态链表讲解(王道版)
  • 比较zab、paxos和raft的算法的异同
  • Python Argparse 库讲解特别好的
  • 「译」Node.js Streams 基础
  • IndexedDB
  • input实现文字超出省略号功能
  • mongo索引构建
  • react 代码优化(一) ——事件处理
  • Vue.js 移动端适配之 vw 解决方案
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 官方解决所有 npm 全局安装权限问题
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 如何编写一个可升级的智能合约
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 什么是Javascript函数节流?
  • scrapy中间件源码分析及常用中间件大全
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​io --- 处理流的核心工具​
  • ​MySQL主从复制一致性检测
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • $.ajax()参数及用法
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)linux使用docker容器运行mysql
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (十三)Maven插件解析运行机制
  • (算法)前K大的和
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .Net Core 中间件验签
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .net6 webapi log4net完整配置使用流程
  • 。Net下Windows服务程序开发疑惑
  • @FeignClient 调用另一个服务的test环境,实际上却调用了另一个环境testone的接口,这其中牵扯到k8s容器外容器内的问题,注册到eureka上的是容器外的旧版本...
  • [1204 寻找子串位置] 解题报告
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [20150629]简单的加密连接.txt
  • [22]. 括号生成
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [Android] Implementation vs API dependency
  • [C++] 多线程编程-thread::yield()-sleep_for()