你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

信息安全主题 | OTFDEC efficiency 基于 STM32H735G-DK 板的验证

[复制链接]
STMCU小助手 发布时间:2021-7-14 13:49
相关文章链接:: h! Z4 z0 c2 f. t5 B# U
1. 信息安全专题 | 代码保护(1)使用STM32H7新特性保护片上代码的机密性
, _( E$ K) l. e2 T* F$ c2. STM32信息安全概览:是时候KO安全问题了!- r/ A2 ?. h7 [3 k: D7 Q& s
3. 信息安全专题 |  安全存储(1)STM32H7的密钥存储9 l% e1 X/ T5 O  \
4. STM32信息安全概览
; C7 S( Y0 @( @' t1 a; F; V5. 信息安全专题 | 手把手教你如何在STM32L0上实现关键数据和代码保护
/ O6 o  x* m0 q: c; P# U6 E6. 信息安全专题 | 安全存储(2)STM32WB的用户密钥存储 (CKS)
/ b, Y) v" B' p  G+ I" |( a- V7. 信息安全主题 | 代码保护(2)使用STM32L5 TrustZone 特性控制调试端口访问,保护片上代码安全
, g2 J/ T0 d" z" a4 B8. 信息安全主题 | 嵌入式系统软件漏洞与安全隔离
/ c& K% [% o; g& \- E. B. l3 M) T' @; J& |5 b4 t1 e

" H- K4 K) v. Q) e# ^/ @前言
/ {( q0 x  h8 W从STM32H73x系列开始,我们引入了一个新外设模块,OTFDEC。它的全名叫做on the fly decryption。它的引入,可以帮助大家解决代码保护的痛点。
* S. ?% M. g4 g) P, h; g  f" u# |) r
8 l' C4 F( g. T8 b1 a! n
OTFDEC简介6 ~% f; E  c* K0 P0 `0 \' r0 l1 I
大家都知道,代码存储在片内Flash,只要做好了JTAG调试端口的保护和片上关键代码的隔离,在防止逻辑攻击和直接探测层面,还是相当安全的。但是片上Flash毕竟容量有限,在一些应用中我们需要把代码放到片外Flash存储甚至直接从片外Flash执行。片外Flash相比片内Flash,在抗攻击方面就脆弱得多。片外Flash一般没有什么硬件层面的保护,只要知道了它的料号,它的读写时序都是可以查到的,那么读出来里面的内容就不是什么难事。
) N/ G7 F, y/ l0 f  O6 t8 P
- V. W2 }9 c) p0 n9 w

$ C/ ]5 l" u" z0 d/ _  V6 D所以大家一个自然的想法就是把代码加密后再放到片外Flash上,这样即使别人读出里面的密文代码,只要没有密钥,也无法获知代码的有效信息。" }# B2 k6 q( V+ F3 E# z
1.png
$ D/ O7 S- E6 |, h& l& {# p. Y* N1 Y4 v  u! h" K+ P
; h; C8 u- l- `$ U: s# v% L  [
就比如胶片中这样的典型拓扑结构:加密代码放在外部的Octo-SPI Flash中。$ b' y8 F: z8 x) o- _; f' F9 q% ]
对这种自然的做法,以往的MCU在执行片外加密代码时,需要先调用OSPI驱动,把密文代码读进来,比如放到SRAM中。然后使用MCU的软件或者硬件解密,把代码明文恢复到SRAM的另一个区域。最后MCU再从这块SRAM执行明文代码。; a' O) z9 J! L# C8 d" V
现在我们引入了OTFDEC这个硬件模块,它位于总线矩阵和Octo-SPI接口之间。把它配置好之后,内核执行片外Flash上的密文代码(在这里Octo-SPI Flash的映射地址是0x9000 0000开始),无需中间再用SRAM倒一次手,而是在OTFDEC的作用下,直接把解密后的代码送到总线矩阵上供内核执行了。也就是说,有了OTFDEC的配合,对于CPU来说,执行外部Flash上的加密代码,就和执行片上Flash的明文代码是一样的。
: f7 A2 c' o3 O4 z 2.png
+ H2 N( \1 y& G1 X
+ b- r/ ~2 W9 u1 v
! |4 h5 N9 T% O3 ~
为了尽量减少OTFDEC解密造成的延迟,OTFDEC被设计工作在AES-128-CTR模式下。不使用AES的链表模式,就是为了尽量缩短对目标地址上密文解密的时间。因此存储在外部Octo-SPI Flash上的加密代码也需要使用同样的AES-128-CTR运算得到。
: b: w8 h6 z; f/ O' {  F7 I3 P( U9 g" Y7 m6 {, x
: `1 S5 |- h. \; T/ s! ]
有一点需要注意的是:为了达到这样的使用效果,Octo-SPI需要配置到memory map模式。% Z7 z* ^) G3 E; s

* c4 _# {4 @* q% Q  _
# @% q0 q$ y2 h
目前,STM32系列家族中,集成了这个OTFDEC模块的有STM32H73x系列,STM32L56x系列,和STM32U585系列。$ ]$ `0 r  O0 c8 K/ M5 M  u# B

* O6 P) B. [( q# y
5 X* E2 e& b) D6 s7 y
图片
, C% n3 Z" A. Z0 t  g5 I7 C$ w, {' r6 _9 {+ p& E
  I* P# p9 O, ]% H* _! G; W
今天我们不是介绍OTFDEC怎么使用,而是回答前段时间在给客户介绍OTFDEC的时候,大家一个比较共同的问题:相对于直接执行外部Flash上的明文代码,执行外部Flash的加密代码,OTFDEC解密操作引入的延迟有多少?
6 M8 j6 i; u% E/ |; ~& Y
5 Q( n# n8 A, d) @- O3 Q实验设计2 ^3 v, s# e9 i3 o/ A! W
4.png ( Q* b7 s% n6 g1 [

4 `* u/ I% k. T$ M! K/ {
7 |& q8 u/ q( m% Z1 S: f/ S) D% |/ x
我们接下来设计一个实验,验证在OTFDEC参与下,内核执行外部Flash上的密文代码效率到底如何,用数据说话。9 ~6 p7 {3 R, }4 a/ `! Z  X* |

0 f/ O" y* |6 a
  u0 X  A  b; `4 p7 M, i7 ^5 @
我找了mbedTLS中一个自测程序Crypto_SelfTest,验证一下把它加密后放在外部Flash,内核执行完整套自测程序需要的时间花销,和执行外部明文代码的差异。为了进一步说明问题,还加了一个场景,就是这个自测程序明文放在片内Flash,内核执行它的花销会快多少。8 `4 r4 U% V, }& t. |4 H

0 ], s6 A/ G) @8 q4 J

# L& F( S8 @* H" T: c* ]: y! m这个Crypto自测程序经过最高优化等级编译后,大小差不多在63K作用的样子。
. o/ P  o/ K$ `) F$ t' W 5.png
" T( _# p; E- Y: g( Q' W# ]( Q/ v7 n+ Z1 A- R- }. K2 m

2 J& a2 N% I! e第一个场景就是最普通的,直接把测试程序灌到片上Flash运行。* w! Y  _& T( G! n
我们先来看一下这个自测程序,主要就是执行selftests这个函数数组里的自测程序。用户可以在mebdtls_conf.h头文件中去选择哪些自测子项被包含进去。现在我选择了6个自测子项。* ^: R9 R+ K" l3 O$ W
" n2 O$ _( S% N. {" n  }# O$ k9 K

0 c  h% k" c$ a& x% V然后在自测程序开始运行之前,通过检测是否有用户按键按下,来决定是否开启Cache。STM32H735集成ARM Cortex-M7内核,自带32K指令Cache和32K数据Cache。
: K" B0 i% F: B' s/ f/ z 6.png
% e% N8 d$ }; @: t  G: [
* O7 n5 x  E5 d9 S* c2 Z0 U

# u! ~0 u; o9 u* X4 P- y  t& n7 K) s因为要测量运行这给自测程序的时间花销,因此我们使能一个内核计数器,然后在每个测试子项的开始复位该计数器,在测试子项结束后把当前计数器的值,记录到全局变量的时间戳数组中。最后在6个测试子项都完成后,根据时间戳数组里记录的值,和当前内核运行频率,转换成时间花销。
# Y0 l. O' @' Z5 h5 k/ i
0 T; B& g# h4 y9 p7 p* y

0 ~) P- h2 @$ a% V$ g7 B由于场景1,是最普通的用法,即程序运行在片上Flash,因此它的链接文件就是STM32Cube包中的缺省配置。我这里以IAR为例,展示了这个测试场景下,code的存放地址,包括复位和中断向量表的存放地址。
9 _  v0 }4 x, y* A+ F: c 7.png 7 b3 v% a: h* J, w2 c# [' s

8 P2 e( y$ T+ m0 v% F2 H4 l* F

1 a! ^; C1 g. f- S1 S+ g第二个场景,自测程序运行在外部Flash。而STM32是不能从外部Flash启动的,我们按照常规的做法,从片上Flash首地址启动,因此在片上Flash我们放一个Bootloader。它的功能很简单,就是初始化OSPI接口,并把它配置到memory-map模式。然后调整堆栈指针SP,以及PC指针,跳到0x9000 0000开始的OSPI 外部Flash首地址运行。而那里,则是我的Crypto自测程序。1 A+ S% z, U. v" y0 m' w
$ N' d& M6 F/ B! d9 U
  K. h8 Q8 {$ ]$ Q, _' T: i
在场景2的自测程序工程Crypto_Selftest_ext_plain中,和之前的工程相比,只需要稍微做两处修改。链接文件,把复位和中断向量表放到0x9000 0000的地方,并且调整内核寄存器的VTOR值。这样子,一旦有任何中断或者异常,都是去位于0x9000 0000处的向量表取执行地址。
! @) J- a% `; i4 I7 _2 h 8.png 7 C/ N0 X' E0 b! k3 Y
第三个测试场景,boot loader工程相比第二个测试场景中,需要增加对OTFDEC的配置。而烧录在0x9000 0000的内容,应该是从场景2下第二个工程生成的project.bin,加密后的密文。这里,左边的Bootloader里是OTFDEC在解密,右边是通过PC端工具预先把代码做加密。7 f0 y' t2 c- }! L
1 i* c- ~& ~- `) j0 F, Q1 d
6 z- M4 p" l3 Z' Z7 o
由于是AES是对称加解密算法,因此OTFDEC的加密参数配置,要和PC端加密工具的参数一致。
$ v& o) i8 Y5 c1 D- ` 9.png 3 M/ a9 O4 Q( S% a# \, y
我们先来设置OTFDEC的解密参数,密钥key和初始向量IV。
! x% ^4 Y, t  V1 W' n  T+ c2 W7 k1 y

9 B% @% m  W% J" Z5 o密钥由用户自己指定,在代码里我们设置在Key数组中。按照数组的写法,考虑到ARM Cortex-M内核是小段对齐,因此这16字节的密钥,在memory中的存储顺序,应该如左下图所示。注意,我这里刻意让16字节的密钥中,每个字节的内容都不一样。为什么?我们接下来看。1 t! E2 h/ w! x" p4 l
  N; }1 V( B9 J3 d" J* P
: M2 }8 d6 m$ i1 f9 h
OTFDEC的IV,HAL驱动封装了一个结构体给用户来填写。由Nounce,OTFDEC将要作用的外部Flash地址范围,以及将要存放在外部Flash那个地址范围里代码的版本号。Nounce,也是由用户自己设定,我这里仍然刻意让8个字节的内容都不相同。# E( l4 [& x: h$ G6 m- g3 Y* Y# \
10.png * Y  d4 m$ V5 X4 h

6 w0 X* d# }( q" \9 ^" D( Q4 ?: ?3 a+ _: R

8 K+ T+ i$ U  g( ]" S! ]2 p接下来我们要配置PC端加密工具的参数了。这里我们使用openssl。
* R' p7 X9 I) z3 A7 Z6 A, m& o& t& J/ D

0 ^% o7 b4 u2 z7 t+ q" v在OTFDEC的解密密钥设置好了之后,我们在openssl中使用的密钥要以字节为单位,在16个字节的范围内,头尾交换一下。但是注意,字节里面的bit顺序不变,也就是每个字节的值不变,只是换了新的位置。这就是为什么我前面故意把OTFDEC的密钥中,16个字节的内容每个字节值都不一样,就是为了方便比对每个字节的移动位置。
- m/ ]' ~& j0 y. a) m6 W0 w7 R1 q$ a4 g" ^/ r5 Y
8 U% k4 u, k  v( |) C5 b) |
为什么要这样调换,这是因为OTFDEC电路设计造成的,我们没有必要去追究原因,知道在这样的设计下,我们该怎么做就可以了。
3 L5 |4 A9 E1 C9 j% I; t: l/ e' D/ V# C" C* j- e! I9 G9 K' R

2 z# L9 {4 e9 V7 V9 \大家注意胶片里贴出来的openssl的命令,-K字符后跟着就是密钥,这是以字节为单位的字节串。也就是说第一个字节是0x9A,接着的字节分别是 0xBC, 0xDE,和胶片中下面的表格中字节顺序排列一样的。! M% n+ u& q1 g- e; K
11.png 6 Q8 g& P% v. M" n5 `: N
然后来看IV。
1 R' Y/ Z* J" r: ?- |0 d
0 E6 u9 y% V0 D9 ^* P! h

6 v7 q' u. N7 [# vOTFDEC的IV,我们在代码中,给HAL驱动封装出来的OTFDEC_RegionConfig结构体每个成员赋值好了之后。这个IV在使用openssl的时候,又需要做怎样的调序呢?如图所示:第一个32位的字,来自Nounce[1]。这个4字节组成的32位字里面,字节顺序也是依次头尾交换了一下。第二个32位字,来自Nounce[0],字节调位顺序也是一样。第三个字的高2位字节来自Version,字节调位顺序和前面一样。第四个32位字来自起始地址的移位和regionID的拼接。$ ~. E0 J$ m, g9 P/ C6 ?' i' n
1 e: T/ n1 U5 G5 I; [5 B  ~

/ B& j. S+ I  _大家注意胶片里贴出来的openssl的命令,-iv字符后跟着就是初始向量,这也是以字节为单位的字节串。也就是说第一个字节是0x13,接着的字节分别是 0x57, 0x9B,和胶片中下面的表格中字节顺序排列一样的。
% P2 r- Y! Z8 x. O( ] 12.png . u. P. p4 I3 N
- t8 k# l# F" z$ r9 m; Z
9 w7 a  Z& h6 A  t" r9 s2 z
openssl命令的密钥和IV输入的内容确定了,还有一件很重要的需要调整的事情:OTFDEC将要解密的对象。* I- k7 R" [: x

  u/ ]4 L) b0 d9 E+ g
8 L4 `% L9 O8 A+ o2 }
它并不是直接的把明文代码Project.bin,使用openssl按照前面的参数加密就好了。仍然是由于不同AES运算工具对字节排序的不同,需要做手动调整。这里我们使用PC端的脚本工具,srec_cat先做输入字节流的填充,然后使用xxd工具,对字节顺序做调整。调整的规则和前面的密钥是一样的,即,对每16字节的内容:在16个字节的范围内,头尾交换一下,字节里面的bit顺序不变,也就是每个字节的值不变,只是换了新的位置。经过调序后的字节流再送到openssl做加密,密文同样还要经过一次相同规则的字节调序,才得到最终可以烧写到片外Flash(0x9000 0000),由OTFDEC做实时解密的加密代码。
* |8 Z! h( k0 a3 K9 G. o/ H 13.png
# w: R+ {1 |$ r$ Z# d
2 h" g4 }' o* d% v/ g$ y$ S

* v* M3 X* b( `打开cmd命令窗口,切换到在这个文档配套的参考例程包里的Utilities/ExtTools目录下,依次输入前一页胶片里的命令,得到预处理阶段的最后输出,即Project_pad_pre_enc_post.bin。! x1 s1 }! R4 u; H$ N
14.png
* f; e' t  f, O9 p; c 15.png 2 ^2 D5 g" p% u& R) N# m

