0


从智能锁谈STM32安全技术

一. 智能锁的安全分析

1.1 安全的概念与保护的对象

1.1.1 什么是安全

安全分为2类:

  • 功能安全 Functional Safety- 保护系统不受那些随机故障或者系统设计缺陷的影响
  • 信息安全 Information Security- 保护系统不受恶意威胁而受损失

本教程讲的安全都指信息安全!!!

1.1.2 信息安全三要素

信息安全有3个基本属性(基本服务):

  • 保密性(C):该你知道,你可以知道;不该你知道的,就不让你知道
  • 完整性(I):真实可靠,信息没有被修改,没有被假冒
  • 可用性(A):总是可以访问这个信息,该用时也可以用

1.2 智能锁及安全分析模型

1.2.1 什么是智能锁

智能锁不用于传统的机械锁具,带有芯片进行控制

  • 结构:机械部分、主控芯片、电机单元、通信模组以及各种传感器
  • 开锁方式:密码、蓝牙、NFC(智能卡、手机、身份证)、生物特种(指纹、掌静脉、虹膜、人脸)
  • 连接方式:不再是孤立的存在,通过蓝牙、WIFI 或者其他窄带通信技术连接到云端
  • 安全:带有异常监控和日志

1.2.2 资产、弱点、威胁模型

分析设备所需要采用的安全技术,有一个非常成熟的模型,叫资产、弱点、威胁模型

  • 资产:一切认为有价值的对象
  • 弱点(也叫漏洞):系统的脆弱性
  • 威胁:利用系统弱点让资产价值损失的行为,让恶意第三方获得利益的行为

如果没有需要保护的资产,则漏洞加威胁,构不成风险,就不需要制定安全措施。所以,当谈及安全时,首先要甄别的是需要保护的资产是什么。

资产弱点威胁模型表明,所有的安全方案应该只针对有风险的系统的弱点来客服系统的漏洞。

1.3 现实中的攻击手段与 STM32 的技术措施

1.3.1 识别智能锁中的资产

智能锁中有哪些是需要保护的资产?从锁的用户角度看,第一重要的价值是锁的访问权。对于智能锁,保护的对象是开锁、关锁的本质:电机的转动。MCU 在什么条件下发出电机转动的信号,这个条件和判断的过程都是安全要保护的内容。密码开锁,则要检测这个密码是不是合法,密码合法的情况下,则发出这个开锁信号,所以这个密码就是具体的资产。

1.3.2 识别智能锁的脆弱性

辨别系统的脆弱性,一个方法,就是系统分解,从功能上分解,从生命周期上分解。例如将系统分解成不同的模块,就可以看到其中存在的弱点:

  • 例如联网:在带来便捷的同时,也代来了远程威胁,比如远程开锁
  • 例如软件及通信协议的设计:如果软件有bug,则很容易被远程利用
  • 例如锁的机械结构:比如开锁是通过简单的电机转动完成的,那么从理论上就有办法,绕过 MCU,也就是绕过所有信息安全的防护,直接给电机一个转动信号,锁就开了

恶意的人如何将威胁具体化形成实际的攻击方式呢?如何破解带芯片的系统,一般有2大类:

  • 物理攻击,按对系统的破坏程度分为:- 非侵入式攻击- 半侵入式攻击- 开盖攻击
  • 软件攻击,又叫逻辑攻击- 抓住系统中漏洞或者弱点,来攻破系统智能锁面临的威胁分类STM32 安全技术设备被破解设备安全STM32 SBSFU 安全启动与安全固件更新设备被假冒设备安全STM32 SFI 安全固件安装 STM32 SBSFU 安全启动与安全固件更新通讯被窃听通讯安全STM32 TLS 通讯安全通讯被篡改通讯安全STM32 TLS 通讯安全云端被破解云端安全云端处理云端被假冒云端安全通讯安全中对云端进行认证

二. 加解密技术

无论通讯安全还是设备安全,都离不开加解密技术。密码的背后一定是加解密技术。

加解密技术说白了就是变换,加密变换和它的反变换--解密变换。通过这种变换,可以提供之前所说的信息安全的三个属性(服务):保密性、完整性及可用性。在讨论加解密算法应用时所提供的服务时不大谈论可用性,因为算法总是要可用的。

同时,又把广义的的完整性细分为:狭义的完整性认证性,就是完整性和真实可靠分离了。这样,加解密技术也有三个属性(服务),也叫 CIA,保密性完整性可认证性(Authentication)。

能够提供保密服务的有对称密钥和非对称密钥,能够提供狭义完整性服务的有单项散列函数,能够提供认证鉴别类服务的有基于非对称密钥技术的数字签名,以及基于对称密钥的消息验证码MAC。

2.1 三类加解密技术与历史

  • 第一阶段:加解密是艺术,1949年以前- 没有统一的标准和理论支撑,加解密技术是基于过程(如隐写术、特制墨水),基于方法(移位和替换)的
  • 第二阶段:对称密钥,1949~1976年- 1949年,香农发表《保密系统的通信理论》,提出基于密钥的加解密算法。加解密过程可以公开,但密钥不公开
  • 第三阶段:非对称密钥,1976~至今- 1976年,美国2位计算机学家,一个缩写为 D,一个缩写为 H,发表了《在不传递密钥的情况下进行解密》的文章,就是 DH 密钥交换协议- 受DH密钥交换协议启发,3个人(名字首字母 R,S,A)发明了 RSA 非对称密钥的加解密技术- 非对称密钥技术是划时代的,解决了对称密钥的管理和分发问题

2.2 加解密的参数模型

对加解密技术定义模型,至少2方参加,加密方(Alice)和解密方(Bob)。A 和 B 之间的距离可以是空间上的(如北京到上海,要考虑到不同路由器之间的不安全通信连接),也可以是时间上的(如今天的你和明天的你进行一段加密与解密之间的对话)。

A对一段明文,用一个密钥进行加密,形成密文。B收到密文,用一个密钥进行解密,得到明文。

针对密钥的个数,有3种情况:

  • A 的加密密钥和 B 的解密密钥,是一样的,即 1 个,叫对称加密技术
  • A 的加密密钥和 B 的解密密钥,不一样,即 2 个,叫非对称加密技术
  • 特殊情况:A 加密了,B 解密不出来,0 个,叫单向散列函数

2.3 单向散列函数

在加解密技术里,散列=摘要=哈希

加解密技术中的摘要,具有2个特征:

  • 对原消息的任何改变都会导致摘要发生变化
  • 无法从摘要里推算出原始消息(原文)的蛛丝马迹,这就是叫单向函数的原因

单项散列函数的用处:

  • 提供完整性服务- 已知一段数据的摘要,如果数据被修改了,使用前计算下摘要,就知道数据是否被修改或替换了- 如果修改了数据,同时重新计算了摘要,此时检查不出来。此时的解决方法:- 通过硬件技术,比如一次可编程技术(如 OTP)将摘要写进去- 通过加密技术, 使用消息验证码 MAC(对称密钥技术)、数字签名(非对称密钥技术)
  • 存储敏感信息- 反面例子:某知名网站发生了信用卡大泄露,也有某知名网站发生过用户名密码大量泄露的事件。预防措施:不论是网站还是 MCU 人机界面系统,用户的密码应该使用哈希值。用户登录系统时,用户输入的值会被转换成哈希值,与系统里存储的哈希值进行比较,这样即使系统被攻破,用户的原始密码不会泄露- 正面例子:区块链系统

构成密码级的散列函数主要是MD5,SHA1,SHA2(包括 SHA-256 和 SHA-512,而 SHA-224 是 SHA-256 的删减版,SHA-384 是 SHA-512 的删减版),SHA3(输出长度: 512、384、256、224、64位)。SHA3 并不是要取代 SHA2,因为 SHA2 目前并没有出现明显的弱点。

CRC32 也是单向函数,但不是加密级单向函数,原因:加密级单向函数要求:不容易构造碰撞,就是已知明文和哈希值,不能轻易构造出另外一个明文对应同一个哈希值。

MD5 和 SHA1 在某种意义上被攻破了,不再推荐使用

在MCU开发中使用单项散列函数,通常就是2个参数:散列函数的类型、明文。

2.4 对称加密技术

对称不对称,指的不是加解密过程,而是加密和解密所使用的密钥。加密和解密所使用的钥匙一样,称为对称密钥

对称密钥技术的发展非常迅速,应用简单,使用广泛,各种各样的文件、文本、声音、图像的保密都是通过对称密钥技术来保护的。如果需要保密地传输一个固件,给固件加密的一定是对称密钥技术。如果上网需要 https,最终加密传输网页的也一定是对称密钥技术。

对称密钥怎么使用?对称密钥算法,如果接口封装的好,一定至少有2个参数:密钥、明文。这个密钥参数也很简单,如果是 128 bit,那么密钥长度就是 16 个字节。

通常密钥都很小,然而矛盾的是,大部分时候需要加密的内容都很长。几个 Byte 的密钥长度与几百 MB 大小的文件,如何使用长度很短的密钥对长度很长的文件进行加密呢?

有2种方法:

  • 将短的一方,变长。一种是把密钥无限扩展,让密钥的长度和明文长度对应起来。这种就是所谓的流加密
  • 将长的一方,变短。还有一种就是把明文分组,分块。分成一个一个块,这个块大小符合符合加密算法锁需要的明文大小。这就是经常听到的分组加密,分块加密

对于第一种方法,流加密,存在一个问题:如何生成密钥流和明文长度大小一样。理想情况下,有一份和明文一样长的随机数做密钥,这样当然安全,可是,密钥这么长,怎么记录,怎么传输,都是问题。现实中还是从一个密钥,通过一种算法,进行计算,生成密钥流。

对于第二种算法,分组加密,看上去很好,符合工程技术的常见思维,对问题分解然后针对每个小块处理。但块与块之间是否就是独立的关系呢?块与块之间独立,对带来安全问题。

对于分组加密,如果每个位置上的数据独立,加密出来的数据总是一样,会有问题。同样的道理,如果每次对一个消息报文加密出来的结果都是一样,也会存在安全问题。

  • 明文和密文一一对应,可以实施重放攻击
  • 明文和密文一一对应,可以实施明文攻击

所以加解密技术也要保证:对于一个文件同样的明文出现在每一处的解密不一样,一个文件今天和明天的加密结果也不一样。如何做到?通过一个初始向量,加上分组连接模式,而这个初始向量是可以公开的,而且每次不一样。

从模型上,如果使用一个对称密钥技术,至少需要2个参数:明文、密码。如果安全点,至少需要4个参数:明文、密钥、初始化向量、分组模式。

对称加解密算法有哪些?

  • DES:密钥长度 64 位,实际密码只有 56 位。每一块长度 8 字节。因为密钥长度太短,现在已经被认为不安全,一般不建议使用 DES 算法。如果一定要使用,建议使用3DES,就是把 DES 算法执行了 3 次,每次的密钥都不一样,这样密钥长度就变成了DES 密钥长度的 3 倍。
  • AES:密钥长度可以使用 128 位、192 位和 256 位。每一个分块的长度固定为 128 位。
  • SM1
  • SM4

对称加密算法常用的5组分组模式:ECB(电子密码本)、CBC(密文分组链接)、CFB(密文反馈)、OFB(输出反馈)、CTR(计数器)。

对称密钥具有简单、速度快的优点,但缺点也很明显。在加密模型中,加密方A(Alice),解密方B(Bob)。对称密钥技术下,A 和 B 必须共享同一个密钥,这样 A、B 之间才能进行保密通信。这就引出了一个问题,如果 A 和 B 之间隔了千山万水,怎么样 A 和 B 之间共享一个密钥呢?在这个问题的解决,非技术手段,就是把 2 个需要通信的召集在一起,面对面协商出一个共享密钥。但这个手段,在过去,信息技术不发达、互联网技术不发达的情况下,还可以勉强接受。在今天,我们和每一个网络服务之间都需要随时随地的进行保密通信,面对面协商根本就不可能,这就必须依赖技术手段。这个技术手段就是非对称加密技术,依靠非对称加密来传输对称加密的密钥。

2.5 非对称加密技术

非对称加密技术指的是加密、解密的密钥不相同。

