1.1.1 什么是安全
安全分为2类:
功能安全 Functional Safety
信息安全 Information Security
本教程讲的安全都指信息安全!!!
1.1.2 信息安全三要素
信息安全有3个基本属性(基本服务):
保密性(C):该你知道,你可以知道;不该你知道的,就不让你知道
完整性(I):真实可靠,信息没有被修改,没有被假冒
可用性(A):总是可以访问这个信息,该用时也可以用
1.2.1 什么是智能锁
智能锁不用于传统的机械锁具,带有芯片进行控制
1.2.2 资产、弱点、威胁模型
分析设备所需要采用的安全技术,有一个非常成熟的模型,叫资产、弱点、威胁模型:
如果没有需要保护的资产,则漏洞加威胁,构不成风险,就不需要制定安全措施。所以,当谈及安全时,首先要甄别的是需要保护的资产是什么。
资产弱点威胁模型表明,所有的安全方案应该只针对有风险的系统的弱点来客服系统的漏洞。
1.3.1 识别智能锁中的资产
智能锁中有哪些是需要保护的资产?从锁的用户角度看,第一重要的价值是锁的访问权。对于智能锁,保护的对象是开锁、关锁的本质:电机的转动。MCU 在什么条件下发出电机转动的信号,这个条件和判断的过程都是安全要保护的内容。密码开锁,则要检测这个密码是不是合法,密码合法的情况下,则发出这个开锁信号,所以这个密码就是具体的资产。
1.3.2 识别智能锁的脆弱性
辨别系统的脆弱性,一个方法,就是系统分解,从功能上分解,从生命周期上分解。例如将系统分解成不同的模块,就可以看到其中存在的弱点:
恶意的人如何将威胁具体化形成实际的攻击方式呢?如何破解带芯片的系统,一般有2大类:
物理攻击,按对系统的破坏程度分为:
非侵入式攻击
半侵入式攻击
开盖攻击
软件攻击,又叫逻辑攻击
智能锁面临的威胁 | 分类 | STM32 安全技术 |
---|---|---|
设备被破解 | 设备安全 | STM32 SBSFU 安全启动与安全固件更新 |
设备被假冒 | 设备安全 | STM32 SFI 安全固件安装 STM32 SBSFU 安全启动与安全固件更新 |
通讯被窃听 | 通讯安全 | STM32 TLS 通讯安全 |
通讯被篡改 | 通讯安全 | STM32 TLS 通讯安全 |
云端被破解 | 云端安全 | 云端处理 |
云端被假冒 | 云端安全 | 通讯安全中对云端进行认证 |
无论通讯安全还是设备安全,都离不开加解密技术。密码的背后一定是加解密技术。
加解密技术说白了就是变换,加密变换和它的反变换--解密变换。通过这种变换,可以提供之前所说的信息安全的三个属性(服务):保密性、完整性及可用性。在讨论加解密算法应用时所提供的服务时不大谈论可用性,因为算法总是要可用的。
同时,又把广义的的完整性细分为:狭义的完整性和认证性,就是完整性和真实可靠分离了。这样,加解密技术也有三个属性(服务),也叫 CIA,保密性、完整性和可认证性(Authentication)。
能够提供保密服务的有对称密钥和非对称密钥,能够提供狭义完整性服务的有单项散列函数,能够提供认证鉴别类服务的有基于非对称密钥技术的数字签名,以及基于对称密钥的消息验证码MAC。
第一阶段:加解密是艺术,1949年以前
第二阶段:对称密钥,1949~1976年
第三阶段:非对称密钥,1976~至今
对加解密技术定义模型,至少2方参加,加密方(Alice)和解密方(Bob)。A 和 B 之间的距离可以是空间上的(如北京到上海,要考虑到不同路由器之间的不安全通信连接),也可以是时间上的(如今天的你和明天的你进行一段加密与解密之间的对话)。
A对一段明文,用一个密钥进行加密,形成密文。B收到密文,用一个密钥进行解密,得到明文。
针对密钥的个数,有3种情况:
在加解密技术里,散列=摘要=哈希
加解密技术中的摘要,具有2个特征:
单项散列函数的用处:
提供完整性服务
已知一段数据的摘要,如果数据被修改了,使用前计算下摘要,就知道数据是否被修改或替换了
如果修改了数据,同时重新计算了摘要,此时检查不出来。
此时的解决方法:
存储敏感信息
构成密码级的散列函数主要是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个参数:散列函数的类型、明文。
对称不对称,指的不是加解密过程,而是加密和解密所使用的密钥。加密和解密所使用的钥匙一样,称为对称密钥。
对称密钥技术的发展非常迅速,应用简单,使用广泛,各种各样的文件、文本、声音、图像的保密都是通过对称密钥技术来保护的。如果需要保密地传输一个固件,给固件加密的一定是对称密钥技术。如果上网需要 https,最终加密传输网页的也一定是对称密钥技术。
对称密钥怎么使用?对称密钥算法,如果接口封装的好,一定至少有2个参数:密钥、明文。这个密钥参数也很简单,如果是 128 bit,那么密钥长度就是 16 个字节。
通常密钥都很小,然而矛盾的是,大部分时候需要加密的内容都很长。几个 Byte 的密钥长度与几百 MB 大小的文件,如何使用长度很短的密钥对长度很长的文件进行加密呢?
有2种方法:
对于第一种方法,流加密,存在一个问题:如何生成密钥流和明文长度大小一样。理想情况下,有一份和明文一样长的随机数做密钥,这样当然安全,可是,密钥这么长,怎么记录,怎么传输,都是问题。现实中还是从一个密钥,通过一种算法,进行计算,生成密钥流。
对于第二种算法,分组加密,看上去很好,符合工程技术的常见思维,对问题分解然后针对每个小块处理。但块与块之间是否就是独立的关系呢?块与块之间独立,对带来安全问题。
对于分组加密,如果每个位置上的数据独立,加密出来的数据总是一样,会有问题。同样的道理,如果每次对一个消息报文加密出来的结果都是一样,也会存在安全问题。
所以加解密技术也要保证:对于一个文件同样的明文出现在每一处的解密不一样,一个文件今天和明天的加密结果也不一样。如何做到?通过一个初始向量,加上分组连接模式,而这个初始向量是可以公开的,而且每次不一样。
从模型上,如果使用一个对称密钥技术,至少需要2个参数:明文、密码。如果安全点,至少需要4个参数:明文、密钥、初始化向量、分组模式。
对称加解密算法有哪些?
对称加密算法常用的5组分组模式:ECB(电子密码本)、CBC(密文分组链接)、CFB(密文反馈)、OFB(输出反馈)、CTR(计数器)。
对称密钥具有简单、速度快的优点,但缺点也很明显。在加密模型中,加密方A(Alice),解密方B(Bob)。对称密钥技术下,A 和 B 必须共享同一个密钥,这样 A、B 之间才能进行保密通信。这就引出了一个问题,如果 A 和 B 之间隔了千山万水,怎么样 A 和 B 之间共享一个密钥呢?在这个问题的解决,非技术手段,就是把 2 个需要通信的召集在一起,面对面协商出一个共享密钥。但这个手段,在过去,信息技术不发达、互联网技术不发达的情况下,还可以勉强接受。在今天,我们和每一个网络服务之间都需要随时随地的进行保密通信,面对面协商根本就不可能,这就必须依赖技术手段。这个技术手段就是非对称加密技术,依靠非对称加密来传输对称加密的密钥。
非对称加密技术指的是加密、解密的密钥不相同。
一对密钥:
因为每个人都可以使用公开的密钥对要发送给我的信息进行加密,对称密钥技术又叫公钥技术。
反过来(用私钥加密,发送给所有人),计算上可以,但是保密性已经不存在了,因为我已经把公钥公开给所有人,所有人都可以看到这个秘密。
但是这个方法也是有用的,如果你用私钥加密一段数据,其他所有人都有你的公钥,能够解开验证其中的内容,就证明是你发送的,这样你就抵赖不了,这就是签名技术。不过在签名体制里,就不能叫加密解密,叫它用私钥签名和用公钥验签。
公钥是用来加密的,私钥是用来解密的;私钥用来签名,公钥用来验签。
非对称机密技术,有 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密钥交换协议
但是所有的非对称密钥技术都不能防备一个问题:加入有人假冒Bob,怎么办?这就是中间人攻击的问题,只有采用数字证书才能解决。因为在任何情况下,我证明我是我,都是很难另不认识的人相信的。数字证书是靠一个可信第三方解决了这个问题。
信息安全的服务除了保密性,还有完整性。信息安全的完整性可以扩展为狭义的完整性服务和认证服务。
如何实现认证服务?就是将单向散列函数与加密技术结合来实现认证服务。主要认证服务有 2 种:
2.6.1 数字签名
数字签名的一般流程:现将要签名的文档,进行一个哈希运算(比如使用SHA-256,计算出32个字节的摘要),然后将摘要根据一定的标准,组成一个数据块,根据这个数据块,使用非对称加密技术的私钥对他进行签名运算。注意,签名的对象是哈希后的值,这样就避免了非对称密钥的速度慢的缺点。
签名总是随着明文发送给其他用户,因为公钥是公开的,其他用户如果需要验证这份文档是否完整可靠,只要用公钥解开签名中的哈希值,同时再计算一下明文的哈希值,如果这两个哈希值匹配,那么可以认为这个文档是完整并且可靠的。
注意:数字签名是有标准的,不能自己创造签名的算法和格式,否则很容易造成不安全。
2.6.2 消息认证码(MAC)
消息认证码有很多种实现与标准:
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 的理念来解决几个典型问题:
本章重点:
网络联网可以小规模组网,也可以直接连上 Internet。
无论设备是与内部的服务器通讯,还是与存在于 Internet 中的云进行进行消息交互,一旦组成网络,就面临通讯链路上的威胁(信息被窃听、被篡改、被假冒)。
什么样的系统弱点导致如此肯定系统会受到威胁?
连接方式不安全
连接方式主要分为2大类:有线连接和无线连接。
中间节点不安全
两个节点之间没有其他中间环节的通信,称为点对点通信 Point to Point;
通信中间可能经过各种各样的网关,各种各样的路由节点,称为端对端通信 End to End。
在端对端通信中,一般需要经历很多个环节,例如我的手机通过 App 向我家中的空调发送一条指令,我的手机要通过移动运营商的基站,也要经过云端服务器,同时要经过一系列骨干网路由器,再连接我所选择的网络服务商,最后通过我家的WIFI 热点发送给我家的空调。环节多,任何一个环节出了问题,那么这个风险就存在了。假设其中的任何一个节点因为病毒,或者操作失误,就会导致我们的信息被泄露、被篡改。处在这种网络威胁中,如果智能锁仅仅按照传统的嵌入式设备来进行编程,那么开锁命令可能会被假冒。恶意的人可以捕获开锁命令,再次向锁进行发送;或者锁的密钥下发可以被其他人完整的获取。
无法确定通讯对方是否可靠
互联网上两个节点之间的通讯,永远面临一个问题,怎么相信对方就是我应该谈话的那个人呢?所有一定要对其进行认证,最好是双向认证,这些需求叠加在一起的一个典型实现就是 TLS。
TLS(Transport Layer Security),意指传输层安全,是解决网络安全的重量级武器。
TCP/IP 的四层模型:物理层、网络层、传输层、应用层。TLS 层位于传输层和应用层之间。
3.2.1 TLS的成长史
TLS发展历史:
SSL1.0,1994年
是个不成熟的版本,网景公司并未对外公布
SSL2.0,1995年 网景公司对外发布,但是被认为是一个不安全的协议版本,具体表现在:
SSL3.0,1996年
TLS1.0,1999年
基于SSL3.0,可以看成 SSL3.1。该版本使用非常广泛。
TLS1.1,2006年
主要是BugFix
TLS1.2,2008年
TLS1.3,2018年
3.2.2 TLS的一般原理
3.2.2.1 两阶段协议
TLS 是一个两阶段协议,一个是握手阶段,一个是应用数据加密传输阶段。
3.2.2.2 TLS子协议
3.2.2.3 TLS握手
握手阶段有3个目标:
过程:
3.3.1 证书
如果进行安全通讯,最基本的前提就是在通讯双方建立起信任。所以,在TLS握手中需要进行身份认证。
对于网络设备来说,每一个设备,都需要一个身份证,在网络中,被称为证书。
证书有统一的国际标准来规范,即 X.509 证书,包括:
X.509 证书,为什么要包含这些信息?这些信息有几个层次:
当我们获得了一个服务器的证书,如何验证该服务器是否正确?是否被别人修改了?验证证书是否完整可靠,是通过验证证书的签名完成的。证书里包含了公钥,我们公钥将这个签名值解开,得到一个哈希值。我们同样要对除了算法 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.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简单表示错误。那么:
就构成了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 实现。
公司 | 方案名称 |
---|---|
arm | MbedTLS |
CypherBridge | Embedded TLS SDK |
HCC | VerifiableSSL/TLS |
Oryx Emb | CycloneSSL |
SEGGER | emSSL |
ST | STM32 Cube-TLS |
wolfSSL | Embedded SSL Library |
对于商用用途免费主要是 MbedTLS,同时 MbedTLS 是 CubeMX 所支持的中间件。
如何将 MbedTLS 在 STM32 上使用起来? ST 推出的每一个云连接的开发包,都包含 MbedTLS,里面的代码是可以运行的。如果觉得云连接开发包里的 MbedTLS 太复杂,可以从 CubeMX 出发,自己修改 MbedTLS 的配置文件。如何配置?主要是密码套件的选择,强烈推荐使用:
如果还是认为 mBedTLS 太大,怎么办?可以考虑其他协议。因为 mBedTLS 所提供的安全服务也是保密、完整、可靠。如果事先想服务器的证书或者等价的身份信息写进 STM32,将客户端的证书或者等价的身份信息写进服务器,则握手这个环节将大大简化。对于在 MCU 和 云端之间定义更加轻量级的 TLS,国内一些知名互联网公司已着手进行。
对于网络安全,取决于智能锁的网络拓扑如何构造。一般情况下,智能锁有3种节点:①智能锁本身;②智能锁服务器;③手机用户。
那么,这里存在多个通讯联系:
为了保证通讯安全,希望智能锁设备与服务器之间的连接,是通过 TLS 连接。而手机通过服务器连接智能锁,需要通过两个阶段:通过 TLS 进行加密,在服务器进行解密,然后再次通过 TLS 进行加密。前提是,我们能确定服务器是安全的,或者要对服务器进行相应的安全服务。这种用法可以说明两个问题:
在这种情况下,传递的信息依赖于中间二传手,也就是服务器的安全性。换言之,当通过手机给智能锁发送指令,这个安全类型,尽管使用了 TLS,并不能算是真正的端到端的安全。
如果用手机通过蓝牙直接连接智能锁,发送开锁指令,并不需要过于关于保密性。蓝牙通讯必然是一个很短的距离,这种场景下,恶意的人并不一定通过无线这种方式窃听;即使通过无线方式窃听,因为距离很近,也很容易被发现。但是,指令的完整可靠性必须得到保证,否则,指令可能被记录,然后再在某个时间被重放,这样智能锁就有可能被恶意打开了。指令的完整可靠性可以借鉴 TLS 使用随机数,加上时间,加上序列号进行验证码 MAC 的计算。
本章要点
4.1.1 什么是安全启动?
安全启动总来讲的就是确保设备总是在运行可信的软件。
安全启动 = 安全根 = 根安全 = 信任根 = 根信任
安全启动,前面加了安全二字,意味着这个启动部分具有全部或者部分的安全属性。
从而,安全启动至少有 2 层含义:
也可以进一步要求:
4.1.2 仅有加密是不够的
安全是一个整体,对于一个 IoT 设备,谈到安全就必须考虑:
安全启动是一个比较宏观,或者说是一个比较层次比较高的安全服务,实现它有多种方式。
如果在 STM32 使用了 RDP level 2,从某种程度上已经实现了一个安全启动。但还需要注意:
4.1.3 安全启动解决了什么问题
攻击者会使用软件攻击加上物理攻击的方法来攻击系统,目的当然是为了实实在在的利益,可能是想获取固件代码,以节省研发成本;可能是想获得身份秘钥,让其他非授权设备获得相应的服务;也可能是想获取更高权限,获得没有购买的服务。
为了保护设备不被破解,不被假冒,我们要采取措施防止物理攻击和软件攻击:
最简单的物理攻击是 JTAG 连接,因此在产品出厂之前一定要确保 JTAG 完全连接不上,或者 JTAG 满足一定的条件才可以连接。
安全启动通过加解密技术检测信任根的代码是否被破坏或者替换,并拒绝有异常的信任根代码执行。
最常见的软件攻击是恶意软件注入以及缓冲区溢出, 在安全启动里要加上相应的动态保护。例如,为了保护安全启动的身份密钥,可以将安全启动与后续的用户代码隔离,那么用户代码就没有机会向安全启动代码里注入恶意代码。
安全检测为系统进一步执行提供检测与判断,保证下一级代码在执行之前也是完整可靠的。这样,从安全启动开始,完整和可靠的特性一级一级的传递下去,也就是说,安全启动构造了一个软件执行的信任链。
4.2.1 安全启动在STM32中的实现
安全需要一个起点。安全启动要求启动的位置一定是固定在某个地方,启动位置靠什么保证?必须靠硬件。软件本身的特点,决定了它很容易被修改,即使做了加密和加扰,破解难度也依然比硬件低很多。所以,安全启动一定要靠硬件来保证,脱离硬件谈安全启动,基本上是不可能的。
实现 Root of Trust(信任根)通用的做法是什么?一般是,芯片有个 Bootrom 启动只读存储区。硬件的设计保证,芯片加电启动一定是从这个只读的甚至不可见的 Bootrom 里执行。
事实上,保持安全启动的原则不变,实现方式可以不一样。Bootrom 如果直接固化在芯片里,会减少灵活性,无法适应 MCU 的广泛应用场景。所以,STM32 采用的是使用 STM32 硬件技术,允许客户在开发过程中形成 Bootrom,Bootrom 具有安全启动的几个特征:
4.2.2 安全启动的STM32软件架构
STM32 SBSFU 架构分为3层:
本章重点是安全启动,重点讨论 STM32 如何构建一个安全环境,如何保证下一级固件的安全执行。
4.2.3 在STM32中固定启动位置
复盘下系统的初始执行过程:系统上电, STM32 MCU 根据 BOOT0 和 BOOT1 管脚的配置,再加上软件 BOOT 寄存器,可以确定系统应该在什么地方启动,存在三种可能:
理论上系统内存可以充当信任根,然而,考虑到系统内存所达到的安全级别和灵活性,我们不希望它从系统内存启动。从 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.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,原因:
注意: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.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
安全启动构造了一个信任链,一级一级来检查,一级一级的传递信任。尽管使用了加解密技术验证了后续环节,后面的环节应该是可靠的。但请记住,没有绝对的安全。我们不知道,是否有其他的攻击手段可以让恶意代码还是混进来了。
所以,对于安全链条的起点——信任根,有保密的需求。为了安全,我们不希望信任链后续的环节来访问这个最初的安全启动代码。即如果有办法将安全最敏感的信任根与后续的环节隔离开,那么系统会安全很多。
如何让安全启动的代码和后续代码彼此分离?
最新的多个 STM32 系列(STM32H7、STM32G0、STM32WB、STM32L5)提供了用户安全存储功能,在不同的STM32 系列可能有细微的差别,同时功能也有细微的差别。
Secure User Memory 功能通过选项字节设置,属于静态防护。Secure User Memory 一个最主要的功能在将启动与后续代码隔离开来,也就是放在 Secure User memory 的 Flash 里的代码,在执行完自身后,可以通过一个服务调用或者一个寄存器设置,让后续其他非可信任代码无法感知安全启动代码的存在,也就是后续代码代码再也无法访问之前的代码。这个功能可以用来提供安全启动代码的保密性,同时可以防止恶意代码去修改安全启动增强完整可靠性。
Secure User Memory 既能防内,又能防外,因为:
4.4.5 WRP
WRP 可以将某段区域或者空白区域设置不可写,那么代码通过协议栈错误擦除软件或者写入到空白 Flash 就不可能发生。WRP 既可以防内被程序误写,也可以防外被 JTAG 擦除。
所有的STM32 系列都有 WRP 功能。
WRP 是静态设置,直接通过选项字节进行设置。WRP 的选项,在 RDP 不为 2 的情况下,是可以被修改的。如果想一次就达到 Read-only 的效果,需要配合 RDP 为 2 使用。
4.4.6 系统监控
如果攻击已经发生,最重要的事情是能够监测到,对此,STM32 提供了多想安全监控技术:
STM32 SBSFU 目前实现了防篡改的软件功能,用户可根据需要加入其它硬件功能。
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 个工程:
用户仅需依次对这 3 个工程编译就可以了。在安全启动与安全固件手册更新有详细的一步一步操作说明。
STM32 安全启动对外提供了受保护的安全引擎。用户程序的加解密操作,若需要比较高安全的服务,可以考虑使用安全引擎所提供的加解密服务。即使安全引擎中的服务不能满足需求,也可以考虑扩展安全引擎的功能,这个扩展不影响安全引擎的安全防护能力。
在保持安全启动不变的情况下,STM32 有更灵活的实现方式,让安全启动可以应用在所有的 STM32 系列上。对于一般用户,可直接集成 STM32 SBSFU 到实际产品中;对于资源受限的 MCU 应用场景,可以借鉴 STM32 安全启动的实现方式,而不需要照搬所有的 SBSFU 代码;用户通过组合简单的 STM32 安全技术,实现轻量级的平台安全方案。
实际开发时,请仔细阅读 STM32 的安全应用笔记、安全启动与安全固件更新手册。特别推荐 STM32 安全介绍 AN5156,安全启动与安全固件更新用户手册 UM2262。对于 STM32 安全启动,以及 STM32 安全技术,在这些文档中都有详细描述。
固件更新是软件生命周期中必须面临的问题,固件更新会带来安全问题,例如使用固件更新来攻击电子钱包。
本章课程就是为了解决固件更新的风险问题,探讨如何使用 STM32 安全技术,软件及硬件安全技术,进行安全的固件升级。
安全固件更新离不开安全启动。STM32 安全启动为 STM32 安全固件更新提供了安全的运行环境。
本章主要内容:
5.1.1 什么是安全固件更新?
固件更新:部分或者完全替换设备上的软件操作,包括添加新应用、修改已有功能、修复软件里的问题。
安全固件更新:以安全的方式去实施固件更新。
安全,就是防止固件在更新实施过程中被修改、被假冒以及被窃取的威胁。安全的三要素是:保密、完整、可用,需要在安全固件更新的准备以及实施过程中得到保证。完整可靠属于安全属性里的完整属性。
5.1.2 为什么需要固件更新
“离开软件,芯片就是一堆沙子(Silicon Without Software is Just Sand)”。离开固件更新,芯片就在回归沙子的路上。因为如果没有固件更新,软件不再适合应用场景,或者停止工作,那么这个芯片也可以说重新变成了沙子。
固件更新很重要,可以从以下几个方面理解:
5.1.3 新功能带来新弱点
固件更新作为一个系统的修改入口,是可以被恶意的攻击者利用。同时,对安全来说,新功能总会带来新弱点,固件更新的自身的功能设计也可能带来新的安全弱点。黑客可以用多种手段对固件更新进行攻击:
为了理解 STM32 安全固件更新,可以先看普通的固件更新如何设计,包括:一般流程、端到端之间的传输以及为了支持传输的数据结构,在 MCU 中的存储以及支持存储的数据结构。
在对固件更新的一般原理有了基本认知后,再看固件安全更新需要额外引入哪些变化,以及 STM32 如何在安全固件更新里实现这些额外的需求。一定要谨记,STM32 安全固件更新离不开 STM32 安全启动这个平台安全做基础。
5.2.1 固件更新的流程
5.2.1.1 一般情况(近距离2方)
一般情况下,固件更新有近距离 2 方参与:
例如,开发人员可以用 ST-Link,直接将新的固件通过 JTAG 烧入到 STM32 MCU。这个更新的过程可以是:
这种有两方参与的固件更新的通讯方式,并不仅限于 ST-Link JTAG 通讯,还可以通过 UART,此时需要将 STM32 MCU 切换至 System memory BootLoader 启动模式或者使用用户自己的启动加载器。
更复杂的情况,尽管也是两方参与,但是他们是通过其他通讯协议,比如以太网通讯。不过,这种复杂性,只是体现在底层协议栈上,对于固件本身来说不需要做任何改变。例如,用户可以开发 BootLoader 充当服务器,允许其他网络用户连上来,发送指令,发送固件,然后来更新系统。
5.2.1.2 IoT时代
在IoT时代,需要一对多的更新,参与者一般是 3 个:
一对多的情况下,管理员将固件上传到 IoT 平台,触发所有设备的更新操作。
IoT 平台则根据各个 STM32 设备的具体情况下进行固件更新的消息通知,以及固件内容进行推送。IoT 平台也可以被动接受请求,这种情况下,固件更新的操作不一定是同步的,可能要花费几天,甚至几个月。考虑到有些设备的离线,若需要等待全部设备的版本升级完成,可能等待的时间更长。
固件更新的网络拓扑结构的改变,不改变传输的固件更新的内容组织。传输的内容,依然可以是:
5.2.2 固件传输
固件更新的通讯上,需要考虑服务器与设备之间的距离的影响。是否需要远距离更新,例如选择什么样的通讯接口;是否不依赖通讯的可靠性,例如是否需要使用断点续传、校验和;以及是否假定通讯带宽总是很低,例如是否需要差分更新以及是否需要压缩。
距离:本地更新与远程更新
本地更新:近距离连接设备,一般通过 JTAG 或者 UART。
远程更新(FOTA):遍布在全球各个角落的 IoT 设备。
仅仅在安全启动里支持下载功能是不够的,还需要用户固件支持下载功能。
可靠:断线续传与校验和
带宽:差分更新与压缩
传输中的数据结构
无额外数据结构:最简单的固件更新是不添加任何额外的数据结构,在通讯链路上传输的就是固件本身。当STM32 完整的收到该固件后,直接将固定位置的程序代码擦除,然后将新的固件写到该位置
带有元数据:一般推荐传输的固件加入元数据信息,可以包括:
为了断点续传或者差分更新,需要标记所传输的数据在固件整体中的位置;校验和需要包括元数据及实际固件,这样可以检测固件头部和实际固件是否在传输过程中没有损坏。
在实际开发固件更新时,元数据可以细分为固件整体的元数据以及每次传输的元数据。
5.2.3 固件存储
固件存储
固件冗余
单镜像(无冗余)
双镜像(冗余)
BootLoader 和 固件冗余并不冲突,可以同时拥有。STM32 SBSFU 提供了BootLoader + 是否固件冗余的选择,包括BootLoader + 单镜像 和 BootLoader + 双镜像,供用户选择。
整体存储结构:固件的存储位置并不影响固件更新的存储的逻辑数据结构,然而是否冗余则意味着在设计时是否需要预留足够的空间。如果是 BootLoader 加固件冗余,则 Flash 会被划分成几个部分:
5.3.1 为了安全的额外设计
要让一般的固件更新变成安全的固件更新,体现在:
总结,如果需要让固件在更新过程中是保密、完整、可靠的目标,要在流程上增加额外的环节,例如增加加密与解密的阶段。增加的环节会影响到传输过程中的设计选择,同时数据结构设计时需要增加额外的字段。
5.3.2 固件更新的额外流程
5.3.3 固件的传输选择
使用 STM32 来构造安全固件更新,希望在数据链路上传输的固件是加密的。
前面提到有 2 种固件更新的流程,一种是近距离通过 JTAG 或者 UART更新,一种是通过云端来更新。对于保密性,强调的是端到端的安全。除非很非常特别信任第三方平台,否则,在固件的加密应该是整体加密。也就是说,即使有了中间节点 IoT 平台,固件更新可以从起点就开始加密,同时不需要在中间节点解密。
考虑到安全,CRC32 是可以被加密级别的哈希函数以及认证码取代。
当然,这种情况下,差分更新会受到影响。如果使用差分更新,需要仔细评估整个更新链条上的安全性。
对于压缩,因为从服务器端传送至设备端,可以认为风险低。
5.3.4 固件的存储
考虑到整体安全的情况下,假设服务器被攻破了,那么所有的固件更新操作都可能错误,推荐用户加入冗余。例如服务器会让 MCU 擦除掉 MCU 的固件,这个时候 MCU 作为一个被动执行者,不可能不执行。如果 MCU 只保存一份正确的固件,那么下次重新启动的时候,可能 MCU 就无法运行了。如果设计时已经考虑到冗余,在 MCU 里总是保存一份正确的固件代码,使用 IWDG 来判断启动时是否有正确的固件执行,如果没有,则总是从保存的正确的默认固件里启动。
5.3.5 安全固件更新的数据结构
在传输的数据结构中,增了了随机数字段,这是加解密算法所需要的字段;增加了哈希字段,是为了检查固件的完整性;增加了签名字段,是为了检查固件的可靠性。
在启动数据区中,增加了固件的版本,用户的密钥信息。
5.4.1 STM32 安全固件更新
STM32 固件安全更新离不开 STM32 安全启动。
STM32 安全固件更新的流程:
从 STM32 SBSFU 的流程可以看出,固件的完整性,以及固件 header 的完整性都很重要,都必须进行检查。 STM32 SBSFU 中固件 header 中包含固件的哈希值或者认证码。为什么可以包括一个哈希值,而不是一个签名值?因为 header 是被签名或者受 AES-GCM 认证码 TAG 保护的。
STM32 SBSFU 典型的 header 如下:
当然,这里的流程,选择的是固件冗余的方案,也就是双镜像 dual-image。如果是单镜像 single-image,流程会有所不同。因为单镜像不支持用户固件的下载,所有只能从 BootLoader 里下载。在这种情况下,就不需要重启再去进行解密验证操作。
同时,这里的流程选择的是固件带加密的流程。如果关于固件不需要加密,只要保证完整性,那么,解密是不需要的。而完整可靠性认证在 STM32 SBSFU 中都是存在的。
STM32 SBSFU 中安全固件更新使用了加解密技术,支持三种方案:
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 是一个很好的定制参考。
安全固件安装的本质是:我们能否相信工厂的制造过程是安全的过程。如果我们相信工厂是可靠的,相信他总是按照我们的指示,例如,我们希望他不要泄露固件代码,我们希望他不要过生产,他们都能一一办到,那么安全固件安装时没有永无之地。
然而,总有一些工厂,或者工厂里的员工,为了一时利益,可能会将研发设计公司辛苦开发的源代码(通常是厂商的二进制固件代码),直接泄露给其他第三方。同时,他们也有可能将委托生产的产品过生产,例如我们希望他生产1000台,结果他们生产了1500台,1000台按照合同提供回给厂商,使用正规的商标与品牌,另外500台则不知道通过什么样的商标流入市场。如果该产品在市场上很火爆,他们获得的利益会远远超过代工的收益。
除了正常的法律手段外,STM32 技术手段可以防止工厂泄露受委托生产的固件二进制代码,同时防止工厂进行过生产,获得非法利益。
这个解决方案里,有三个角色:
安全固件安装是 STM32 提供给客户的解决工厂不可信的安全技术,属于全生命周期安全的一部分。
本章主要对标准与认证做概要性介绍,帮助开发人员了解:
标准其实就是一些规范文件。
标准可以来做什么?标准的最大特点是可以反复使用,而且达到通过标准,能获得一致性结果。
遵循标准对安全有什么价值?不遵循安全标准,基本上等于不安全。举个栗子,在 STM32 加密库中,看到 RSA PKCS 1.5 的函数名称,而不是直接的 RSA 运算。这是因为 RSA 算法的基本原理只是给我们展现了教科书级别的算法。在实际应用中,如果直接使用,非常容易受到攻击。例如,假设我们所有人的公钥指数都选为 3,虽然模数 n 不同,私钥指数是不同的。但是如果没有按照 RSA PKCS1.5 进行填充,那么根据中国余数定理,如果同一内容多方加密后,攻击,可以在不知道私钥指数的情况下,将明文恢复。这是所谓的广播攻击。而如果遵循了 RSA PKCS 1.5 的随机填充要求,这个攻击就无法奏效。这是我们需要 PKCS1.5 的原因。当然这些年随着攻击防护研究的深入,PKCS1.5 标准也正在更新换代中。
所以安全技术开发中有一个非常重要的原则:要遵循标准。很多公司都规定要使用公开的算法,要用公开的标准。在做安全开发时,一定要记住:不可随随便便搞发明创新,否则极有可能给系统留下安全漏洞。当然,在遵循算法标准的情况下,可以在实现算法的过程中加入更多的防护。
当谈论标准时,通常涉及两类标准。
大类标准,例如 FIPS(美国联邦信息处理标准),再如国密 SM(商用密码)
一些具体的算法或者协议标准,该类内容很多,这里只谈STM32 用户开发中常用的。
除了这些国际算法标准,中国也有相应的国家密码标准,称之为国密。如 SM1 为对称密钥,其加密强度与 AES 相当;SM2 为非对称加密,基于椭圆曲线 ECC;SM3 摘要消息;SM4 是一个对称密钥算法。
通讯安全标准也是有标准,如 TLS。
处理信息安全的时候会发现,基本上天天需要和这些已经标准化的算法与协议打交道。
通用控制器目前信息安全强制认证并不常见。但是如果所销售的产品所在行业已经有国家强制性标准,比如要销售给美国政府的事业单位,则认证是必不可少的步骤。还有一些客户是基于风险所考虑的。不同的国家有不同的法律要求,如果因为安全的原因,例如用户个人信息泄露,而导致制造商分销商被起诉,则有可能面临巨额赔偿。而认证可以降低该风险,甚至免除责任。最后一种则是客户为了提高客户满意度,提高产品区分度,所通过的一些认证。
如果打算去做认证,那么在做产品计划的时候,就需要认真考量时间和费用这两点。很多产品认证,一方面需要的时间很长,半年甚至一年;另一方面,认证的费用也很高,百万级别的费用并不少见。
有时候产品认证的范围,不仅仅是送测的产品。整个公司的研发,生产环境以及公司的产品制造流程都可能在认证范围之内。
认证实验室和颁发证书的认证机构主体通常不是同一个。
为了通过认证,一般需要提交文档、源代码、产品样片。认证测试很多时候价格不菲,而且按次收费。为了通过认证,就要选用正确的芯片与软件开发包。STM32 与 STM32 加密库在这个时候就可以发挥作用。
认证实验室或者受委托机构,会对你提交的文档、代码进行审查,同时进行产品安全测试。如果认证标准需要评估管理体系生成流程,则认证机构会组织对提交材料的单位进行现场审查。
文档审查、产品测试以及现场审查都符合标准,则由认证实验室或者受委托机构提交一份评估报告。
认证主管机构认可评估报告,会颁发相应证书。证书一般都是有期限的。
收到证书后,认证进入维护阶段。若产品发生变化,则可能需要提交产品更改申请。若产品到期,则要考虑是否重新延展认证书年限。同时,认证管理机构会定期对获得证书的单位进行监督审查。
当然,有些认证可能只需要提交问卷或者提交自测试的结果就可以了。那样的话会比较简单,但前提是认证机构相信这个自测试结果。
NIST FIPS 140 是美国国家强制的标准,该标准是美国 NIST 联合加拿大制定的,在英国等其他国家也得到承认。当前版本是 FIPS-140 2。NIST 创建了 CMVP 来认证加密模块是否达到了 FIPS140 的要求。不符合该标准的产品无法进入美国政府的采购列表。产品一旦符合它的规定,并得到认证,就已达到了美国和加拿大公共机构的安全标准,可以向美国和加拿大出口。这个认证分为两部分,CAVP 密码算法验证体系,以及 CMVP 密码模块验证体系。CAVP 密码算法验证体系是 CMVP 的先决条件。
FIPS-140 2 具有 4 个等级:
FIPS-140 认证过程时间长,成本高。如果产品通过了 CMVP 认证,获得 FIPS-140 认证证书,可在产品中加入 FIPS-140 2 inside 等标志。
STM32 加密库为了减轻客户 FIPS-140 产品认证工作,提供通过 CAVP 认证的加密库 XCUBE-CRYPTOLIB。
STM32 的 CAVP 认证包括以下内容: