在 STM32 系列里,STM32L0、STM32L4,提供了 Firewall 硬件。Firewall 硬件提供了调用门 Callgate 技术。也就是说,对于安全敏感的代码与操作,只有一个入口。任何不通过这个接口,而对受保护的代码与数据进行访问,则会导致系统重启。如同字面意思,Firewall 形成了一个围墙,形成了一个城堡,同时提供一个入口,来提供运行时内部的保密、完整、可靠、可用特性。
通过 Firewall 单一入口的功能,可以形成对外的简单接口。该接口简单,所以安全敏感的操作细节就不会对外泄露。因为外界只能通过接口访问,那么在内部操作之中所使用到的密钥也不会泄露。
STM32 Firewall 通过启动代码激活后,就不能再关闭,该功能可以保证安全敏感的代码和数据在上电周期内不会被非法访问。这不同于 MPU。MPU 一定要配合特权模式与用户模式的分离机制,否则设置会被修改。
Firewall 的缺点是中断处理。中断处理需要读取中断向量表。如果中断向量表放在防火墙之外,则会造成防火墙之外的读操作。那么根据防火墙的保护机制,则系统会重启。如果放在防火墙之内,则中断处理函数需要继续调用其它函数,那么所有其它函数都要放入防火墙内。考虑到实际系统要处理的中断情形复杂,在实现中,一般在进入防火墙之前,会将中断关闭。
同样的,作为一个动态保护技术,Firewall 不能单独存在,必须结合防外部攻击的 RDP 一起使用。
STM32 SBSFU 在 STM32L4 上利用了防火墙增强安全。使用 STM32L4 SBSFU 需要考虑中断带来的影响。
既防外又防内
STM32 也提供了不少内存保护技术,既可以防护内部攻击,例如恶意代码注入,也可以在 RDP 为0的时候起到一定程度的对外防护作用。
PCROP
我们先来看几个场景:
① 设想某一个公司,它有一个非常好的算法。所以它不希望普通权限的员工了解它。但是这个算法又要在开发中被其他模块或者说其他员工使用。源代码肯定不能提供。提供二进制代码,也面临被复制的风向。那到底怎么办?
② 设想某公司有一个模块,需要提供给其他公司在此基础上做二次开发。需要对模块进行保密。源代码肯定不能提供。提供二进制代码,也面临被复制的风险。不提供的话,其他公司又没有办法进行二次开发? 那到底怎么办?
③ 安全启动包括校验流程与密钥。为了安全,自然并不希望其他人用任何手段来了解。
这几个问题本质上都是同一个问题,也就是希望能够保护一段代码既不能被外部 JTAG 读出来,也不能被后续软件访问。此时,PCROP 就可以发挥作用了。
很多 STM32 系列都有的专有代码读出保护 PCROP,即使是默认的 RDP 级别,也就是 RDP 为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 read in code memory 没有在代码区的数据读取。用户在生成受 PCROP 保护的代码,需要根据不同的编译器,勾选不同的选项。
PCROP 所保护的区域只允许执行,也就意味着无法对它单独擦除。如果需要更新,则需要在 RDP 从1到 0 进行整片擦除后,才能重新配置。整片擦除时,也可以选择保留 PCROP 所保护的区域,不做改变。
STM32 SBSFU 使用了 PCROP 来保护密钥。
Secure User Memory
安全启动构造了一个信任链,它是一级一级来检查,一级一级的传递信任。尽管我们使用加解密技术验证了后续环节,后续的环节应该是可靠的。但是请记住没有绝对的安全。我们不知道,是否有其他的攻击手段,让恶意的代码还是混进来了。
所以,对于安全链条的起点,信任根,我们有一个保密的需求。为了安全,我们不希望信任链条后续的环节,来访问这个最初的安全启动代码。即如果我们有办法,将安全最敏感的部分,也就是这个信任根,与后续的环节隔离开来,那我们的系统就会安全很多。
我们前面介绍的一些技术,例如,动态保护技术 MPU 结合特权模式与用户模式,可以用来构造这样的一个隔离防护。当然使用 MPU,特权模式与用户模式需要细心的管理。
再例如 Firewall,可以用来构造一个隔离防护。当然,使用 Firewall 需要小心处理中断。
如果除此之外,还有没有一种简单的设置,让安全启动的代码与后续代码彼此分离呢?这个时候,Secure User Memory 就可以发挥作用了。
在最新的多个 STM32 系列提供了一个被称之为安全用户存储的功能。这些 STM32 系列有H7、G0、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 还是1,JTAG 再也无法连接上去,相当于达到 RDP2 的效果。
在使用 Secure User Memory 时,请务必参考实际所选用型号的用户手册,或者参考我们的 SBSFU 的实现。STM32 SBSFU 使用了 Secure User memory 来进行安全启动代码与用户代码的隔离。
WRP
WRP 可以将某段区域或者空白区域设置不可写,那么代码通过协议栈错误擦除软件或者写入到空白 Flash 就不可能发生。所有的 STM32 系列都具有 WRP 这个功能。WRP 是个静态设置,直接通过选项字节进行设置。WRP 既可以防内被程序误写,也可以防外,防止JTAG 擦除。然而,WRP 的选项,在 RDP 不为2的情况下,是可以被修改的。所以如果希望 WRP 设置一次就达到一个 Read-Only 的效果,需要配合 RDP 为2使用。
系统监控
如果攻击已经发生,最重要的事情是能够监测到。对此,STM32 提供了多项安全监控技术。
IWDG: 独立看门狗。如果外界攻击发生,导致系统不能及时喂狗,则系统会重启。
Anti-tamper: 可通过将 MCU 防攥改管脚连接到设备外表面,若设备被强行打开,则会触发相应操作。
Clock Security System: 可监控外部时钟。若外部时钟紊乱,则自动切换到内部时钟。
供电电压监控:如果电压降低到某个水平,则系统不再响应。
STM32 SBSFU 目前实现了防攥改的软件功能。用户可根据需要加入其他硬件功能。
相关文章