一对密钥:

  • 私钥:保留的那个密钥
  • 公钥:开放的那个密钥。任何人都可以使用公钥,从而解决了密钥分发的问题。

因为每个人都可以使用公开的密钥对要发送给我的信息进行加密,对称密钥技术又叫公钥技术。

反过来(用私钥加密,发送给所有人),计算上可以,但是保密性已经不存在了,因为我已经把公钥公开给所有人,所有人都可以看到这个秘密。

但是这个方法也是有用的,如果你用私钥加密一段数据,其他所有人都有你的公钥,能够解开验证其中的内容,就证明是你发送的,这样你就抵赖不了,这就是签名技术。不过在签名体制里,就不能叫加密解密,叫它用私钥签名和用公钥验签。

公钥是用来加密的,私钥是用来解密的;私钥用来签名,公钥用来验签。

非对称机密技术,有 2 个主要的用途:

  • 保密性服务:公钥加密、私钥解密
  • 认证的服务:即数字签名,私钥签名、公钥验签

非对称加密技术提供的保密服务有在密钥管理方面的优点,是否可以取代对称加密技术呢?不行,原因在于性能。非对称加密技术都是基于一些数学难题,即使正常运算,运算量也很大。非对称加密的性能比对称加密要慢一个数量级,无法满足许多通信实时性要求高的场合。

非对称加密技术,性能不能,还有什么用呢?利用非对称密钥在钥匙管理的有点,来传输对称密钥。可以预先设计好的对称密钥,或者临时生成的会话密钥,通过非对称加密技术构造的管道传递出去。

非对称密钥技术目前有哪些成熟算法?

  • RSA:基于两个大的素数乘起来。私钥包括模数 n、密钥对 d 和 e;公钥包括模数n、密钥 e。生成模数 n 的素数 p 和素数 q,如果要应用余数定理快速计算,则可以保留p,q。但公钥只能包含模数 n 以及加密密钥 e。
  • 椭圆曲线 ECC:指20世纪90年代开始流行的椭圆曲线,是基于离散对数的求解困难。椭圆曲线参数是公开的(加解密的双方都可以拿到),不是生成的,私钥是一个随机数,公钥是私钥运算得到。椭圆曲线参数,包括椭圆曲线多项式的各项系数例如系数a,系数b,以及一个模数p,再加上椭圆曲线的一个参考点G。基点既然是椭圆曲线上的一个参考点,那就是就是以坐标的形式出现的,一个是x,一个是y,私钥则在椭圆曲线基础加上一个随机数,公钥是私钥和基点进行点乘后的结果,同样点乘的结果还是点,那么在代码里公钥就是具有x,y的形式,一个是x,一个是y。ECC 与 RSA 相比,RSA 的密码长度较长,比如典型的 2048 位、256 字节;ECC 同等加密强度小于 256 位、32 字节。在密钥长度上 RSA 比椭圆曲线高了一个数量级,10倍左右。
  • 密钥协商技术:DH密钥交换协议- 原理:乘方- 双方共同约定一个乘方的底数g- A(Alice)选取了一个随机数x,保密,基于g,计算g的x次方,然后把结果及g发送给B(Bob)。这里并没有把随机数x发送给B,而且因为求对数困难,任何人也无法从g的x次方结果里反推出来x- B收到g,以及g的x次方的结果。注意:B并没有收到x。B也选取一个随机数y,保密,同样以g为底,计算g的y次方,然后把g的y次方发送给A。B没有把y发送给A,而且因为求对数困难,任何人也无法从g的y次方结果里反推出来y- 最终,A有g的y次方以及自己的x,可以计算出g的y次方的x次方;B有g的x次方以及自己的y,可以计算出g的x次方的y次方。因为乘方的次序是没有关系的,所以这两个值是一样的,共同密码就协商出来了。而窃听的人知道了g的x次方,g的y次方,也知道底数g,还是没有办法求出对数,求出x和y,从而没有办法计算出g的x次方的y次方的结果。

但是所有的非对称密钥技术都不能防备一个问题:加入有人假冒Bob,怎么办?这就是中间人攻击的问题,只有采用数字证书才能解决。因为在任何情况下,我证明我是我,都是很难另不认识的人相信的。数字证书是靠一个可信第三方解决了这个问题。

2.6 数字签名与消息验证码

信息安全的服务除了保密性,还有完整性。信息安全的完整性可以扩展为狭义的完整性服务和认证服务。

如何实现认证服务?就是将单向散列函数与加密技术结合来实现认证服务。主要认证服务有 2 种:

  • 基于对称加密技术,称为消息认证码(MAC)
  • 基于非对称加密技术,称为数字签名

2.6.1 数字签名

数字签名的一般流程:现将要签名的文档,进行一个哈希运算(比如使用SHA-256,计算出32个字节的摘要),然后将摘要根据一定的标准,组成一个数据块,根据这个数据块,使用非对称加密技术的私钥对他进行签名运算。注意,签名的对象是哈希后的值,这样就避免了非对称密钥的速度慢的缺点。

签名总是随着明文发送给其他用户,因为公钥是公开的,其他用户如果需要验证这份文档是否完整可靠,只要用公钥解开签名中的哈希值,同时再计算一下明文的哈希值,如果这两个哈希值匹配,那么可以认为这个文档是完整并且可靠的。

注意:数字签名是有标准的,不能自己创造签名的算法和格式,否则很容易造成不安全。

2.6.2 消息认证码(MAC)

消息认证码有很多种实现与标准:

  • HMAC:将明文和密钥进行组合,然后进行哈希运算,运算后的结果再次与密钥进行组合,然后再进行哈希运算,这样运算后的结果才 HMAC。A 将明文和 MAC 发送给 B 时,B 可以按照同样的过程对明文做个 HMAC 运算。因为有了密钥的参与,若恶意第三方修改或者假冒明文,但是因为他们没有密钥,所以没办法计算出正确的 HMAC。
  • 基于AES的操作模式或者分组模式:AES 的 CBC 或者 GCM 模式都是将所有的明文块在加密的密文里通过某种关系联系在一起了,也就是最后一个加密块的结果事实上依赖于前面的所有的明文块。这就起到了狭义完整性的需要。这种情况下,块连接模式,或者操作模式、分组模式,就起到了类似哈希函数的效果。而 AES 本身是有密钥参与的,那么我们只要取最后一个块,或者单独计算的标记值,就成为消息认证码 MAC。

2.7 智能锁中用到的加解密以及STM32 CryptoLib

STM32 提供的加密库 X-Crypto-lib 支持单向散列函数、对称密钥技术、非对称密钥技术,通过了美国密码算法认证体系认证,在实现上安全性得到了保证,也适合在一些有认证需求的MCU产品上。

密码技术可以由软件实现,也可以由硬件加速软件加密库可以运行在所有的STM32平台上。STM32 特定型号有常用的算法加速,可以减轻内核负载,降低功耗。

除了 STM32 加密库,如果用户对认证要求不高,也可以采用一些第三方或者开源的加密实现,例如 mbedTLS 就包含了所有流行的加解密算法的实现。

2.7.1 算法工具

PC 上工具推荐 OpenSSL,是个命令行工具,支持所有的主流算法。

2.7.2 智能锁需要的加解密算法

从前面的安全分析中,可以知道,智能锁最需要保护的用户开锁的权利,制造商的知识产权,以及用户的隐私。

  • 开锁的权利事实上就是如何在系统中产生一把数字化的钥匙,这把钥匙可以不要求保密,但必须完整可靠。那么数字签名技术肯定是必须。对于 MCU,推荐使用椭圆曲线算法 ECC 以及 SHA256 来实现数字签名技术。
  • 知识产权保护涉及到固件安装或者升级时的保密,肯定需要对称加密技术,推荐使用AES 算法。同时,在启动时要检测固件是否已经被破坏,则需要数字签名技术(推荐ECC + SHA256)
  • 保护用户隐私,则应当使用对称密钥技术(推荐 AES)进行数据加密
  • 对外界通讯(包括云端)安全则需要依赖 TLS,TLS 几乎需要所有种类的加解密算法。

三. 通讯安全

现实的系统已经不再是信息孤岛,物联网更是提出了“万物互联”的口号。一个 MCU,小到和周边其他芯片进行协作,大到和云端进行通信,上报数据、接受指令,如何保证这些数据、指令不被修改、假冒、窃听,已经是一个基本需求。对于通信安全的主要部分,网络安全技术,在 STM32 MCU 上有个轻量级的 TLS 实现,已经被作为中间件集成到 STM32 CubeMX。

TLS 的实现本身是一个密码学技术的集成,非常复杂,实现了保密,完整,身份认证,消息认证的服务,大家最关心的还是如何使用 TLS。本章不仅介绍 TLS 的基本原理、身份认证、密钥协商、通信加密,并帮助你解决 TLS 中的问题,比如,选密码套件为什么不再推荐使用 AES-CBC 这个加密模式了;为什么推荐 ECDHE 进行密钥协商,不再推荐使用 RSA 的公钥加密了。

本章中介绍如何使用 STM32 Cube mbedTLS 来解决实际问题中的网络安全问题,更进一步,通过对 TLS 的来龙去脉进行剖析,可以使用类似于 TLS 的理念来解决几个典型问题:

  • 如何安全的与同一主板上外围芯片进行安全通信,例如 WiFi 模组,以及安全芯片等
  • 在特定 MCU 资源受限的情况下,如何构造轻量级的网络安全方案,例如,若不使用TLS,如何在网络上进行安全的通讯

本章重点:

  • 点对点通讯与端对端通讯的区别
  • 通讯有什么样的威胁,安全威胁来自什么样的弱点
  • 常用的 TCP/IP 模型和 TLS 位于哪个层次
  • SSL、TLS、HTTPS 是什么关系
  • 密钥协商如何保证前向安全
  • 身份认证的数字证书为什么需要链式结构
  • 如何避免中间人攻击
  • STM32 Cube TLS 从哪里获取

3.1 智能锁面临的远程威胁

网络联网可以小规模组网,也可以直接连上 Internet。

  • 小规模组网,则多个设备在某个空间区域组成一个内部的网络,这种情况下,信息在这个局部网络之间流动
  • 连入 Internet,则设备能够通过 Internet 向服务器发送消息,也可以从服务器接收消息。

无论设备是与内部的服务器通讯,还是与存在于 Internet 中的云进行进行消息交互,一旦组成网络,就面临通讯链路上的威胁(信息被窃听、被篡改、被假冒)。

什么样的系统弱点导致如此肯定系统会受到威胁?

  • 连接方式不安全连接方式主要分为2大类:有线连接和无线连接。- 对于使用集线器连接的局域网,可以相互监听通信数据包。以太网的所有通信信号都在同一个链路上传送,即使是设备 Alice 向设备 Bob 发送消息,同样的消息也会发送到该子网中的每一台终端。WireShark 软件可以用来分析数据包,也可以用来监听一个小网络内的所有节点之间的通信。可以使用交换机代替集线器来增强安全性,然而,传统线路上的数据还是明文的。- 对于无线连接,信号本来就散布在空中,本质上就是不安全的。比如 GSM 伪基站。
  • 中间节点不安全- 两个节点之间没有其他中间环节的通信,称为点对点通信 Point to Point; 通信中间可能经过各种各样的网关,各种各样的路由节点,称为端对端通信 End to End。- 在端对端通信中,一般需要经历很多个环节,例如我的手机通过 App 向我家中的空调发送一条指令,我的手机要通过移动运营商的基站,也要经过云端服务器,同时要经过一系列骨干网路由器,再连接我所选择的网络服务商,最后通过我家的WIFI 热点发送给我家的空调。环节多,任何一个环节出了问题,那么这个风险就存在了。假设其中的任何一个节点因为病毒,或者操作失误,就会导致我们的信息被泄露、被篡改。处在这种网络威胁中,如果智能锁仅仅按照传统的嵌入式设备来进行编程,那么开锁命令可能会被假冒。恶意的人可以捕获开锁命令,再次向锁进行发送;或者锁的密钥下发可以被其他人完整的获取。
  • 无法确定通讯对方是否可靠- GSM协议不去校验服务器端,无法知道基站的真伪,就冒出了很多GSM伪基站。- 因为通讯线路是对所有人可见,那么我们希望通讯线路上流动的数据是加密的,而且是可靠的。这里有一个安全考虑,仅仅保护最后一公里是否可以?是否可以相信骨干网上所有的中间节点都是安全的?结论是:不能做这个假设,骨干网上的节点也处在危险之中。要做端到端的加密和消息认证,而不是某一段点对点的加密与认证,好处:不管中间任何环节点被攻破了,或者被修改了,它都不能获取我们的秘密。

