Secure boot说是为了防止黑客篡改系统,窃取你的数据。但是你的数据一般值得黑客窃取么,我想更重要的原因是商业利益:防止水货和自己偷偷升级不给服务费,防止黑客二次加工卖钱。所以厂商为了商业利益都要上Secure boot。特别是跨国贸易中的水货。保障自己的嵌入式商品上的软件被篡改,几乎各个公司都有这样的需求,不然就像卖DIY产品了,也需要大量的工程师来去做这个事情。所以Secure boot的目的只有一个:防止刷机。
整体来说需要关注两件事:
- 怎么从ROM开始构建image的信任链?
- 使用什么加解密算法校验不会被破解?
这个其实跟特工组织一样,有一个领头人发展下线,下线再发展下线,相邻两个级别之间有暗号,不同级别之间互不认识。这样敌人抓到某一个级别的人,也破解不了全局。
1. 固件加载信任链
之前介绍过ATF的文章:XXX中用过上面的一幅图ARM的启动图,从下到上进行启动的,顺序为:
BL33就是uboot,之后就会启动linux内核了。每一个阶段,上一阶段都会对下一个阶段的镜像进行校验,发现有改动就终止启动了,主打一个防篡改。
ATF:Arm Trusted Firmware(ARM安全固件),运行在EL3异常级别,ATF为Armv7-A 和 Armv8-A提供了一些安全可信固件。具体包括上面说的:ATF= BL1、BL2、BL31、BL32、BL33,其中BL33有就是U-Boot。都运行在EL3模式。具体为:
BL1:也叫bootrom,rom的意思就是只读的,具有最高的执行权限EL3,在 CPU 出厂时就被写死了。为什么要写死,这里有一个安全驱动概念(Secure Boot)。CPU上电启动的时候,加载镜像的顺序为BL1 -》 BL2 -》 BL31 -》 BL32 -》BL33(uboot)-》OS(Linux),但是如果其中的一个镜像被换掉了怎么办?这里不是说网络攻击换掉,就是物理上拿到电路板,然后把存储SD卡拔掉换了自己的OS,那不是想干啥就干啥,完全控制了硬件设备,俗称“越狱”。答案就是对每一个镜像进行签名校验。
例如BL33加载OS,需要OS镜像算出hash利用私钥 加密,然后BL31在加载OS的时候会读取这个加密的Hash,利用自己的公钥解密,解密后的hash是对的就进行加载。那么这么一级一级按照加密向前传递,那第一个根BL1如果可以在SD卡上伪造,那校验就没用了。所以BL1需要只读,并且作为只读硬件直接搞进到CPU里面,你从板子上也拆不下来,更替换不了。因为要写死到CPU内部,所以独立出来了,也是其由来的原因。
BL2:BL2在flash中的一段可信安全启动代码,主要完成一些平台相关的初始化,比如对ddr的初始化等。因为BL31和BL32是一个runtime,也就是上电后一直运行的程序,那么需要加载到内存里面,需要先初始化内存ddr,BL2就干这个事情的。所谓的Loder。
BL31:作为EL3最后的安全堡垒,它不像BL1和BL2是一次性运行的。如它的runtime名字暗示的那样,它通过SMC指令为Non-Secure OS持续提供设计安全的服务,在Secure World和Non-Secure World之间进行切换。是对硬件最基础的抽象,对OS提供服务。例如一个EL3级别的特权指令,比如关机、休眠等OS是无权处理的,就会交给BL31来继续操作硬件处理。
BL32:是所谓的secure os,运行在Secure mode。在ARM平台下是ARM 家的 Trusted Execution Environment(TEE)实现。OP-TEE 是基于ARM TrustZone硬件架构所实现的软件Secure OS。
启动BL1,BL2,BL31,BL32,BL33,Linux则是一个完整的ATF信任链建立流程(ARM Trusted Firmware)。并且负责加载镜像的BL1、BL2、BL33都不是runtime,只要Linux系统启动,是不可能再有加载镜像的机会了,挺狠!
一个更通用不局限于ARM ATF的secure boot模型图如下:
2. 镜像加密算法基础
说完了链式加载的信任链,那么接下来就是对固件加密和解密的知识了,因为这个被破解那也是搞不定这个流程的。
2.1 消息摘要算法介绍
为了防止数据被篡改,就要保证数据的完整性,一般做法是根据消息内容hash加密函数算出一个摘要,接收者拿到消息后也按同样的算法算出摘要,然后两个摘要对比就知道真假了。这个在消息传递的时候经常用到,这里我们用在了固件bin文件数据上。
数字指纹:消息摘要采用单向Hash函数,将需加密的明文"摘要"成一串固定位数(如128bit)的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。
不可逆:消息摘要具有不可逆性,在消息摘要生成过程中,会丢失很多原文的信息,而且无法找回。一个好的摘要算法,是极难产生Hash碰撞的,也就是找到另一段明文经计算后产生相同的摘要。
消息摘要算法常用的有:MD5,SHA,MAC等。在secure boot中,一般使用sha128,sha256、sha512等算法作为完整性算法。
2.2 非对称加密算法-RSA
什么是对称加密?
使用相同的密钥进行加密和解密。常见的算法有AES(Advanced Encryption Standard)。如果密码被敌人获得那就完了。特别是现在的网络时代,告诉对方密码也需要通过网络,这就更加的难了。
什么是非对称加密?
加密和解密不使用同样的密码和规则,只要这两种规则之间存在某种对应关系即可,这样就避免了直接传递密钥,这就是非对称加密。
使用非对称加密,A要给B发消息:
- B生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
- A获取B的公钥,然后用它对信息加密。
- B得到加密后的信息,用私钥解密。
如果公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的。
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。
下面我们来讲下RSA的原理。
互质:两个正整数,除了1以外,没有其他公因子,例如15和32之间。互质的一些推理:
任意两个质数构成互质关系,比如13和61。
一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
1和任意一个自然数是都是互质关系,比如1和99。
p是大于1的整数,则p和p-1构成互质关系,比如57和56。
p是大于1的奇数,则p和p-2构成互质关系,比如17和15。
欧拉函数φ(n):小于等于n的正整数之中,与n构成互质关系的数的个数。在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(8) = 4。
- ab-1/n=M, M是整数,正整数a和n互质,则b就叫做a的"模反元素",或者说ab被n除的余数是1。
- 如果b是a的模反元素,则 b+kn 都是a的模反元素。
- 模反元素必然存在,且不止一个
- a的 φ(n)-1 次方,就是a的模反元素。
比如,3和11互质,那么3的模反元素就是4,因为 (3 × 4)-1 可以被11整除。显然,模反元素不止一个, 4加减11的整数倍都是3的模反元素 {...,-18,-7,4,15,26,...},即如果b是a的模反元素,则 b+kn 都是a的模反元素。
加密:
也就是说RSA加密是对明文的E次方后除以N后求余数的过程。从通式可知,只要知道E和N任何人都可以进行RSA加密了,所以说E、N是RSA加密的密钥,也就是说E和N的组合就是公钥,我们用(E,N)来表示公钥
解密:
也就是说对密文进行D次方后除以N的余数就是明文,这就是RSA解密过程。知道D和N就能进行解密密文了,所以D和N的组合就是私钥
制造秘钥方法:
举例:
求N
我们准备两个很小对质数,
p = 17
q = 19
N = p * q = 323求L
L = lcm(p-1, q-1)= lcm(16,18) = 144
144为16和18对最小公倍数求E
求E必须要满足2个条件:1 < E < L ,gcd(E,L)=1
即1 < E < 144,gcd(E,144) = 1
E和144互为质数,5显然满足上述2个条件
故E = 5
此时公钥=(E,N)= (5,323)
求D
求D也必须满足2个条件:1 < D < L,E*D mod L = 1
即1 < D < 144,5 * D mod 144 = 1
显然当D= 29 时满足上述两个条件
1 < 29 < 144
5*29 mod 144 = 145 mod 144 = 1
此时私钥=(D,N)=(29,323)加密
准备的明文必须时小于N的数,因为加密或者解密都要mod N其结果必须小于N
假设明文 = 123
则 密文=明文EmodN=1235mod323=225- 解密
明文=密文DmodN=22529mod323=123
解密后的明文为123。
为什么知道公钥E,N不能破解?
公钥和私钥的纽带就是两个质数的成绩,如果两个质数特别的大,几百位,那么很难因式分解到正确的值。RSA算法的安全性随着密钥长度的增加而增强。更长的密钥意味着更大的n值,从而增加了分解的难度。例如,1024位的RSA密钥被认为是基本安全的,而2048位的密钥则被认为是极其安全的。迄今为止,还没有公开宣布成功破解了长度超过768位的RSA密钥。
详细的解释涉及到数学公式的证明,具体参考:
- https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
- https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
3. Secure boot镜像校验应用
如上图中两个要素:1. 信任链 2.加解密算法
在使用中某个两个节点的步骤为:
- 使用hash算法计算镜像的hash值
- 编译服务器上用私钥将hash值签名后,将签名的结果一起打进镜像中,存放在特定位置。
- 运行时上电后拿到公钥,将存储在镜像中特定位置的hash解密。
- 将解密的hash值与设备中当前镜像hash算法计算的hash值做对比,二者一致则校验通过。
总结:编译服务器上私钥签名hash值,签名后的hash值和公钥和hash算法一并打包入镜像。启动的时候从镜像读hash值利用公钥解密,解密后的hash值跟再重新算一遍hash的值是否一致。
- 私钥在编译时使用,编译服务器都是在公司里面的,例如你不可能闯入苹果公司把私钥从编译服务器里面抢出来,文明社会不能处处古惑仔。
- 在物理机存储上的东西我们可以拆机利用特殊设备读出来,物理机里面的存储的有公钥和hash算法,以及加密后的hash值。黑客通过读二进制反编译等技术知道了这些,没有私钥也破解不了。
有一个天马行空的想法:如果黑客在刚运行BL2的时候,让CPU停止运行,然后在内存里面找到BL2代码中去校验下一个image的代码,进行修改不去校验,然后继续运行,感觉就可以破解了。上网搜了这个想法感觉让CPU停下来比较难,运行代码的内存也是禁止写的,看来还需要硬件工程师的深度配合才能进行破解了。总之感觉造软件的的自己没秘钥自己也破解不了,造硬件的只要连接出来一些线应该能控制,但是封装技术可能会阻碍。另外提醒下破解secure boot是非法的。
另一个成熟的方法就是把芯片吹下来,换一个不带BL1校验image的芯片去启动,把信任根搞定就可以了。但是芯片厂商可能出厂的所有芯片rom都带校验,这就麻烦了,找不到芯片的替代了。另外efuse的熔断机制也可以去读芯片的型号进行破坏运行环境而无法正常校验通过而停止启动。
后记:
安全是个挺有意思的话题,有点hack的东西,道高一尺魔高一丈,总是伴随着技术的进步而进步,可以深究的东西太多,而且大多涉及到硬件的设计,后续再慢慢阐述。