
STM32 MCU内建多种安全功能,为设备信息安全打造坚固的平台,在这个平台的基础上进而可以运行可信的进程以及密码学操作。STM32 MCU底层硬件具备的安全能力能够覆盖存储访问保护、代码/系统隔离、启动入口限定、防篡改检测、密码学算法加速等多方面的安全需求,例如:* { w& t, Q& v8 F 4 ~) \) Z6 u: |$ p' s }% I0 R7 u1 s4 b0 s: G' ]7 _ 身份识别:UID, OTP0 Z. v7 D7 A7 n( B 存储访问保护/软件IP保护:RDP(读保护),WRP(写保护),PCROP(专有代码保护),OTFDEC(On-The-Fly Decryption实时解密模块) - M3 y2 J5 e" G: d" _/ y% z0 K, m 代码/系统隔离:MPU,Firewall(防火墙),TrustZone,Secure User Memory(安全用户存储区) 启动入口限定:RDP2,BOOTLOCK(启动锁定) 4 s' k9 S: a6 [ n$ H _ 防篡改检测:Anti-Tamper5 [* ^/ W9 {- L ) W! E1 T) M2 Q% L# i ! y4 Z2 U9 O, \% G5 C, f! S 密码学算法硬件:AES,HASH,PKA,TRNG8 q* T- s) p( } ( Y' {9 f# g7 }5 i9 j/ W 芯片硬件安全功能结合软件能够实现更多应用层的安全功能,例如安全启动,安全更新,安全存储,安全通信,可信执行环境等等。 8 b' g, }( G. A5 u9 d r! f# c: ~ 这次我们将手把手教你如何基于防火墙和PCROP在STM32L0上实现关键数据和代码保护。6 k: B' \) _# ~ STM32L现有5大子系列,STM32L0、STM32L1、STM32L4/STM32L4+和刚刚宣布量产的STM32L5,为低功耗应用提供了完整的解决方案。 STM32L0系列是意法半导体的入门级32位超低功耗MCU产品组合,旨在实现卓越的低功耗水平。由此产生了真正的超低功耗MCU,125℃时的功耗为世界最低。ARM® Cortex®-M0+内核与STM32超低功耗特性的独有结合,使STM32L0非常适合电池供电或供电来自能量收集的应用。8 e* {/ V& A+ t) h6 p8 M ![]() STM32L0和STM32L4系列的硬件除了通常的RDP、WRP,还带有Firewall和PCROP硬件安全特性。使用STM32的security硬件模块(Firewall和PCROP)实现对关键数据(密钥)的保护:应用代码可以使用加解密功能,但是无法拿到密钥。本方案提供了在STM32L073(STM32L073-Nucleo)上的参考实现,以及相应测试代码。 方案包的内容包括: 2 A. U1 Y2 K, p 5 O& ] T$ _' I0 A% y8 ^$ B. i 三个IDE下的示例工程:IAR,MDK,STM32CubeIDE! e' Z7 n( h* [) U# K/ `5 W, K( m2 Q ! }) I4 f8 I8 |! b 方案介绍文档 1 r6 p9 R$ g. S3 o; Y; T* Y 7 n/ a4 ~0 P* J; k% [4 O 脚本工具(生成秘钥相关文件) PCROP私有代码保护 : ]) S. @. c- s8 {5 n+ E" |% V7 w r PCROP提供了一个额外层次的代码保护,PCROP可以设置内部Flash的指定区域成为只能执行的代码区域,该区域无法被读取(作为数据),修改或擦除,也就是说无论是通过调试器还是芯片运行的代码(包括运行在PCROP区域内部的代码本身)都无法读取PCROP保护区域的指令,而且,即使在RDP0的状态下,PCROP所保护的区域也无法通过调试器进行数据读取。2 O7 j, G L6 v1 e% d- l PCROP的常见用途是联合开发,对部分软件IP进行保护,受保护部分的代码不能被二次开发用户读出或修改,但是其中的函数API可以被调用。PCROP同时也可以成为对数据进行某种程度保护的手段,这时候,需要将被保护的数据转换成一段指令,只有执行该段指令之后,数据才会被恢复到寄存器或者RAM当中。# Q; s: }: T D& f& l2 H PCROP主要防止来自CPU、DMA以及通过调试端口的数据访问;PCROP主要保护用户片上Flash的一段区域(由选项字节设置区域范围,保护上电即生效)。其保护机制是PCROP区域仅能被指令访问,不允许数据访问(包括DMA 、调试访问) 。; N' `7 q% d) W8 i0 s7 V" x6 V" a6 f 主要用途包括: + V8 E* K* i- c Z5 d e ' J4 @7 ]+ x$ I, O1 X: f1 h 常用于保护关键代码,只可被执行,不能被读出(dump)' y7 x q2 }# e/ S 防止内部攻击:注入恶意程序读取关键代码; k# `+ o' P# F1 t$ l' U 防止外部攻击:来自调试端口的读取 也可用于保护“代码化的” 关键常数数据" w9 H: b0 V9 ^) q0 U4 X2 r1 t 需要先将关键常数转换成代码 CPU执行这段代码,会把关键数据恢复到SRAM或者寄存器中,这段SRAM再 使用STM32的其他存储和执行保护模块,比如Firewall保护,防止恢复出来的 关键数据被用户代码直接读取;或者寄存器本身是WO属性6 N$ z z5 \8 w1 H0 |$ {6 y( ~ Firewall防火墙 . l( y0 C4 M7 z8 P1 C使用Firewall防火墙硬件IP,可以在片上Flash和RAM中隔离出一块隔离区,该隔离区具有唯一的入口,只有从该入口进入后防火墙隔离区域内的代码和数据才能够被执行和访问,从防火墙保护区域跳转出去之前也必须进行关闭操作才能够允许跳转,也就是说函数只能从设计允许的出口退出,除此之外的任意的调用、访问和跳转都会引发系统复位。 + }+ Y' @. O. Y6 d3 }7 ? y! M 通过防火墙提供的隔离功能,部分关键的软件功能和数据可以放置于防火墙隔离区内,防火墙外的应用程序只能够通过防火墙提供的唯一入口函数调用防火墙内部的功能,而无法直接看到其中的代码或者数据,起到将关键操作与系统其它部分的软件隔离开的作用。& u$ x& V+ A6 p 防火墙的功能是系统运行后动态开启的,而不是来自OptionByte的静态设置,但是防火墙一旦使能,则在当前的Power Cycle始终有效,且不能被禁止,直到下一次复位。' E& J1 H2 s0 r2 G" n j 具有防火墙功能的STM32系列包括STM32L0和STM32L4。/ Q- w% q3 a9 y8 T 防火墙主要用于防护CPU,DMA,保护用户片上Flash、片上EEPROM 和 片上SRAM上的指定区域(防火墙里的区域)。 ' u! g, V: {# A/ [; d 保护机制: % J# d6 |0 s1 `0 |, Z' S& F/ U 防火墙里的代码、数据不能被墙外的代码直接访问6 f, V) |" C! X& ?9 y7 z. l 9 x- j+ r9 r3 ]* j2 c 必须走固定入口(Call Gate)进入墙里再访问% M4 _: `" Z. Q# A" W& X 6 d# f1 } i# N& [' F 2 }9 q/ h! I9 R' F# } 出去(跳转到防火墙以外的代码)之前必须置位寄存器- p& H h4 G$ `+ |* Q* c* L1 _( y* r 4 g y7 v5 c9 e- X. ]9 f ; V5 U5 h6 e3 N. ]0 h9 i- r" u 否则产生系统复位" c- ` Y2 J4 t4 ^ L. Z* e0 K (对中断的影响: 进墙之前,应用需要关中断;出墙之后,应用需要开中断) 3 v& _; P; ?2 r! c# i 由寄存器设置范围并使能,即上电缺省没有保护0 U8 S8 w" k! L1 E ' a) _) \# p: h* I, a6 o v! U 一旦防火墙使能,保护生效直到下一次系统复位$ |$ J2 r9 Z! b9 Z2 w , V) J3 o6 p5 c# w' Q, G& n& A, D 使用PCROP和防火墙保护关键数据和操作: 密钥及使用密钥的加解密操作2 N% s7 V. @, ~ * m- _# K( ?6 C; K' j & {% v) D e8 U0 E$ z 加解密函数放在防火墙(Firewall)的Code segment; R* c1 ~2 G8 z+ B! X# S/ F ; {8 g& f' ?# C- l 终端客户的密钥代码化后,放在PCROP区域里,该区域也是防火墙的一部分;执行密钥代码把密钥数据恢复到也由防火墙保护的SRAM中0 C/ u0 \! B1 z/ _# Z4 {/ F4 w* ` 7 e! n3 w4 m) S7 j3 u x4 {5 C, ^ 墙外的用户代码要调用加解密函数,必须走固定的地址进入(防火墙Call Gate),并以参数的形式指定调用具体的加解密函数 墙内的加解密函数调用同在墙内的密钥代码,从而把密钥数据恢复到SRAM中,供加解密函数使用 : e& V+ k& r, i, q+ E 3 g6 p. P# y7 [# n& e 墙外的代码不能直接调用墙内的加解密函数,也不能直接读写墙内的数据,否则系统复位+ T9 x" X: g$ \0 L) y8 D# P 保护方案图解 4 i9 ~7 }3 A: {5 N6 K. F 允许的访问: 6 [4 S8 b2 ?% Y7 e' J& m. u 墙外代码通过callgate进入墙内,调用墙内代码 ! X! F5 r' L2 o$ h# J) d / a# m% Y c1 p& E$ U 墙内代码执行也在墙内的PCROP区域 ! u$ H/ ?2 F% x7 \, S6 J! n 墙内代码读写墙内的数据 非法的访问: 7 ?% O, [' `2 C , L' z! x* e# g5 V# q1 T 墙外代码企图直接调用墙内代码 : \9 ?' r" L# |7 j# \ 墙内代码读写墙的PCROP区域/ H" H( k- _' T3 x 墙外代码企图直接读写墙内数据 8 ?4 |' _" H; Y& B ![]() ' b# k$ y8 h$ ] 和不使用PCROP和防火墙的普通方案比较: 9 k# f0 J% w" V; K, D 未使用PCROP、FW功能的方案 " i- @/ U( P0 ~* l f2 O 密钥数据未受保护 ( x3 ]. C8 C8 V9 o3 Z8 H 内部攻击,注入的恶意程序可以直接读取 外部攻击,通过调试probe可以读取(没有 RDP的情况下) ![]() * |, L6 U& @, n. B 本方案 % f6 o, s: a2 {) u2 w5 W5 Z# P 密钥数据被PCROP和Fire wall双重保护 : [+ ]: E1 p, w3 j4 w3 F" @ 加解密功能被Fire wall保护 墙外代码可以使用加解密功能,但得不到密钥数据) e5 G: q* g7 ~$ f. a: o ![]() STM32L0上的密钥保护方案示例 6 j8 f2 L! y4 E( T( c1 b$ F) e8 C: o0 b7 |7 A STM32L0的PCROP) e) {' g2 q' L6 Z ![]() STM32L0的Firewall ![]() STM32L0上的密钥保护Demo(1)! x8 N7 b1 S# p. m$ A ![]() 3 ?( f Q! _% |% j& V" K STM32L0上的密钥保护Demo(2). S) s& N% I. u( ~ ![]() STM32L0上的密钥保护Demo(3) ![]() 墙外代码合法调用墙内代码:走Callgate进入 ) o& D: ~! E- O9 X. l . o' p, f2 a; G' T/ ? 外部代码访问墙里的代码,进入callgate之前需要关中断,退出后开中断 " t$ h5 y- b; Y SE_EnterSecureMode&SE_ExitSecureMode 给函数指针赋值 fp=...CALL_GATE_FUNCTIOIN_ADDR +1 = 0x0800 05051 X7 Q2 q% K8 K4 A6 o : F9 q7 W& F2 c# }* T 4 B$ \3 J3 J/ G% s- z& g( u 该函数指针带两个参数,发起调用 ![]() 墙内代码可以随意调用墙内代码、数据 ![]() 9 h9 G A6 ?# E7 P STM32L0上的密钥保护Demo(4)& ], E4 U5 ?: y8 ?0 M7 d ![]() 墙外代码的非法访问,被识别和阻止6 B E: t4 B( H5 o8 j( f0 U) r2 V# @ ![]() 应用key的生成和烧录0 G2 W1 J, v7 H! M ![]() 9 ]9 C4 ^2 L% Z! M5 l7 t( v$ [ 例程Setup . v. S1 M7 s( k: F- p8 p9 M- |2 q; r" J7 ^ ·解压缩例程包/ E9 G4 Z4 W. E2 P9 N ·下载X-Cube-Cryptolib软件包(https://www.st.com/x-cube-cryptolib),将软件包中FW库 L0对应目录下的Middlewares文件夹拷贝到例程包对应位置(参见下面左图) Y0 D0 _4 X# _( I ·使用你熟悉的IDE,分别打开Boot和App两个工程,编译后分别下载* |5 q8 r9 X- |; g0 s' c7 H ·使用STM32CubeProgrammer把例程包里的密钥文件烧写到0x08004000(参见下面右图)3 I) @ J7 Y0 T" s; b6 C+ S& h ·使用STM32CubeProgrammer设置0x08004000开始的4KB区域为PCROP保护 ·STM32L0上,闪存扇区的“写保护”和“PCROP保护”都由WRPOT控制; S+ u: l! K1 x' T ·当WRPMOD=1,WRPOT=0,表示对应扇区被PCROP ·勾选WRPMOD;除了WRPOT4之外,其他WRPOTx都勾选上 ·打开串口调试助手 115200,8 bit数据位,1 bit停止位;无校验位,无流控4 B' d4 z" q- X4 f ·STM32L073-Nucleo板:按复位键,运行 ![]() 测试案例(1) 代码化KEY未烧录或已擦除:使用hardcode的缺省Key2 t8 x: v" s3 M# v , L* K" @6 b0 r0 [( x # |: V& p" C. n& w# `$ b' m BL不会设置和使能防火墙8 X& v5 d7 T% g6 l 9 R! W$ ]8 ?1 \ 用户未使能PCROP& i2 c* ~& k; U, ~* }; o; P 2 U" Z( C" e* w4 m5 Y0 \ case 2: 读取墙内数据 → OK( s+ q' B* s5 Y5 b case 3: 写墙内数据 → OK8 q1 m( l! \. K; s case 4:直接调用敏感代码 → 执行0指令码, hard fault case 5:直接读取敏感数据 → OK case 6:合法调用加密函数 → 加密失败4 c5 \( }; b$ J case 7:合法调用解密函数 → 解密失败 ![]() t- C5 ?4 H, M" M4 A, X: y ![]() ![]() 测试案例(2) ![]() ![]() ![]() ( o {; N3 w7 q- t. ~0 _ IDE相关的链接文件 存储区域分配及其Layout# e; }5 c% W$ F0 W9 D ![]() / Q% V) D2 `) I& z2 e4 [( N3 l8 o" a 把函数放到指定地址 ![]() ; ~" R; X! E5 h- V! A, i! |% c: l& ]& V 通过链接文件实现期望的存储区分配- Boot工程,IAR环境下的 linker.icf 文件 ![]() 1 @" J: e. T. Z* }! F# ~0 \2 O. ~ 通过链接文件实现期望的存储区分配—— Boot工程,MDK环境下的 linker.sct 文件6 h# s Y* V& \4 N ![]() ; F$ @- e3 V! Z) @$ s8 M4 d 通过链接文件实现期望的存储区分配 ——Boot工程,STM32CubeIDE环境下的 linker.ld文件 ![]() 1 h$ _4 O$ S* w0 C! Q 通过链接文件实现期望的存储区分配 Boot工程,STM32CubeIDE环境下的 linker.ld文件& ?6 ?0 m$ K5 K% M. ^7 g ![]() |
STM32如何分配原理图IO
STM32ADC过采样及几种ADC采样的处理方法
分享一个STM32L031的串口调试程序
基于STM32L051使用CubeMX生成工程文件ST系列芯片通用经验分享
基于STM32L051开始添加需要的代码经验分享
STM32L051测试I2C协议设备的添加经验分享
基于STM32L051测试Flash和EEPROM的读写
基于STM32L051串口测试与Enocean模块通讯问题
基于STM32L0的EEPROM读写经验分享
基于STM32L0 ADC使用HAL库关于校准问题经验分享