互联网上两个节点之间的通讯,永远面临一个问题,怎么相信对方就是我应该谈话的那个人呢?所有一定要对其进行认证,最好是双向认证,这些需求叠加在一起的一个典型实现就是 TLS。

3.2 TLS基本原理

TLS(Transport Layer Security),意指传输层安全,是解决网络安全的重量级武器。

TCP/IP 的四层模型:物理层、网络层、传输层、应用层。TLS 层位于传输层和应用层之间。

3.2.1 TLS的成长史

TLS发展历史:

  • SSL1.0,1994年是个不成熟的版本,网景公司并未对外公布
  • SSL2.0,1995年 网景公司对外发布,但是被认为是一个不安全的协议版本,具体表现在:- 哈希函数使用了 MD5- 握手消息没做保护,易遭到中间人攻击- 消息完整性检查和消息加密使用了同一个密钥- 可以很容易中断会话
  • SSL3.0,1996年
  • TLS1.0,1999年基于SSL3.0,可以看成 SSL3.1。该版本使用非常广泛。
  • TLS1.1,2006年主要是BugFix
  • TLS1.2,2008年- TLS1.1 和 TLS1.2 都没有发现明显的安全缺陷- TLS1.2 提供了较先进的待认证的加解密方式(AEAD)
  • TLS1.3,2018年- 将之前协议中加密传输前,额外的2个来回的握手简化为1个来回- 减少了密码套件的选择,不再有 3DES,RC4,AES-CBC,SHA1,MD5选择- 取消使用RSA做密钥交换的方式

3.2.2 TLS的一般原理

3.2.2.1 两阶段协议

TLS 是一个两阶段协议,一个是握手阶段,一个是应用数据加密传输阶段。

3.2.2.2 TLS子协议

  • Sub Protocol - Handshake Protocol 握手协议- ChangeCipherSpec Protocol 改变密码规格协议- Alert Protocol 警告协议- Application Data Protocol 应用数据协议
  • Record Layer Protocol 记录协议层

3.2.2.3 TLS握手

握手阶段有3个目标:

  • 关于密码算法达成一致
  • 关于密码算法的参数达成一致,因为密码算法的参数是一个主密钥分割而成,这里是如何生成主密钥
  • 验证对方身份,建立信任

过程:

  • 首先,客户端向服务器打招呼,发送Hello消息。Hello消息包括:①自己的版本号;②会话ID;③随机数(这个随机数是一个输入,用来创建会话的对称密钥,初始化向量以及消息验证码密钥);④一些建议的密码套件,包括什么样的密钥协商算法、签名算法、哈希算法、会话对称密钥算法以及验证码算法。
  • 服务器端看到客户端向它打招呼,要根据客户端打招呼的信息,决定选择哪种加密算法。虽然客户端有它的喜好,但是服务器也要根据自己的实际情况作出选择,然后同样要发送Hello消息,包括:①自己的版本号;②会话ID;③服务器生成的随机数(这个随机是一个输入,和客户端的随机数一道,作为输入,用来创建会话的对称密钥,初始化向量,以及消息验证码密码);④如果服务器返回所选择的算法;⑤同时返回给客户端服务器的证书;⑥如果必要,服务器可以要求客户端提供证书。
  • 双方打了招呼,则需要进一步协商生成一个主密钥的前密钥(pre-master secret)。这个密钥的生成方式又有两种,一种是由客户端生成,通过证书的公钥加密发送给服务器端;另一种是客户端与服务器端基于DH协议的各种变换,各自生成。密钥生成方式是在 Hello 消息中选择并达到一致的。
  • 一旦双方有了生成主密钥的基础(主密钥的前密钥、客户端随机数、服务器端随机数),双方可以根据一致的算法PRF生成主密钥。主密钥拆分成3组密钥:①消息验证码密钥;②初始化向量;③用来加密的对称密钥。
  • 这时客户端可以向服务器表示要改变加密方式,进行加密通讯。同样服务器也可以做同样的表示。
  • 于是握手阶段完成,双方达到同一个状态,同时获得了所有进行加密通信的参数,就可以进行第二阶段应用加密。

3.3 身份认证

3.3.1 证书

如果进行安全通讯,最基本的前提就是在通讯双方建立起信任。所以,在TLS握手中需要进行身份认证。

对于网络设备来说,每一个设备,都需要一个身份证,在网络中,被称为证书

证书有统一的国际标准来规范,即 X.509 证书,包括:

  • 证书的有效期信息:从什么时候开始,到什么时候结束
  • 证书的所有者信息,颁发给谁
  • 证书的颁发机构,由谁颁发
  • 证书的公钥
  • 证书的公钥算法
  • 证书的签名

X.509 证书,为什么要包含这些信息?这些信息有几个层次:

  • 一些简单可判断的信息- 对方很容易从证书里判断这个是否快过期了,这个证书的所有人到底是谁。如果这两个信息都不对,那么这个证书就会判断为危险- 要检查证书的颁发者,或者这个验证人。如果这个颁发机构本来就臭名昭著,那马这个证书就很危险。或者是自己验证自己,这样就无法从信任链上自动验证。
  • 与算法有关的信息- 允许对方来验证演示的算法材料,如公钥、公钥算法以及签名。- 签名不是一个简单的数字。首先,签名所用到的信息,他是除算法 ID 和签名自己之外的所有信息,也就是对有效期、所有者、颁发机构,包括公钥在内的所有信息,做了个哈希(摘要),然后再使用非对称密钥的私钥对其进行一个签名运算。这个签名运算的结果,没有正确的私钥的人,无法算出一模一样的结果。

当我们获得了一个服务器的证书,如何验证该服务器是否正确?是否被别人修改了?验证证书是否完整可靠,是通过验证证书的签名完成的。证书里包含了公钥,我们公钥将这个签名值解开,得到一个哈希值。我们同样要对除了算法 ID 和签名之外的所有信息做个哈希运算,然后看看这两个哈希值是否一样,就知道这个证书有没有被修改了。

那么证书即使是完整的,我们又怎么详细对方是证书的所有者呢?我们要发起一些只有数字证书的合法所有人才能完成的操作,并给出回应。例如,我把后面的会话密钥的前主密钥,就是通过这个数字的证书的公钥加密,或者与这个数字证书的私钥紧密关联,那么如果对方不是证书的合法所有人,就没法将对话进行下去。

3.3.2 中间人攻击

还存在一个风险,如果另一个人 E 也宣传我是我,他也产生一对公钥私钥,用私钥对证书签名。然后 Alice 向 Bob 申请证书,他在半路截胡,把自己的证书发给 Alice,然后继续请求 Bob 发出证书给他自己。同样,如果双向认证,Bob 向 Alice 申请证书,这个恶意中间人E,就同样把自己的证书发送给 Bob。此时,如果 Alice 拿到的证书不是 Bob 的,而是这个恶意中间人 E 的证书。同理,Bob 拿到证书也不是 Alice 的,也是这个恶意中间人 E 的。这就像人与人之间谈话,如果需要中间人传话,中间人完全可以无中生有弯曲双方的真实意图。

如果没法方法区别开来,这个恶意中间人 E 可以把 Alice 发出的所有的消息全部解密,因为Alice 使用的所谓 Bob 的公钥,实为恶意中间人 E 的公钥。接下来,E 可以看情况是否修改这个报文,也可以不加修改,只是看看有什么样的秘密,然后就把这样消息通过真实的 Bob 的公钥发送给 Bob。Bob 完全不知道有个恶意中间人 E 盯着 Alice 发送给 Bob 的一切。以此类推,对于 Bob,恶意中间人也可以进行同样的操作。这样 Alice 和 Bob 都以为正在和对方进行保密通信,实际上数据已经被窃听,也可能被修改,这就是所谓的中间人攻击

如何应对?一种方式是使用可信的方式交换证书,即只相信那些手工检查后,手工接受的证书。对于 STM32 等资源受限的设备,有时候应该采用这种方式:直接先烧录一些证书,同时关联某些服务器。它表示我们在连接服务器时,会直接相信其关联的证书。但这种方式没法用在要跟很多并无直接的面对面接触的网站,因为数量就过于庞大,且无法更新。而且,并非所有人都具备这个能力去检查证书的真伪,这就需要一个根证书和证书链。

3.3.3 根证书和证书链

我们需要一个根证书,这个根证书是我们所信任的。如果没有这个根证书,那么就等于没有一个集中式的信任。当然,也不是没有办法,例如,把这个证书抛出来,让大家投票,如果打分人觉得可信,我们就接受它。这个分布式的信任解决方法,是区块链中所用到的技术。这个技术从效率上讲是非常低的,并不适合于一般的网络情况下。因此,根证书是非常必要的。

根证书从哪里来?有一个机构叫 CA 授权中心,是可信的。我们就在设备里预先烧好这个根证书。这样,当我们收到一个外部的证书时,可以从陌生证书的链条,一级一级向上验证,看看最终能不能归结到这个根证书。如果能,那么这个证书是可靠的。

有些网站的证书不能通过验证,就会弹出警告给用户,问是否你能接受,这种情况要小心处理。比如,一个代理服务器希望你接受它的证书,那么通过该代理服务器的所有访问都可能被破解。因为这个代理服务器充当了中间人的角色。

如果去抓 TLS 的握手协议报文,在服务器返回的证书链中,一般至少有两个证书:服务器的证书、CA授权中心的中间证书。不能直接发送根证书,否则自己证明自己,很容易被伪造。

3.3.4 单向认证与双向认证

很多网站服务器提供内容的服务,并关心连接的客户端是谁,也就是说,谁都可以连接它;而客户端关心连接的服务器是否真实。例如我想连接工商银行,需要确定是工商银行,否则,这个发送的密码及产生的交易,就可能存在风险。

对于物联网设备,你能否享受云服务,而通常云服务不是免费的,那么云端一定要认证设备,否则这个云就是免费的云。同时,如果我的物联网设备的很多数据是有价值的,那么物联网设备希望云是可靠的。这种情况下,物联网设备就需要验证这个云的证书。因此,在大部分情况下,物联网设备和云端,在通信安全上执行的是双向认证。

3.4 密钥协商与通信加密

3.4.1 密钥协商

通讯线路上如果要对消息进行保密,我们需要对称加密;如果需要对消息进行认证,则需要对称密钥。所以最终所需要的密钥有3个:

  • 消息加解密的对称密钥
  • 与对称密钥相关的初始化向量
  • 对消息进行认证的密钥

TLS 这3个密钥是从一个叫主密钥 Pre Master Secret 通过某个固定算法 PRF 伪随机函数派生而来的,而且这3个密钥,各不相同。否则,一个被破解,会导致另外一个也被破解。

那么这个主密钥是从何而来的呢?从一个前主密钥 Pre Master Secret 通过某个固定算法 PRF 伪随机函数派生而来,这个伪随机函数的输入包括这个前主密钥、加上双方Hello消息里的随机数。

Pre Master Secret 的产生则具有不同的方式和不同的安全效果。

第一种是基于 DH 协议。DH 协议允许在不安全的通信信道上,发送双方的公钥,然后根据双方的公钥及各自不公开的私钥,计算出一致的 Pre Master Secret。

第二种是基于 RSA 协议构成的公钥加密通道。握手阶段,服务器回复的Hello消息中包括了证书,证书可以用来验证身份,同时证书提供了服务器的公钥。客户端可以采用这个公钥,将前主密钥 Pre Master Secret 加密的信息传递给服务器。这样双方可以获得获得一致的前主密钥 Pre Master Secret。使用 RSA 协议进行密钥协商因其不具有前向安全性,在TLS1.3 中已经被删除。前向安全:长期使用的主密钥泄露不会导致过去的会话密钥泄漏,而那些会话的内容也不会因某个主密钥的泄漏而被破解。在今天的海量存储的时代,是存在先保存,后破解的可能性。例如,有人暂时不能破解通信双方的密钥,但是当其存储所有双方的来往信息,期望有一天能攻破某一方的私钥后,获得某个密钥,从而破解通讯内容。假设我们使用静态的固定密钥,这个静态的固定密钥总会存在某个地方,如果有一天,黑客利用 OpenSSL 的 HeartBleeding 漏洞或者其他手段攻破这个系统,那么以前的所有存储的会话就破解了。我们认为这种系统就不具有前向安全性。