3 R4 j: M! W, }) _+ d. P' V
7 h: \6 x2 W2 W8 [
我们可以使用STM32CubeProgramer来验证OTDEC配置好了之后,从0x9000 0000的地方看到的就是明文代码的样子。8 c$ c+ N: ?3 N: R* ^

* w: z) x6 z: N- J
) M5 N" g" ]! D. I6 N
验证步骤请参照胶片中的指示。7 ], R1 o$ ^6 \8 D" R5 E
16.png ( K# C9 U/ v- Q- t+ X- |& e
4 G9 s" e# f% {" Y% e
接下来我们让板子脱机运行,把场景3运行起来。从板载的LCD屏幕可以看到自测程序完成后,打印出来的时间花销。+ ^/ C$ [" g5 P) P# c2 |0 B) f4 Y; \

/ ^( N8 d7 u/ Y4 k
) ?% ]$ Q! A8 I  a+ H
根据我复位的时候是否按下用户按键,可以展现使能Cache和不使能Cache的效果。
$ S( X2 n2 F  R/ D/ E9 O8 z2 U3 C) y! I( ]2 e3 R

( E' A7 y: O0 [7 {0 F  Q% L从total time cost这一行可以看出,不是能Cache,执行时间要8秒;而使能了Cache,执行时间只要0.2秒。8 [$ C6 C; U7 v% q  @" G6 p8 a) K( C- M0 O
17.png 6 b) e+ a+ d/ v' R$ L3 [$ I# V

4 Q8 W& ~: S8 X6 s+ T8 d3 N
- I, z% I- o. A  m0 v2 b2 @( V
我们再把场景1和场景2下,启动工程和自测工程下载到板子上分别运行,再记录各自的时间花销。
9 e8 h/ y7 H2 l5 C4 h8 h. P7 @( [$ A
+ ?/ N3 ]+ V+ w+ N+ E4 ?: Q
图中红色数字是未开Cache的情况,绿色数字是开启Cache的情况。9 M6 C/ M0 z2 E4 v1 T7 v' v* s
- d6 z- s" l# Q+ m9 V  [
) o& ]. ?$ U; U9 [. u2 u9 {
结论
2 P1 [# h1 H8 i可以得出结论:代码运行在外部Flash的时候,运行明文和使用OTFDEC运行密文,效率相差无几;要提高代码运行在外部Flash的效率,主要加速措施是使能内核自动的Cache。
+ C5 y& P/ r8 V/ s7 C) E
$ }& ]7 r- d5 l* f+ b; n8 l
3.png
赞 收藏 评论0 发布时间:2021-7-14 13:49

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版