单纯的 DH 协议是不能防备中间人攻击的,因此 DH 协议中所使用的公钥必须要经过认证。在这种情况下,我们一般使用基于椭圆曲线的 DH 协议就是 ECDH,这是第三种产生前主密钥 Pre Master Secret 的方式。

如果使用证书中静态的公钥来进行 ECDH 密钥交换,那么静态私钥与公钥对的多次使用,会增加密钥对被破解的风险,从而将服务器的证书置于风险之中。可以通过为密钥交换生成临时的公钥私钥对来防范风险。

3.4.2 通讯加密

双方都获得所有的安全参数之后,就可以进行应用数据的加密。对于应用数据,仅仅是加密肯定不够,还存在中间人攻击的风险,中间人有可能篡改消息,如重放或者打乱顺序等等。所以,还需要对它进行消息认证。那么,是否只要使用了认证就完事大吉呢?答案是如果使用的认证方式不对,如 AES-CBC,依然可能会遭到中间人攻击。

在最早的TLS实现里,实现加密的方式是:①先对消息认证,也就是计算 MAC,包括头部、序列号、消息本身;②然后进行补齐;③最后进行加密。实质上,这种方式存在很大问题,很容易遭到中间人侧信道攻击。

来看一看具体的算法内容。

填充算法依据是 PKCS#5 或者 PKCS#7 标准。对于 AES, 块的长度固定为16个字节。那么如果需要填充1个字节则是为 01, 如果是需要填充2个字节则是 02 02,如果需要填充 3 个字节则是 03 03 03,那么如果什么字节都不需要填充是16个0,也就是0的个数是分块的长度。所以对于 AES 算法,填充后最有1个字节,只有16种可能。

典型的加密认证算法有 AES-CBC,但是 AES-CBC 这个块加密模式的特点是密文块连接模式,也就是前一个密文块和后一个明文进行 XOR,然后再去加密。

在TLS实现中,TLS 服务器会按照下列顺序相应:①解密;②检查Padding格式是否正确;③检查MAC是否正确。第二步对最后的填充字符给出响应,会告诉我们这个填充字符是否正确。我们在此不用实际的返回码,使用1简单表示正确,使用0简单表示错误。那么:

  • 固定的填充格式
  • 有单独的错误码1或者0;
  • CBC的密文块连接方式

就构成了TLS的一个协议层次上的弱点。

AES-CBC 解密的过程是,先单独对一个密文块进行 AES 解密运算,得到中间结果,然后让中间结果与前一个密文进行 XOR。由此看出,改变前一个密文会影响输出的明文。

伪造前一个密文块,接上一个正确的密文块,看看出会出现什么。第一个目标,是让服务器相信明文中的填充字符是 0x01。我们不知道解密的中间结果是什么,但是我们只要不停地改变伪造的密文的最后一个字节,总共有 256 种可能,总有 1 次让服务器输出 0x01。一旦输出为 0x01, 服务器就会返回 1,表示正确,接下来尝试结束。服务器告诉我们,在这种情况下,明文的最后一个字节是 0x01,前一个密文是我们伪造的,我们是知道它是多少,只要将 0x01 与伪造的前一个密文的最后一个字节 XOR,就可以得到正确的中间结果的最后结果。如法炮制,很快就可以得到整个正确的中间结果,这时,当将前一个密文换成保存的那个块后 XOR,在没有密码的情况下,我们就知道了明文。

这时,我们只需要尝试 256 x 16,而不是 256 的 16 次方,即可得到一个块的明文。这不是说 CBC 不安全,更不用说 AES 不安全,但是将①固定的填充格式;②有单独的错误码1或者0;③CBC的密文块连接方式加在一起就不安全了。

这就是 TLS1.3 去掉 AES-CBC 模式的原因。理解此原因后,在使用 TLS1.2 的过程中,选择密码套件应该有清晰的方向。

3.4.3 STM32 CubeTLS库

TLS 在 PC 上的实现就是大名鼎鼎的 OpenSSL。对于 STM32 MCU,根据单片机本身 Flash 及 RAM 大小,可以达到不同的实现。下表列出了各个公司支持 STM32 的 TLS 实现。
公司方案名称armMbedTLSCypherBridgeEmbedded TLS SDKHCCVerifiableSSL/TLSOryx EmbCycloneSSLSEGGERemSSLSTSTM32 Cube-TLSwolfSSLEmbedded SSL Library
对于商用用途免费主要是 MbedTLS,同时 MbedTLS 是 CubeMX 所支持的中间件。

如何将 MbedTLS 在 STM32 上使用起来? ST 推出的每一个云连接的开发包,都包含 MbedTLS,里面的代码是可以运行的。如果觉得云连接开发包里的 MbedTLS 太复杂,可以从 CubeMX 出发,自己修改 MbedTLS 的配置文件。如何配置?主要是密码套件的选择,强烈推荐使用:

  • 传输前主密钥 Pre-master secret,选择基于椭圆曲线的 DH 密钥交换协议,不要选择基于 RSA 加密方式
  • 身份认证可选择基于 RSA 或者椭圆曲线的数字证书
  • 选择带认证的加密方式,如 AES-GCM
  • 选择 SHA256 作为密钥派生的哈希函数

如果还是认为 mBedTLS 太大,怎么办?可以考虑其他协议。因为 mBedTLS 所提供的安全服务也是保密、完整、可靠。如果事先想服务器的证书或者等价的身份信息写进 STM32,将客户端的证书或者等价的身份信息写进服务器,则握手这个环节将大大简化。对于在 MCU 和 云端之间定义更加轻量级的 TLS,国内一些知名互联网公司已着手进行。

3.5 STM32 通讯安全在智能锁中的应用

对于网络安全,取决于智能锁的网络拓扑如何构造。一般情况下,智能锁有3种节点:①智能锁本身;②智能锁服务器;③手机用户。

那么,这里存在多个通讯联系:

  • 智能锁设备与服务器
  • 手机通过服务器连接智能锁
  • 手机通过蓝牙连接智能锁

为了保证通讯安全,希望智能锁设备与服务器之间的连接,是通过 TLS 连接。而手机通过服务器连接智能锁,需要通过两个阶段:通过 TLS 进行加密,在服务器进行解密,然后再次通过 TLS 进行加密。前提是,我们能确定服务器是安全的,或者要对服务器进行相应的安全服务。这种用法可以说明两个问题:

  • 安全是一个整体。必须考虑每一个部分的安全。
  • 没有最完美的安全方案。TLS 协议是在传输层之上,应用层之下。对于那些不是传输层直接连接的两个节点之间的通讯,TLS 协议需要应用层介入进行解包再打包。

在这种情况下,传递的信息依赖于中间二传手,也就是服务器的安全性。换言之,当通过手机给智能锁发送指令,这个安全类型,尽管使用了 TLS,并不能算是真正的端到端的安全。

如果用手机通过蓝牙直接连接智能锁,发送开锁指令,并不需要过于关于保密性。蓝牙通讯必然是一个很短的距离,这种场景下,恶意的人并不一定通过无线这种方式窃听;即使通过无线方式窃听,因为距离很近,也很容易被发现。但是,指令的完整可靠性必须得到保证,否则,指令可能被记录,然后再在某个时间被重放,这样智能锁就有可能被恶意打开了。指令的完整可靠性可以借鉴 TLS 使用随机数,加上时间,加上序列号进行验证码 MAC 的计算。

四. 安全启动

本章要点

  • 安全启动的概念
  • 安全启动的重要性
  • 安全启动具有不同的实现形式
  • 如何使用 STM32 的安全技术来实现安全启动

4.1 安全启动的概念

4.1.1 什么是安全启动?

安全启动总来讲的就是确保设备总是在运行可信的软件

安全启动 = 安全根 = 根安全 = 信任根 = 根信任

安全启动,前面加了安全二字,意味着这个启动部分具有全部或者部分的安全属性

  • 保密性: - 安全启动的代码,部分或全部,不应该被外界所了解;- 安全启动可以要求后续代码或者低权限代码,不能访问安全启动的内容;
  • 完整性:安全启动的代码,不能被修改、破坏,也不能被跳过;
  • 可靠性:启动所执行的代码总是来源可靠的代码。

从而,安全启动至少有 2 层含义:

  • 启动位置是固定的;
  • 启动顺序是不能改变的

也可以进一步要求:

  • 启动代码对后续用户软件应该是不可访问的,或者说是不可见的。

4.1.2 仅有加密是不够的

安全是一个整体,对于一个 IoT 设备,谈到安全就必须考虑:

  • 设备安全:要将安全作为一个整理考虑。如果仅仅使用加解密算法,设备也还是会很容易被假冒、被破解 - 威胁1:修改启动顺序,举个栗子:代码在启动时,有一段算法,先比较某段数据的验证码 HMAC,再决定是否使用这段数据。如果没有安全启动,就意味着可以执行顺序,那么黑客就可以直接修改这段并直接跳转过去。实际的跳转代码最后就是一个标志位,很容易找到。软件的功能被破解了,这个设备也就被破解了。- 威胁2:修改启动位置,举个栗子:代码已经投产,烧好在Flash中。只要换个启动方式,比如使用 JTAG 从 RAM 启动,将代码读出来,这样资产就被破坏了。如果里面也包含通讯安全的密钥,那么这个设备的身份也被偷走了。
  • 通信安全 - 孤立的通讯安全是不够的,必须是整体的安全
  • 在通讯安全里,一直假定,设备和云已经做了相应的安全保护;或者设备和云端都是在安全的环境里运行,不会受到恶意的破坏。否则,即使使用了具有前向安全的 ECDHE 协议来协商密钥保证通信安全,如果两端的私钥都可能被黑客掌握,那这个算法又能有没什么用?
  • 云端安全

安全启动是一个比较宏观,或者说是一个比较层次比较高的安全服务,实现它有多种方式

如果在 STM32 使用了 RDP level 2,从某种程度上已经实现了一个安全启动。但还需要注意:

  • 是否充分使用了 STM32 的硬件安全功能:RDP, WRP, PCROP, Secure User Memory, FireWall, MPU, DAP SW enable/disable, anti-tamper, 甚至 STM32L5 的 TrustZone 等。
  • 是否为后续阶段提供了安全执行应该有的安全服务。

4.1.3 安全启动解决了什么问题

攻击者会使用软件攻击加上物理攻击的方法来攻击系统,目的当然是为了实实在在的利益,可能是想获取固件代码,以节省研发成本;可能是想获得身份秘钥,让其他非授权设备获得相应的服务;也可能是想获取更高权限,获得没有购买的服务。

为了保护设备不被破解,不被假冒,我们要采取措施防止物理攻击和软件攻击:

  • 最简单的物理攻击是 JTAG 连接,因此在产品出厂之前一定要确保 JTAG 完全连接不上,或者 JTAG 满足一定的条件才可以连接。
  • 安全启动通过加解密技术检测信任根的代码是否被破坏或者替换,并拒绝有异常的信任根代码执行。最常见的软件攻击是恶意软件注入以及缓冲区溢出, 在安全启动里要加上相应的动态保护。例如,为了保护安全启动的身份密钥,可以将安全启动与后续的用户代码隔离,那么用户代码就没有机会向安全启动代码里注入恶意代码。
  • 安全检测为系统进一步执行提供检测与判断,保证下一级代码在执行之前也是完整可靠的。这样,从安全启动开始,完整和可靠的特性一级一级的传递下去,也就是说,安全启动构造了一个软件执行的信任链。

4.2 STM32安全启动架构

4.2.1 安全启动在STM32中的实现

安全需要一个起点。安全启动要求启动的位置一定是固定在某个地方,启动位置靠什么保证?必须靠硬件。软件本身的特点,决定了它很容易被修改,即使做了加密和加扰,破解难度也依然比硬件低很多。所以,安全启动一定要靠硬件来保证,脱离硬件谈安全启动,基本上是不可能的

实现 Root of Trust(信任根)通用的做法是什么?一般是,芯片有个 Bootrom 启动只读存储区。硬件的设计保证,芯片加电启动一定是从这个只读甚至不可见的 Bootrom 里执行。

事实上,保持安全启动的原则不变,实现方式可以不一样。Bootrom 如果直接固化在芯片里,会减少灵活性,无法适应 MCU 的广泛应用场景。所以,STM32 采用的是使用 STM32 硬件技术,允许客户在开发过程中形成 Bootrom,Bootrom 具有安全启动的几个特征:

  • 启动位置是固定的
  • 启动顺序是不可修改的
  • 启动代码或者核心部分对后续代码是不可见的

4.2.2 安全启动的STM32软件架构

STM32 SBSFU 架构分为3层:

  • 驱动层,包括 BSP 以及 STM32 HAL
  • 中间件层,包括 STM32 Cryptolib,以及安全引擎。安全引擎提供了 Secure Key Storage 安全存储密钥的服务,以及安全进行加解密操作的服务
  • 应用层,包括 SBSFU,以及用户固件。其中,安全启动与安全固件更新部分,包括了安全配置、用户固件加载,以及为用户固件更新所准备的固件烧录功能

本章重点是安全启动,重点讨论 STM32 如何构建一个安全环境,如何保证下一级固件的安全执行。

4.2.3 在STM32中固定启动位置

复盘下系统的初始执行过程:系统上电, STM32 MCU 根据 BOOT0 和 BOOT1 管脚的配置,再加上软件 BOOT 寄存器,可以确定系统应该在什么地方启动,存在三种可能:

  • 从系统内存 System Memory BootLoader 启动
  • 从用户 Flash 启动
  • 从 SRAM 启动

理论上系统内存可以充当信任根,然而,考虑到系统内存所达到的安全级别和灵活性,我们不希望它从系统内存启动。从 SRAM 启动,如果没有信任根的话,那么这个 SRAM 的安全无法得到保证。因此,只有唯一的选择,也就是 STM32 目前的安全设计方案:将系统固定在用户 Flash 启动。

如何将系统固定在用户 Flash 启动?

根据 STM32 手册,需要使用读保护 RDP。不使用 RDP,首先 boot pin 可以重新拉高或者拉低,从而构成不同的配置;其次 JTAG 可以连接上去,让 STM32 从 SRAM 启动。

读保护 RDP 要设置成2,才能够确保启动位置不会被修改。RDP 设置成 1,是保护 Flash 内容不被读出,并不能将启动顺序固定到用户 Flash。 例如, JTAG 依然可以连接上 STM32,boot 脚依然可以被重新修改。

所有的 STM32 系列都具备 RDP 功能,唯一需要注意的是 STM32F1 系列不具备 RDP level 2 这一设置。

例如,不设置 RDP 为 2,也可以实现可信的安全固件安装。在安全固件安装中,系统内存提供了 RSS 根安全服务,在这个过程中,系统内存充当了信任根,这就是一个RDP 级别1 的信任根。

再例如,如果使用 STM32L4, 将堆栈放在 SRAM2 中,那么在 RDP 1 的情况下,想通过 JTAG 攻击 STM32,例如读 Flash 内容,或者对 Flash 修改,基本不可能。

总结一下,构造安全启动,必然需要设置读保护 RDP,一般设置成2,有些情况下也可以设置为1(这时要确保 SRAM1 不包含任何安全敏感信息)。

4.3 STM32安全技术RDP与MPU

4.3.1 检查安全配置 & 构造安全执行环境

与一般的 MCU 启动比较,STM32 安全启动多了些步骤。

  • step 1:检查静态安全环境,如 RDP level、PCROP、WRP
  • step 2:配置动态安全配置,如 Firewall、MPU、IWDG
  • step 3:验证用户固件

这个安全的执行环境保证了启动的顺序不会被改变,以及启动代码的保密性。

4.3.2 防外部攻击

此处谈的外部攻击,不是把芯片剖开,使用光学显微镜进行拍照,从而进行逆向工程;或者使用激光对芯片线路进行切割或者连接。这种攻击的成本非常高,需要丰富的专业知识,也不是通用 MCU 所设计防护的目标。没有绝对的安全,所做的防范都是根据安全风险分析而制定的。

MCU 的内置 Flash、内置 SRAM 的特点,决定了 STM32 可以很容易做到防外部攻击,因为外部攻击的主要手段来自 JTAG。而不像非单片机那样,需要担心外置的 Flash, 是否直接被 Solder off,然后放在其他读写器进行修改;也不用担心外置的 SRAM 会不会被攻击者进行探测。

STM32 防外部攻击主要是通过将 RDP 设置成 2

4.3.3 RDP

读保护 RDP 是 STM32 系列一个非常重要的安全设置,而且设置特别简单。

芯片默认的 RDP 一般是 level 0。此时,任何人都可以通过 JTAG 访问所有资源,可以擦除、读写 Flash,可以进行调试,可以通过 JTAG 直接从 SRAM 启动,也可以修改 boot pin 脚从 system memory 进行启动。

若需要达到的一般化的安全等级,RDP 要设置为 1。这个时候通过 JTAG 就没有办法直接访问 Flash 了,这种情况下,固件的知识产权收到了保护。

为了更好的安全性,一般将 RDP 设置成 2,原因:

  • JTAG 不能连接 STM32
  • 选项字节不能再修改,例如 WRP 所保护的区域就是一个真正的 ROM 了,再也不能修改了。

注意:RDP 设置成 2 就再也不能回退了,所以,如果代码还没有测试好,例如通过软件更新系统的功能还没测试好,那么就不要着急把 RDP 设置为 2。

RDP 设置非常简单,可以通过 ST-Link 直接选择,也可以通过 STM32 Cube Programmer 来完成,也可以在程序里直接完成。

4.3.4 外部攻击的其他手段

外部攻击还有一些手段,如操纵电压、操纵芯片的温度、操纵时钟,将设备打开让开发板的布线暴露,针对这些攻击,STM32 也有相应的技术,这类防护手段归结为系统监控类,后面会提到。

4.3.5 防内部攻击

内部攻击的表现形式一般是注入恶意代码,或者缓冲区溢出。通过这些攻击手段,攻击者的目的就是为了盗取密码、固件代码或者其他敏感信息,达到破解设备或者仿冒设备的结果。“外敌易挡,家贼难防”,如果有恶意代码混进来,需要额外的多项 STM32 安全技术来保护。

4.3.6 MPU

内存保护单元是来自 ARM 的一项技术,STM32的所有系列(除 STM32F0)都有该硬件单元。

MPU 是一种权限设置保护。通过 MPU,可以将内存分为多个单元,每个单元可以单独的设置权限。例如,若将 RAM 设置成不可执行,那么代码注入和 Buffer overflow 到 RAM 就不能工作。

MPU 是一种内存保护技术,主要保护内核所访问的内存,缺点:①它不能保护DMA;②它不能自己保护自己。默认情况下,后面的代码依然可以修改MPU设置,也就是说,如果前面使用了 MPU 保护一段内存单元,后面的代码还可以修改 MPU 设置将保护去掉。

解决这个问题的办法是结合内核的特权模式与非特权模式。STM32的编程手册里写到,某些寄存器只有在特权模式下才能访问,这些只有在特权模式下才能访问的寄存器,就包括了 MPU 的控制寄存器。即只有特权模式才能修改 MPU 的配置。如果将整个代码分为2部分:安全敏感性操作工作在特权模式,一般用户固件在非特权模式下。那么非特权模式下的用户代码就无法修改 MPU 寄存器,也就无法将配置好的 MPU 关掉或者修改掉。STM32 SBSFU 对 STM32F4、STM32F7 就使用了 MPU 结合特权用户模式,来达到更好的安全效果。

值得强调的,MPU 作为动态保护技术,无法单独存在,必须结合防护外部攻击的 RDP 一起使用。但是 MPU 也可以用来做开发诊断。 MPU 的分区权限设置这个功能,可以帮助开发人员定位错误。

4.4 STM32 防内外攻击技术

4.4.1 Firewall

STM32L0 和 STM32L4 提供了 Firewall 硬件。Firewall 硬件提供了调试门 Callgate 技术,也就是说,对于安全敏感的代码与操作,只有一个入口。任何不通过这个接口,而对受保护的代码与数据进行访问,则会导致系统重启。Firewall 形成了一个围墙,形成了一个城堡,同时提供一个入口,来提供运行时内部的保密、完整、可靠、可用等特征。

通过 Firewall 单一入口功能,可以形成对外的简单接口。安全接口简单,所以安全敏感的操作细节就不会对外泄露;因为外界只能通过接口访问,那么在内部操作之中所用到的密钥也不会泄露。

Firewall 通过启动代码激活后,就不能再关闭,该功能可以保证安全敏感的代码和数据在上电周期内不会被非法访问。这不同于 MPU(一定要配合特权模式和用户模式的分离机制,否则设置会被修改)。

Firewall 的缺点是中断处理。中断处理需要读取中断向量表,如果中断向量表放在防火墙之外,则会造成防火墙之外的读操作,那么根据防火墙的机制,则系统重启;如果中断向量表放在防火墙之内,则中断处理函数需要继续调用其他函数,那么所有其他函数都要放到防火墙内。考虑到实际系统要处理的中断情形复杂,在实现中,一般进入防火墙之前,会将中断关闭。

同样的,作为一个动态保护技术,Firewall 不能单独存在,必须结合防外部攻击的 RDP 一起使用。

STM32 SBSFU 在 STM32L4 上利用了防火墙增强安全,使用 STM32L4 SBSFU 需要考虑中断带来的影响。

4.4.2 既防外又防内

STM32 也提供了不少内存保护技术,既可以防护内部攻击,例如恶意代码注入,也可以在 RDP 为 0 的时候起到一定程度的对外防护作用。

4.4.3 PCROP

我们先来看几个场景:

  • 设想某一个公司,他有一个非常好的算法,所以不希望普通权限的员工了解它。但是这个算法又要在开发中被其他模块或者说其他员工使用。源代码是肯定不能提供,提供二进制文件,也面临被复制的风险。到底怎么办?
  • 设想某公司有一个模块,需要提供给其他公司在此基础上进行二次开发。需要对模块进行保密,源代码肯定不能提供,提供二进制代码也有被复制的风险;不提供的话,其他公司有没有办法进行二次开发。到底怎么办?
  • 安全启动包括校验流程与密钥。为了安全,自然并不希望其他人用任何手段来了解。

这几个问题本质上都是同一个问题:希望能够保护一段代码,既不能被外部 JTAG 读出来,也不能被后续软件访问。此时,PCROP 就可以发挥作用了。

很多 STM32 系列都有的 PCROP(专有代码读出保护),即使是默认的 RDP 级别(也就是 level 0),一旦对某段区域设置了 PCROP 保护,这段代码也无法读出来。这可以用来解决这个问题。

PCROP 是将一段区域设置成可以执行,那么任何代码向该区域进行读写操作,都会引起系统重启。PCROP 在很多 STM32 系列都得到了支持,如 STM32F4、STM32F7、STM32L0、STM32L1、STM32L4、STM32H7、STM32G0等。不同的 STM32 系列 PCROP 配置,可能会有所不同,有的是按照大小设置,有的是按照 Sector 扇区设置。

PCROP 具有较强的既防内又防外的功能,适合保护算法。同时 PCROP 配合 RDP 以及其他动态保护技术(如 Firewall、MPU),可以用来构造保密区域来保护密钥。

PCROP 的硬件机制只允许指令读取,这就意味着:需要 PCROP 保护的代码,常量要一到保护区域之外,或者将常量变成代码。同时,在编译时,要让编译器不要生成文字库。对于文字库编译选项,不同的编译器,名称会有不同,Keil 叫 Execute-only,IAR 叫 no data in code memory,用户需要根据不同的编译器,勾选不同的选项。

PCROP 所保护的区域只允许执行,也就意味着无法对他单独擦除。如果需要更新,需要在 RDP 从 1 到 0 进行整片擦除后,才能重新配置。整片擦除时,也可以选择保留 PCROP 所保护的区域,不做改变。

STM32 SBSFU 使用了 PCROP 来保护密钥。

4.4.4 Secure User Memory

安全启动构造了一个信任链,一级一级来检查,一级一级的传递信任。尽管使用了加解密技术验证了后续环节,后面的环节应该是可靠的。但请记住,没有绝对的安全。我们不知道,是否有其他的攻击手段可以让恶意代码还是混进来了。

所以,对于安全链条的起点——信任根,有保密的需求。为了安全,我们不希望信任链后续的环节来访问这个最初的安全启动代码。即如果有办法将安全最敏感的信任根与后续的环节隔离开,那么系统会安全很多。

如何让安全启动的代码和后续代码彼此分离?

  • 动态保护技术 MPU 结合特权模式与用户模式构造隔离防护
  • Firewall 可以构造防护隔离防护
  • Secure User Memory (用户安全存储),设置很简单

最新的多个 STM32 系列(STM32H7、STM32G0、STM32WB、STM32L5)提供了用户安全存储功能,在不同的STM32 系列可能有细微的差别,同时功能也有细微的差别。

Secure User Memory 功能通过选项字节设置,属于静态防护。Secure User Memory 一个最主要的功能在将启动与后续代码隔离开来,也就是放在 Secure User memory 的 Flash 里的代码,在执行完自身后,可以通过一个服务调用或者一个寄存器设置,让后续其他非可信任代码无法感知安全启动代码的存在,也就是后续代码代码再也无法访问之前的代码。这个功能可以用来提供安全启动代码的保密性,同时可以防止恶意代码去修改安全启动增强完整可靠性。

Secure User Memory 既能防内,又能防外,因为:

  • 在某些 STM32 系列上,一旦设置了 Secure User Memory 所保护的内容就再也不能被 JTAG 访问
  • 另外的 STM32 系列上,一旦设置了 Secure User Memory 大小以及 UBE 被设置,即使 RDP level 1,JTAG 再也无法连接上,相当于达到 RDP level 2 的效果

4.4.5 WRP

WRP 可以将某段区域或者空白区域设置不可写,那么代码通过协议栈错误擦除软件或者写入到空白 Flash 就不可能发生。WRP 既可以防内被程序误写,也可以防外被 JTAG 擦除。

所有的STM32 系列都有 WRP 功能。

WRP 是静态设置,直接通过选项字节进行设置。WRP 的选项,在 RDP 不为 2 的情况下,是可以被修改的。如果想一次就达到 Read-only 的效果,需要配合 RDP 为 2 使用。

4.4.6 系统监控

如果攻击已经发生,最重要的事情是能够监测到,对此,STM32 提供了多想安全监控技术:

  • IWDG 独立看门狗,如果外界攻击发生,导致系统不能及时喂狗,则系统会重启
  • Anti-tamper,可通过将 MCU 防篡改 管脚连接到设备外表面, 若设备被强行打开,则会触发相应操作
  • Clock Secure System,可监控外部时钟,若外部时钟发生紊乱,则自动切换到内部时钟
  • 设备温度,可以通过内部温度传感器监测温度异常
  • 供电电压监测,如果电压降低到某个水平,则系统不再响应

STM32 SBSFU 目前实现了防篡改的软件功能,用户可根据需要加入其它硬件功能。

4.5 STM32 安全启动应用

4.5.1 构造启动信任链

安全启动实现了信任根。为了整体安全的需求,需要将信任传递到下一级,这个信任传递是通过加解密技术完成的

在启动时,要至少保证下一级固件的完整可靠性。要使用认证技术,对用户固件进行验证,可以使用哈希函数,也可以使用基于对称密钥的验证码 AES-GCM。

系统在安全启动前,需要保存已经烧录到 Flash 里的用户固件的哈希值,这个哈希值,通过STM32 安全技术,保证它不会被内外攻击所改变。

系统在安全启动时,利用构造的安全执行环境,对需要进一步执行的用户固件,进行一个哈希运算。哈希运算一般采用 SHA256。

系统比较这两个值,如果计算出来的哈希值,与存储的哈希值相同,那么认为没有人对用户固件进行了改动。安全启动这时可以执行一个敏感信息的清理工作。如果有可能,可以将安全启动部分设置为不可访问,然后就放心大胆地跳转到用户固件去执行;如果哈希值验证不通过,则系统直接重启。

对于 AES-GCM 来计算固件的验证码,也与算 SHA256 相同,都是对固件重新计算出一个值,然后与保存的值进行比较。

4.5.2 可重用的安全引擎

STM32 SBSFU 将一些安全高度敏感的操作,包装在安全引擎里。这些安全高度敏感操作,包括从安全引擎的初始化,从 Flash 中读取密钥、读写固件、进行加解密运算,从 Flash 中读些安全相关的参数。

安全引擎是安全启动的核心可信代码,需要多项 STM32 平台的安全保护技术来综合保护,例如 Firewall、MPU。安全引擎对外提供单一入口。在调用返回前,清理敏感信息。安全引擎也可以被用户固件调用。对于用户固件来说,安全引擎是安全服务提供方。

4.5.3 启动安全在智能锁中的应用

对于某个特定的应用,只要资源不是受限制,那么安全启动的功能应该是相似的。对于智能锁,可以直接应用 STM32 SBSFU。在编译 STM32 SBSFU 工程文件前,需要准备 Python 环境,对密钥进行转化以及为固件更新所需要的对固件进行封装。

用户拿到 STM32 SBSFU 开发包时,一般会包含 3 个工程:

  • 中间件 SE 安全引擎 Project,该工程是安全启动的核心可信任代码,包括受信任的代码和密钥,密钥可以是对称密钥或者椭圆曲线 ECC 公钥。防火墙的调用门机制就是在该工程中实现。该工程输出的固件,会集成到安全启动与安全固件更新工程中去。
  • 安全启动与安全固件更新 Project,该工程配置安全防护引擎,集成安全引擎。该工程同时对外输出安全引擎的符号文件,供用户固件使用。
  • 用户固件工程 Project,这是一个固件示例。

用户仅需依次对这 3 个工程编译就可以了。在安全启动与安全固件手册更新有详细的一步一步操作说明。

STM32 安全启动对外提供了受保护的安全引擎。用户程序的加解密操作,若需要比较高安全的服务,可以考虑使用安全引擎所提供的加解密服务。即使安全引擎中的服务不能满足需求,也可以考虑扩展安全引擎的功能,这个扩展不影响安全引擎的安全防护能力。

在保持安全启动不变的情况下,STM32 有更灵活的实现方式,让安全启动可以应用在所有的 STM32 系列上。对于一般用户,可直接集成 STM32 SBSFU 到实际产品中;对于资源受限的 MCU 应用场景,可以借鉴 STM32 安全启动的实现方式,而不需要照搬所有的 SBSFU 代码;用户通过组合简单的 STM32 安全技术,实现轻量级的平台安全方案。

实际开发时,请仔细阅读 STM32 的安全应用笔记、安全启动与安全固件更新手册。特别推荐 STM32 安全介绍 AN5156,安全启动与安全固件更新用户手册 UM2262。对于 STM32 安全启动,以及 STM32 安全技术,在这些文档中都有详细描述。

五. 安全固件更新

固件更新是软件生命周期中必须面临的问题,固件更新会带来安全问题,例如使用固件更新来攻击电子钱包。

本章课程就是为了解决固件更新的风险问题,探讨如何使用 STM32 安全技术,软件及硬件安全技术,进行安全的固件升级。

安全固件更新离不开安全启动。STM32 安全启动为 STM32 安全固件更新提供了安全的运行环境

本章主要内容:

  • 什么是安全固件更新
  • 安全固件更新的重要性
  • 安全固件更新如何实现
  • 安全固件更新的应用

5.1 固件更新的安全挑战

5.1.1 什么是安全固件更新?

固件更新:部分或者完全替换设备上的软件操作,包括添加新应用、修改已有功能、修复软件里的问题。

安全固件更新:以安全的方式去实施固件更新。

安全,就是防止固件在更新实施过程中被修改、被假冒以及被窃取的威胁。安全的三要素是:保密、完整、可用,需要在安全固件更新的准备以及实施过程中得到保证。完整可靠属于安全属性里的完整属性。

5.1.2 为什么需要固件更新

“离开软件,芯片就是一堆沙子(Silicon Without Software is Just Sand)”。离开固件更新,芯片就在回归沙子的路上。因为如果没有固件更新,软件不再适合应用场景,或者停止工作,那么这个芯片也可以说重新变成了沙子。

固件更新很重要,可以从以下几个方面理解:

  • 固件更新可以解决系统的功能缺陷或者安全漏洞。软件几乎不可能一个 bug 也没有,所以固件更新的一个重要用途,就是给开发人员一个修改的机会,在这个产品生命周期内修复系统系统缺陷,这些缺陷也可以包括安全弱点。
  • 固件更新可以给系统提供新功能新价值
  • 固件更新可以帮助用户产品加快进入市场的速度,可以先实现一个最小的完备系统,支持基础性功能,同时带有固件更新的能力,那么不需要等待所有功能完成,就可以进行工厂制造。
  • 固件更新可以降低设备的维护成本,有了固件更新,可以要求所有的设备都运行在某一版本或者该版本之上,那么,支持的版本个数就减少了,从而支持多个版本的成本就可以显著降低。

5.1.3 新功能带来新弱点

固件更新作为一个系统的修改入口,是可以被恶意的攻击者利用。同时,对安全来说,新功能总会带来新弱点,固件更新的自身的功能设计也可能带来新的安全弱点。黑客可以用多种手段对固件更新进行攻击:

  • 攻击固件本身。黑客可以在更新的起点替换固件。 - 如果固件来源不可靠,轻则固件不能运行,重则运行起来破坏这个设备- 如果固件是不完整的、被破坏了的,更新固件后,能不能运行就是个问号- 如果固件一部分被注入了恶意软件,则固件更新后,系统就有了后门,也就相当于家里进了小偷或者强盗
  • 攻击固件的传输过程- 传输过程中,如果没有做到保密,固件可以被别人获得- 固件过程过程中,可能被人为更改
  • 攻击固件的烧写过程- 即使在固件传输过程中采取了固件加密措施,如果设备在烧写时不采用相应的安全措施,固件的内容还是通过固件烧写的过程泄密了
  • 攻击固件的存储位置(PS:不管有没有固件更新,用户固件的存储位置会对系统安全的作用都不可忽视) - 存储在 MCU 内部,可以很方便的使用 MCU 自带的硬件安全技术来保护固件,例如使用 STM32 的RDP level 2 来防止外部攻击, 保护固件的保密,以及固件的完整可靠性- 使用外扩 Flash。外扩 Flash 会带来极大的安全风险。攻击者可以将 Flash 吹掉,放在读写器上读出 Flash 里的内容。同时,外扩 Flash 的管脚,会暴露在攻击者的枪口之下,即使固件得到验证后,也不应该直接从外扩 Flash 直接执行。攻击者完全可以欺骗安全启动,在执行的一刹那,将外扩 Flash 换成不安全的内容(hot-swapping)
  • 攻击固件的版本,理论上新的版本比旧的版本要安全,而攻击者则反其道而行之,利用旧版本来替换新版本,从而降低系统的安全性 - 旧的版本。攻击者可以故意利用旧版本来替换新版本,旧版本带有明显的安全漏洞,则攻击者会利用该漏洞作为入口对系统进行攻击- 介于新旧之间的版本。对于离线设备,攻击者可以利用一个有弱点的中间版本来攻击设备。该中间版本,比设备上的版本新,但是又不是厂家的最新版。这种情况下,设备很难分辨是否应该要升级。

5.2 固件更新的总体设计

为了理解 STM32 安全固件更新,可以先看普通的固件更新如何设计,包括:一般流程、端到端之间的传输以及为了支持传输的数据结构,在 MCU 中的存储以及支持存储的数据结构。

在对固件更新的一般原理有了基本认知后,再看固件安全更新需要额外引入哪些变化,以及 STM32 如何在安全固件更新里实现这些额外的需求。一定要谨记,STM32 安全固件更新离不开 STM32 安全启动这个平台安全做基础。

5.2.1 固件更新的流程

5.2.1.1 一般情况(近距离2方)

一般情况下,固件更新有近距离 2 方参与:

  • 设备端:固件的接收方 STM32 MCU
  • 服务端:固件的提供方,比如PC

例如,开发人员可以用 ST-Link,直接将新的固件通过 JTAG 烧入到 STM32 MCU。这个更新的过程可以是:

  • 一次性擦除这个 Flash 并写入新固件
  • 擦除一部分 Sectors 或者 Pages,然后对这个部分区块进行更新

这种有两方参与的固件更新的通讯方式,并不仅限于 ST-Link JTAG 通讯,还可以通过 UART,此时需要将 STM32 MCU 切换至 System memory BootLoader 启动模式或者使用用户自己的启动加载器。

更复杂的情况,尽管也是两方参与,但是他们是通过其他通讯协议,比如以太网通讯。不过,这种复杂性,只是体现在底层协议栈上,对于固件本身来说不需要做任何改变。例如,用户可以开发 BootLoader 充当服务器,允许其他网络用户连上来,发送指令,发送固件,然后来更新系统。

5.2.1.2 IoT时代

在IoT时代,需要一对多的更新,参与者一般是 3 个:

  • 固件的提供方,一般是管理员
  • 固件的存储方与管理方,一般是 IoT 平台
  • 设备端,也就是固件的接受者 STM32 MCU

一对多的情况下,管理员将固件上传到 IoT 平台,触发所有设备的更新操作。

IoT 平台则根据各个 STM32 设备的具体情况下进行固件更新的消息通知,以及固件内容进行推送。IoT 平台也可以被动接受请求,这种情况下,固件更新的操作不一定是同步的,可能要花费几天,甚至几个月。考虑到有些设备的离线,若需要等待全部设备的版本升级完成,可能等待的时间更长。

固件更新的网络拓扑结构的改变,不改变传输的固件更新的内容组织。传输的内容,依然可以是:

  • 从头开始完整的传递一个固件
  • 只更新一部分

5.2.2 固件传输

固件更新的通讯上,需要考虑服务器与设备之间的距离的影响。是否需要远距离更新,例如选择什么样的通讯接口;是否不依赖通讯的可靠性,例如是否需要使用断点续传、校验和;以及是否假定通讯带宽总是很低,例如是否需要差分更新以及是否需要压缩。

  • 距离:本地更新与远程更新- 本地更新:近距离连接设备,一般通过 JTAG 或者 UART。- 远程更新(FOTA):遍布在全球各个角落的 IoT 设备。仅仅在安全启动里支持下载功能是不够的,还需要用户固件支持下载功能。
  • 可靠:断线续传与校验和- 断点续传:在不稳定的情况下,一般考虑保存固件下载的状态。当网络连接断开又恢复后,下载不需要从头开始,而只需要从网络断开处继续,这样可以减少重复的数据下载,从而减少对网络带宽的要求。- CRC 校验和:网络存在发生错误的可能性,导致下载的固件,大小相同,内容却不可用。在无法确定通信一定可靠的情况下,对这个固件加入 CRC 校验字段,而不仅仅判断固件包的大小。
  • 带宽:差分更新与压缩- 是否差分更新:差分更新建立在新版本和旧版本的差异上,只传输差异部分,由设备端恢复出完整需要更新的固件。一般情况下,临近两个版本不可能差别太大,这时采用差分更新,只传输较小的差异部分,节省网络带宽,也能降低设备的功耗- 是否压缩:对固件进行压缩,可以节省传输的带宽。但是压缩可能会带来安全弱点,这取决于压缩的内容是否包含一些敏感信息,同时是否有接口允许黑客选择要发送的内容。
  • 传输中的数据结构- 无额外数据结构:最简单的固件更新是不添加任何额外的数据结构,在通讯链路上传输的就是固件本身。当STM32 完整的收到该固件后,直接将固定位置的程序代码擦除,然后将新的固件写到该位置- 带有元数据:一般推荐传输的固件加入元数据信息,可以包括:- 固件名称- 固件大小- 固件版本- 固件校验和- 本地传输起始地址- 本次传输结束地址- 元数据的校验和为了断点续传或者差分更新,需要标记所传输的数据在固件整体中的位置;校验和需要包括元数据及实际固件,这样可以检测固件头部和实际固件是否在传输过程中没有损坏。在实际开发固件更新时,元数据可以细分为固件整体的元数据以及每次传输的元数据。

5.2.3 固件存储

  • 固件存储- 位置:可以存储在 MCU 内部,或者外扩 Flash中。外扩 Flash 会带来安全风险。- BootLoader - 无 BootLoader:无 BootLoader 则需要利用 STM32 的双 bank 特性,Flash 分为 Bank A 和 Bank B,当系统从 Bank A启动时可以更新 Bank B,确保更新成功后,可以将系统设置成从 Bank B启动。STM32 的双 Bank 启动依赖于 System memory BootLoader(此时 RDP 不能设置为 2)。- 有 BootLoader:由 BootLoader 去接收新的固件,然后由 BootLoader 将该固件写入固定的位置。一般推荐更新固件使用 BootLoader,简单。
  • 固件冗余- 单镜像(无冗余)- 优点:①更多用户空间;②适合更小的 Flash 要求。- 缺点:代码从 Flash 里运行时无法更新自己,不支持用户更新- 双镜像(冗余)- 优点:①可以回退,更安全;②用户固件也可以更新- 缺点:需要更多的空间BootLoader 和 固件冗余并不冲突,可以同时拥有。STM32 SBSFU 提供了BootLoader + 是否固件冗余的选择,包括BootLoader + 单镜像 和 BootLoader + 双镜像,供用户选择。
  • 整体存储结构:固件的存储位置并不影响固件更新的存储的逻辑数据结构,然而是否冗余则意味着在设计时是否需要预留足够的空间。如果是 BootLoader 加固件冗余,则 Flash 会被划分成几个部分:- 启动加载器- 启动数据区- 固件区- 备份固件区

5.3 固件更新的额外设计

5.3.1 为了安全的额外设计

要让一般的固件更新变成安全的固件更新,体现在:

  • 固件是保密的 - 传输过程中固件保密,通过加解密技术保证- 设备更新过程中的保密,通过 STM32 安全启动所提供的安全机制保证
  • 固件是完整的 - 传输过程中的完整性,是通过加密函数中的哈希函数 SHA256 或者 AES-GCM 来保证。在有攻击的情况下,单独谈完整性是没有意义的。哈希函数一般都是结合加密技术来保证完整性的。
  • 固件是可靠的 - 通过加密技术中的认证函数,或者数字签名技术 ECDSA 或者认证码 AES-GCM来保证。

总结,如果需要让固件在更新过程中是保密、完整、可靠的目标,要在流程上增加额外的环节,例如增加加密与解密的阶段。增加的环节会影响到传输过程中的设计选择,同时数据结构设计时需要增加额外的字段。

5.3.2 固件更新的额外流程

  • 固件的加密与解密 - 固件在发布之前采用 AES 进行加密,使用的加密密钥应该与预埋在安全启动部分的密钥一样,对称密钥要保密。要让安全启动保证对称密钥的保密、完整、可靠的三个属性的保护。- 收到固件后,需要对固件进行解密。STM32 SBSFU 的双镜像解密操作是发生在启动时,而不是固件下载时。这种设计是为了双镜像的数据交换。Flash 运行的代码不能是正在改写的代码,而启动时两个镜像都没有在使用。
  • 固件的认证与验证:对固件进行签名或者认证。 - 如果采用非对称密钥技术,需要将公钥预埋在安全启动部分。由安全启动部分对该公钥进行完整可靠性的保证- 接收到需要更新的固件,安全启动要校验签名或者使用对称密钥的认证码进行比对
  • 固件版本的检查 - 在安全启动的安全数据区保存软件的正确版本,确保版本总是向前正常,而不是向后减小。

5.3.3 固件的传输选择

使用 STM32 来构造安全固件更新,希望在数据链路上传输的固件是加密的。

前面提到有 2 种固件更新的流程,一种是近距离通过 JTAG 或者 UART更新,一种是通过云端来更新。对于保密性,强调的是端到端的安全。除非很非常特别信任第三方平台,否则,在固件的加密应该是整体加密。也就是说,即使有了中间节点 IoT 平台,固件更新可以从起点就开始加密,同时不需要在中间节点解密。

考虑到安全,CRC32 是可以被加密级别的哈希函数以及认证码取代。

当然,这种情况下,差分更新会受到影响。如果使用差分更新,需要仔细评估整个更新链条上的安全性。

对于压缩,因为从服务器端传送至设备端,可以认为风险低。

5.3.4 固件的存储

考虑到整体安全的情况下,假设服务器被攻破了,那么所有的固件更新操作都可能错误,推荐用户加入冗余。例如服务器会让 MCU 擦除掉 MCU 的固件,这个时候 MCU 作为一个被动执行者,不可能不执行。如果 MCU 只保存一份正确的固件,那么下次重新启动的时候,可能 MCU 就无法运行了。如果设计时已经考虑到冗余,在 MCU 里总是保存一份正确的固件代码,使用 IWDG 来判断启动时是否有正确的固件执行,如果没有,则总是从保存的正确的默认固件里启动。

5.3.5 安全固件更新的数据结构

在传输的数据结构中,增了了随机数字段,这是加解密算法所需要的字段;增加了哈希字段,是为了检查固件的完整性;增加了签名字段,是为了检查固件的可靠性。

在启动数据区中,增加了固件的版本,用户的密钥信息。

5.4 STM32 SBSFU 的实现与应用

5.4.1 STM32 安全固件更新

STM32 固件安全更新离不开 STM32 安全启动。

STM32 安全固件更新的流程:

  • 下载固件头
  • 验证固件头
  • 下载(加密)固件
  • 重启
  • 检查/设置安全环境
  • 检查是否需要固件更新
  • 验证固件头
  • 加密/验证固件,并烧入固件
  • 认证固件
  • 执行新固件

从 STM32 SBSFU 的流程可以看出,固件的完整性,以及固件 header 的完整性都很重要,都必须进行检查。 STM32 SBSFU 中固件 header 中包含固件的哈希值或者认证码。为什么可以包括一个哈希值,而不是一个签名值?因为 header 是被签名或者受 AES-GCM 认证码 TAG 保护的。

STM32 SBSFU 典型的 header 如下:

  • SFU Magic 数字
  • SFU 协议版本
  • 用于加解密的随机数,被 AES-GCM 作为 header 或者 AES-CBC 作为 IV
  • 固件版本
  • 固件大小
  • 固件认证码/哈希值
  • 保留字段
  • 头部的认证码/签名

当然,这里的流程,选择的是固件冗余的方案,也就是双镜像 dual-image。如果是单镜像 single-image,流程会有所不同。因为单镜像不支持用户固件的下载,所有只能从 BootLoader 里下载。在这种情况下,就不需要重启再去进行解密验证操作。

同时,这里的流程选择的是固件带加密的流程。如果关于固件不需要加密,只要保证完整性,那么,解密是不需要的。而完整可靠性认证在 STM32 SBSFU 中都是存在的。

STM32 SBSFU 中安全固件更新使用了加解密技术,支持三种方案:

  • 对称密钥 AES128-CBC,非对称密钥认证 ECDSA,以及完整性函数 SHA256
  • 无加密,非对称密钥认证 ECDSA,以及完整性函数 SHA256
  • 对称密钥加密 AES128-GCM,对称密钥认证 AES128-GCM

STM32 SBSFU 的固件包的制作是由编译器调用工程中的脚本直接完成。

STM32 SBSFU 支持 BootLoader,支持使用 UART 串口在用户程序或者在 BootLoader 里更新用户固件。

STM32 SBSFU 支持双 image 或者 单 image,单 image 就不支持固件冗余。

5.4.2 STM32 SBSFU 在智能锁中的应用

对于某个特定的应用,只要资源不受限制,那么安全固件更新的功能总是应该相似的。对于智能锁,可以直接应用 STM32 SBSFU。应用 STM32 SBSFU 需要考虑 Flash 大小,加密模式的配置,以及用户固件的替换。

如果使用 STM32 SBSFU 安全启动与安全固件更新,系统大约会增加 50 KB 的 Flash。

STM32 SBSFU 的密钥配置在文本文件里,非对称密钥椭圆曲线 ECC 的是 ECCKEY.txt,对称密钥则是在OEM_KEY_COMPANY1_KEY_AES_CBC.bin,在开发包中搜索 ECC 和 AES 就很容易找到密钥配置文件。实际必须修改这些密钥。

系统默认使用非对称密钥认证固件,也就是:对称密钥加密 AES128-CBC,非对称密钥认证 ECDSA,以及完整性函数 SHA256。如果需要改动,可以修改配置文件se_crypto_config.h,在开发包中搜索 config 很容易找到该文件。

当然,STM32 SBSFU 中的用户固件,只是一个示例,实际是需要进行替换的。同时,该例子固件只支持从 UART 传输固件。实际中的固件则是需要支持从网络接收固件,这一点在改动时,可以修改固件下载部分。STM32 SBSFU 的固件下载部分和固件解密部分是分开的。固件下载可以在用户固件中完成,需要的安全性低。而固件解密则是在安全性高的启动部分完成的。

对于资源敏感的 STM32 MCU,可以根据原理,定制安全启动与安全固件更新,得到轻量级的实现。STM32 SBSFU 应用笔记 AN5056 是一个很好的定制参考。

六. 安全固件安装 SFI (Secure Firmware Installation)

6.1 为什么需要安全固件安装

安全固件安装的本质是:我们能否相信工厂的制造过程是安全的过程。如果我们相信工厂是可靠的,相信他总是按照我们的指示,例如,我们希望他不要泄露固件代码,我们希望他不要过生产,他们都能一一办到,那么安全固件安装时没有永无之地。

然而,总有一些工厂,或者工厂里的员工,为了一时利益,可能会将研发设计公司辛苦开发的源代码(通常是厂商的二进制固件代码),直接泄露给其他第三方。同时,他们也有可能将委托生产的产品过生产,例如我们希望他生产1000台,结果他们生产了1500台,1000台按照合同提供回给厂商,使用正规的商标与品牌,另外500台则不知道通过什么样的商标流入市场。如果该产品在市场上很火爆,他们获得的利益会远远超过代工的收益。

除了正常的法律手段外,STM32 技术手段可以防止工厂泄露受委托生产的固件二进制代码,同时防止工厂进行过生产,获得非法利益。

6.2 安全固件安装的两个关键问题及解决

  • 固件代码的保密性- 解决方法:通讯安全,从公司研发部门到设备中断构建一个端对端的安全通道来传输固件。研发部门发布二进制固件时进行加密,设备(也就是 STM32)进行解密。
  • 过生产、产计数- 解决方法:授权,ST的可信代码能保证解密的密钥只使用一次,从而保证使用次数和授权次数一致。

6.3 安全固件安装的示例解决方案

这个解决方案里,有三个角色:

  • 研发部门,固件的制造者 - 安全固件安装流程需要设定一个密钥,然后使用 Trust Package Creator(UM2238)的 STM32 工具对需要安装的固件,进行加密,加密算法可以是 AES-CBC,或者其他所支持的算法- 产计数的核心是密钥授权的概念,要将密钥写入智能卡 HSM 硬件安全模块(是一张智能卡,或者说是安全芯片,叫 ST Safe)里,它保证了没有人能够获得固件的解密密钥。同时,为了计数的需要,要对智能卡设置一个次数,这个次数定义了对外生成授权文件的多少,例如1000次,这个数字一旦写入智能卡,就不能再次修改,只能在使用过程中进行逐次递减。
  • 工厂的产线 - 固件已经被加密了,工厂就没有办法知道原始的二进制文件是什么- 工厂还需要研发部门所准备的智能卡,HSM 包含了固件解密所需要的密钥,HSM 也提供给了工厂,但是因为安全芯片的本质,工厂没有办法从芯片中抽取密钥。- 在生成过程中,HSM 会生成授权文件。当然,可以生成多少次授权文件,已经被研发部门所固定。
  • STM32 设备 - 接收工厂通过通讯接口传递过来的加密固件,同时通过安全通道,请求 HSM 生成授权文件给 STM32 芯片。那么,STM32 芯片就安全的拥有了固件解密的密钥,也就具备了对固件解密的能力。解密的过程是发生在 STM32 芯片内部。- 同时,该过程被 STM32 安全技术所保护,因而,在整个产品的生成过程中,不存在固件的泄漏,也不存在过生产。当然,如果对同一个设备,多次申请加密固件上授权文件,那么等同于对这个设备进行了多次解密安装,从而可授权次数会相应减少。比如1000次授权,把其中200次都用在同一个设备上,最终的设备最多只有800+1台。

安全固件安装是 STM32 提供给客户的解决工厂不可信的安全技术,属于全生命周期安全的一部分。

七. 标准与认证

本章主要对标准与认证做概要性介绍,帮助开发人员了解:

  • 开产品开发中,需要遵循的各种标准
  • 在产品初步开发完成后,需要推向市场时,应该通过什么样的信息安全认证

7.1 标准概论

标准其实就是一些规范文件。

标准可以来做什么?标准的最大特点是可以反复使用,而且达到通过标准,能获得一致性结果。

遵循标准对安全有什么价值?不遵循安全标准,基本上等于不安全。举个栗子,在 STM32 加密库中,看到 RSA PKCS 1.5 的函数名称,而不是直接的 RSA 运算。这是因为 RSA 算法的基本原理只是给我们展现了教科书级别的算法。在实际应用中,如果直接使用,非常容易受到攻击。例如,假设我们所有人的公钥指数都选为 3,虽然模数 n 不同,私钥指数是不同的。但是如果没有按照 RSA PKCS1.5 进行填充,那么根据中国余数定理,如果同一内容多方加密后,攻击,可以在不知道私钥指数的情况下,将明文恢复。这是所谓的广播攻击。而如果遵循了 RSA PKCS 1.5 的随机填充要求,这个攻击就无法奏效。这是我们需要 PKCS1.5 的原因。当然这些年随着攻击防护研究的深入,PKCS1.5 标准也正在更新换代中。

所以安全技术开发中有一个非常重要的原则:要遵循标准。很多公司都规定要使用公开的算法,要用公开的标准。在做安全开发时,一定要记住:不可随随便便搞发明创新,否则极有可能给系统留下安全漏洞。当然,在遵循算法标准的情况下,可以在实现算法的过程中加入更多的防护。

7.2 与安全有关的标准

当谈论标准时,通常涉及两类标准。

  • 大类标准,例如 FIPS(美国联邦信息处理标准),再如国密 SM(商用密码)
  • 一些具体的算法或者协议标准,该类内容很多,这里只谈STM32 用户开发中常用的。- 加密:DES、AES、RSA。DES已经认为不安全,应该被AES取代- 单向散列函数:MD5,SHA1,SHA2,SHA3。MD5已经被攻破,SHA1不推荐使用。- 消息认证码:含有 HAMC 以及以及 AES 算法的消息认证码- 数字签名: RSA PKCS1.5、椭圆曲线 ECDSA- 公钥体系基础架构:X.509,常用的数字证书就是 X.509 标准定义的除了这些国际算法标准,中国也有相应的国家密码标准,称之为国密。如 SM1 为对称密钥,其加密强度与 AES 相当;SM2 为非对称加密,基于椭圆曲线 ECC;SM3 摘要消息;SM4 是一个对称密钥算法。通讯安全标准也是有标准,如 TLS。处理信息安全的时候会发现,基本上天天需要和这些已经标准化的算法与协议打交道。

7.3 认证概论

通用控制器目前信息安全强制认证并不常见。但是如果所销售的产品所在行业已经有国家强制性标准,比如要销售给美国政府的事业单位,则认证是必不可少的步骤。还有一些客户是基于风险所考虑的。不同的国家有不同的法律要求,如果因为安全的原因,例如用户个人信息泄露,而导致制造商分销商被起诉,则有可能面临巨额赔偿。而认证可以降低该风险,甚至免除责任。最后一种则是客户为了提高客户满意度,提高产品区分度,所通过的一些认证。

如果打算去做认证,那么在做产品计划的时候,就需要认真考量时间和费用这两点。很多产品认证,一方面需要的时间很长,半年甚至一年;另一方面,认证的费用也很高,百万级别的费用并不少见。

有时候产品认证的范围,不仅仅是送测的产品。整个公司的研发,生产环境以及公司的产品制造流程都可能在认证范围之内。

7.4 认证流程

认证实验室和颁发证书的认证机构主体通常不是同一个。

为了通过认证,一般需要提交文档、源代码、产品样片。认证测试很多时候价格不菲,而且按次收费。为了通过认证,就要选用正确的芯片与软件开发包。STM32 与 STM32 加密库在这个时候就可以发挥作用。

认证实验室或者受委托机构,会对你提交的文档、代码进行审查,同时进行产品安全测试。如果认证标准需要评估管理体系生成流程,则认证机构会组织对提交材料的单位进行现场审查。

文档审查、产品测试以及现场审查都符合标准,则由认证实验室或者受委托机构提交一份评估报告。

认证主管机构认可评估报告,会颁发相应证书。证书一般都是有期限的。

收到证书后,认证进入维护阶段。若产品发生变化,则可能需要提交产品更改申请。若产品到期,则要考虑是否重新延展认证书年限。同时,认证管理机构会定期对获得证书的单位进行监督审查。

当然,有些认证可能只需要提交问卷或者提交自测试的结果就可以了。那样的话会比较简单,但前提是认证机构相信这个自测试结果。

7.5 NIST FIPS 140

NIST FIPS 140 是美国国家强制的标准,该标准是美国 NIST 联合加拿大制定的,在英国等其他国家也得到承认。当前版本是 FIPS-140 2。NIST 创建了 CMVP 来认证加密模块是否达到了 FIPS140 的要求。不符合该标准的产品无法进入美国政府的采购列表。产品一旦符合它的规定,并得到认证,就已达到了美国和加拿大公共机构的安全标准,可以向美国和加拿大出口。这个认证分为两部分,CAVP 密码算法验证体系,以及 CMVP 密码模块验证体系。CAVP 密码算法验证体系是 CMVP 的先决条件。

FIPS-140 2 具有 4 个等级:

  • 等级1. 通常用于软件加密产品
  • 等级2. 要求基于角色的认证,具备记录物理篡改的能力(篡改取证)
  • 等级3. 增加物理篡改预防的措施
  • 等级4. 包含高级防篡改的功能。针对产品运行在不受保护的物理环境中。任何方向的攻击都可以大概率被检测到,然后导致明文数据被擦除。

FIPS-140 认证过程时间长,成本高。如果产品通过了 CMVP 认证,获得 FIPS-140 认证证书,可在产品中加入 FIPS-140 2 inside 等标志。

7.6 STM32 的 CAVP 认证

STM32 加密库为了减轻客户 FIPS-140 产品认证工作,提供通过 CAVP 认证的加密库 XCUBE-CRYPTOLIB。

STM32 的 CAVP 认证包括以下内容:

  • 对于 Hash 和 HMAC算法: - SHA-1、SHA-224、SHA-256、SHA-384、SHA-512- HMAC-SHA-1、HMAC-SHA2-224、HMAC-SHA2-256、HMAC-SHA2-384、HMAC-SHA2-512
  • 对于对称密钥 AES 算法,STM32 AES-CBC、AES-CCM、AES-CFB128、AES-CMAC、AES-CTR、AES-ECB、AES-GCM、AES-KW、AES-OFB、AES-XTS
  • 对于随机数,STM32 DRBG
  • 对于RSA公钥算法,STM32 RSA PKCS 1.5 通过了认证,可以用来签名和验证签名。
  • 对于椭圆曲线 ECC 公钥算法,STM32 ECDSA 密钥生成、密钥验证、签名生成、签名认证
标签: 单片机

本文转载自: https://blog.csdn.net/m0_61687959/article/details/125394332
版权归原作者 m0_61687959 所有, 如有侵权,请联系我们删除。

“从智能锁谈STM32安全技术”的评论:

还没有评论