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

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

[复制链接]
STMCU-管管 发布时间:2021-7-5 10:57

# @$ k; x, I) W3 ?前言
5 \" R4 g4 K  r( T, e7 x0 S; ?从STM32H73x系列开始,我们引入了一个新外设模块,OTFDEC。它的全名叫做on the fly decryption。它的引入,可以帮助大家解决代码保护的痛点。! f/ ^5 ?2 u8 @2 v: B* Z
! e  l8 s" v  T/ u: R/ b8 a; \* u, I( I
OTFDEC简介9 k8 t6 a2 {  H8 R. v5 T& J( U, h
5 J( v8 p9 w3 P& a

, ^! o0 L; W/ |- Y+ c+ z& }大家都知道,代码存储在片内Flash,只要做好了JTAG调试端口的保护和片上关键代码的隔离,在防止逻辑攻击和直接探测层面,还是相当安全的。但是片上Flash毕竟容量有限,在一些应用中我们需要把代码放到片外Flash存储甚至直接从片外Flash执行。片外Flash相比片内Flash,在抗攻击方面就脆弱得多。片外Flash一般没有什么硬件层面的保护,只要知道了它的料号,它的读写时序都是可以查到的,那么读出来里面的内容就不是什么难事。
9 d3 R) u( W9 r2 M( l- F
3 Z: N3 N7 y. n
7 Y  r' e1 L1 z- T
所以大家一个自然的想法就是把代码加密后再放到片外Flash上,这样即使别人读出里面的密文代码,只要没有密钥,也无法获知代码的有效信息。+ j1 z" A# z0 X: f! D
11.png
就比如胶片中这样的典型拓扑结构:加密代码放在外部的Octo-SPI Flash中。
) r! {  t1 |1 `1 \' V% _  L. [
/ G* K9 S: G  J8 F
: a' c/ x0 H( x, @- R, t* v
对这种自然的做法,以往的MCU在执行片外加密代码时,需要先调用OSPI驱动,把密文代码读进来,比如放到SRAM中。然后使用MCU的软件或者硬件解密,把代码明文恢复到SRAM的另一个区域。最后MCU再从这块SRAM执行明文代码。
+ b, _( K% n, y9 I$ g) Q& g
# E/ F2 `7 y& ^$ F7 s! h
- f( N3 \3 X4 I, }" x4 s# _$ h
现在我们引入了OTFDEC这个硬件模块,它位于总线矩阵和Octo-SPI接口之间。把它配置好之后,内核执行片外Flash上的密文代码(在这里Octo-SPI Flash的映射地址是0x9000 0000开始),无需中间再用SRAM倒一次手,而是在OTFDEC的作用下,直接把解密后的代码送到总线矩阵上供内核执行了。也就是说,有了OTFDEC的配合,对于CPU来说,执行外部Flash上的加密代码,就和执行片上Flash的明文代码是一样的。
  A' P- q0 v$ X2 F
12.png
为了尽量减少OTFDEC解密造成的延迟,OTFDEC被设计工作在AES-128-CTR模式下。不使用AES的链表模式,就是为了尽量缩短对目标地址上密文解密的时间。因此存储在外部Octo-SPI Flash上的加密代码也需要使用同样的AES-128-CTR运算得到。7 F. `2 ], G" X- t, H, e: `

* t: Y+ ?/ x( N( h; @% [2 ?

- M+ u" }7 [. n  a! a. a有一点需要注意的是:为了达到这样的使用效果,Octo-SPI需要配置到memory map模式。
) Y6 m1 ~4 k& y7 D+ c
+ j  D/ G/ y, l# p/ s2 R* ]目前,STM32系列家族中,集成了这个OTFDEC模块的有STM32H73x系列,STM32L56x系列,和STM32U585系列。
2 t, z8 W- R$ W( e6 [
13.png
今天我们不是介绍OTFDEC怎么使用,而是回答前段时间在给客户介绍OTFDEC的时候,大家一个比较共同的问题:相对于直接执行外部Flash上的明文代码,执行外部Flash的加密代码,OTFDEC解密操作引入的延迟有多少?
8 o4 F- p8 d1 w7 U7 Q8 e+ _) M0 X
实验设计
9 e5 c+ y/ e  w. p8 w) \! X- d
14.png
我们接下来设计一个实验,验证在OTFDEC参与下,内核执行外部Flash上的密文代码效率到底如何,用数据说话。
1 G; a" R0 m7 {9 O' ^# u3 L4 d  X! U  N. m) Z
我找了mbedTLS中一个自测程序Crypto_SelfTest,验证一下把它加密后放在外部Flash,内核执行完整套自测程序需要的时间花销,和执行外部明文代码的差异。为了进一步说明问题,还加了一个场景,就是这个自测程序明文放在片内Flash,内核执行它的花销会快多少。; T+ N" N$ F( a0 a

" I2 z$ F! q4 E- W# |6 E这个Crypto自测程序经过最高优化等级编译后,大小差不多在63K作用的样子。
# I8 c% @+ s; F6 F
15.png
第一个场景就是最普通的,直接把测试程序灌到片上Flash运行。- A. `+ t5 ?3 A$ g5 v
8 Z7 b8 H1 b( \8 }# t" v8 y* T

' v4 V0 u2 H2 l& ?/ Q我们先来看一下这个自测程序,主要就是执行selftests这个函数数组里的自测程序。用户可以在mebdtls_conf.h头文件中去选择哪些自测子项被包含进去。现在我选择了6个自测子项。
! S( S2 U3 k2 O& l4 J
- p! e# K! C& N6 m9 ~0 l6 u* p

. V, b% w( b& V4 n然后在自测程序开始运行之前,通过检测是否有用户按键按下,来决定是否开启Cache。STM32H735集成ARM Cortex-M7内核,自带32K指令Cache和32K数据Cache。
9 b2 T. m4 U1 a$ `+ ?# I  V
16.png
因为要测量运行这给自测程序的时间花销,因此我们使能一个内核计数器,然后在每个测试子项的开始复位该计数器,在测试子项结束后把当前计数器的值,记录到全局变量的时间戳数组中。最后在6个测试子项都完成后,根据时间戳数组里记录的值,和当前内核运行频率,转换成时间花销。" ^" J1 r7 F! ^, B5 d. {: H- v+ r

, u& d, D8 \- V2 ?3 g由于场景1,是最普通的用法,即程序运行在片上Flash,因此它的链接文件就是STM32Cube包中的缺省配置。我这里以IAR为例,展示了这个测试场景下,code的存放地址,包括复位和中断向量表的存放地址。9 w; n$ O$ D8 @' }5 }5 ~
17.png
第二个场景,自测程序运行在外部Flash。而STM32是不能从外部Flash启动的,我们按照常规的做法,从片上Flash首地址启动,因此在片上Flash我们放一个Bootloader。它的功能很简单,就是初始化OSPI接口,并把它配置到memory-map模式。然后调整堆栈指针SP,以及PC指针,跳到0x9000 0000开始的OSPI 外部Flash首地址运行。而那里,则是我的Crypto自测程序。
) J' F2 ~  t. |8 @9 I4 o/ b  t8 p9 ], k0 P

! W0 m$ q! F8 @* J8 o8 ]+ b在场景2的自测程序工程Crypto_Selftest_ext_plain中,和之前的工程相比,只需要稍微做两处修改。链接文件,把复位和中断向量表放到0x9000 0000的地方,并且调整内核寄存器的VTOR值。这样子,一旦有任何中断或者异常,都是去位于0x9000 0000处的向量表取执行地址。
, U* N9 [2 j* V! h; k3 c3 k3 K
18.png
第三个测试场景,boot loader工程相比第二个测试场景中,需要增加对OTFDEC的配置。而烧录在0x9000 0000的内容,应该是从场景2下第二个工程生成的project.bin,加密后的密文。这里,左边的Bootloader里是OTFDEC在解密,右边是通过PC端工具预先把代码做加密。
" T- G$ C2 f  B% k3 g0 [. j; _  s& D  F* s( K+ o
由于是AES是对称加解密算法,因此OTFDEC的加密参数配置,要和PC端加密工具的参数一致。. w/ u: }7 d- w0 i$ Y) I3 `
19.png
我们先来设置OTFDEC的解密参数,密钥key和初始向量IV。$ r( B$ D! U8 @8 F
* `) [9 F( U  a0 Q) U
% Z9 z! C* @: c' H
密钥由用户自己指定,在代码里我们设置在Key数组中。按照数组的写法,考虑到ARM Cortex-M内核是小段对齐,因此这16字节的密钥,在memory中的存储顺序,应该如左下图所示。注意,我这里刻意让16字节的密钥中,每个字节的内容都不一样。为什么?我们接下来看。) \2 A, c" _. N/ @: `' Q" F% O& b1 \
; w% t+ w; l: p7 W9 b- o

; U& T5 C1 g1 O9 R9 f3 ZOTFDEC的IV,HAL驱动封装了一个结构体给用户来填写。由Nounce,OTFDEC将要作用的外部Flash地址范围,以及将要存放在外部Flash那个地址范围里代码的版本号。Nounce,也是由用户自己设定,我这里仍然刻意让8个字节的内容都不相同。
' a8 C3 ^) t- T/ J  ?# K
20.png
接下来我们要配置PC端加密工具的参数了。这里我们使用openssl。7 g1 e- j, Q' Q. T; H1 k
! @  K% Z2 }; O( v4 t2 r  p) i. [) a
在OTFDEC的解密密钥设置好了之后,我们在openssl中使用的密钥要以字节为单位,在16个字节的范围内,头尾交换一下。但是注意,字节里面的bit顺序不变,也就是每个字节的值不变,只是换了新的位置。这就是为什么我前面故意把OTFDEC的密钥中,16个字节的内容每个字节值都不一样,就是为了方便比对每个字节的移动位置。7 g- U5 m0 ?- a

5 {/ f" M) _6 F" h为什么要这样调换,这是因为OTFDEC电路设计造成的,我们没有必要去追究原因,知道在这样的设计下,我们该怎么做就可以了。
- I2 K3 v; Z" @; n" Y2 a* G# t; v; S6 O" C" G' L; i* j, T
4 r% k- D# @' A+ Z' U& @
大家注意胶片里贴出来的openssl的命令,-K字符后跟着就是密钥,这是以字节为单位的字节串。也就是说第一个字节是0x9A,接着的字节分别是 0xBC, 0xDE,和胶片中下面的表格中字节顺序排列一样的。
, X. b" V0 m) H2 A) a% O
21.png
然后来看IV。
. J  ?! O/ `; N
& A' C1 q& ^5 X4 A) X

8 @' O  V9 t3 h( @; a1 fOTFDEC的IV,我们在代码中,给HAL驱动封装出来的OTFDEC_RegionConfig结构体每个成员赋值好了之后。这个IV在使用openssl的时候,又需要做怎样的调序呢?如图所示:第一个32位的字,来自Nounce[1]。这个4字节组成的32位字里面,字节顺序也是依次头尾交换了一下。第二个32位字,来自Nounce[0],字节调位顺序也是一样。第三个字的高2位字节来自Version,字节调位顺序和前面一样。第四个32位字来自起始地址的移位和regionID的拼接。9 J1 [, X6 u# ^% \7 D

% Y. q) `* ~% N6 P! K( x" z( o

2 g& o- s8 b. Q3 B大家注意胶片里贴出来的openssl的命令,-iv字符后跟着就是初始向量,这也是以字节为单位的字节串。也就是说第一个字节是0x13,接着的字节分别是 0x57, 0x9B,和胶片中下面的表格中字节顺序排列一样的。
$ _5 u5 u4 G6 \* F; C% K
22.png
openssl命令的密钥和IV输入的内容确定了,还有一件很重要的需要调整的事情:OTFDEC将要解密的对象。
: a) @5 _; R5 u8 E" W# O* w5 P" j0 q$ l- R5 k

1 k( A0 O' o5 _* {: _4 I! F它并不是直接的把明文代码Project.bin,使用openssl按照前面的参数加密就好了。仍然是由于不同AES运算工具对字节排序的不同,需要做手动调整。这里我们使用PC端的脚本工具,srec_cat先做输入字节流的填充,然后使用xxd工具,对字节顺序做调整。调整的规则和前面的密钥是一样的,即,对每16字节的内容:在16个字节的范围内,头尾交换一下,字节里面的bit顺序不变,也就是每个字节的值不变,只是换了新的位置。经过调序后的字节流再送到openssl做加密,密文同样还要经过一次相同规则的字节调序,才得到最终可以烧写到片外Flash(0x9000 0000),由OTFDEC做实时解密的加密代码。
( C; Q( V( R6 O4 _
23.png
打开cmd命令窗口,切换到在这个文档配套的参考例程包里的Utilities/ExtTools目录下,依次输入前一页胶片里的命令,得到预处理阶段的最后输出,即Project_pad_pre_enc_post.bin。
, j. N# x& K! W9 ]* [* {/ o! i
24.png
25.png
我们可以使用STM32CubeProgramer来验证OTDEC配置好了之后,从0x9000 0000的地方看到的就是明文代码的样子。
0 x( m( N/ i9 j. z* _: L5 m* C) J7 G0 [

& \# u5 ?) d& A验证步骤请参照胶片中的指示。6 E' a5 w2 K! H  [
26.png
接下来我们让板子脱机运行,把场景3运行起来。从板载的LCD屏幕可以看到自测程序完成后,打印出来的时间花销。9 |8 @! V3 ?) n2 \2 G

  t1 C( c* Q, e4 w6 |

" C& O9 [0 P1 [5 K  \' {根据我复位的时候是否按下用户按键,可以展现使能Cache和不使能Cache的效果。
$ n+ h! D9 J1 Q% s/ Z" T
* [) m7 n/ n/ y3 z% B/ @: Z, a

' \2 L  h* K' H- c% r从total time cost这一行可以看出,不是能Cache,执行时间要8秒;而使能了Cache,执行时间只要0.2秒。( h6 A5 X1 Y+ T, A( g& m
27.png
我们再把场景1和场景2下,启动工程和自测工程下载到板子上分别运行,再记录各自的时间花销。
& c5 p5 W$ E/ K8 l5 c8 ?) ?. w. ^
! T$ _: \: z- P: P8 o3 B( |

; E8 b9 T7 T9 b; M5 j$ o图中红色数字是未开Cache的情况,绿色数字是开启Cache的情况。
5 Q3 o/ L+ e9 t' x6 v% y; D8 F  d- e/ S/ J
结论
5 Z( E) d+ R, d5 S6 V- v7 v7 {可以得出结论:代码运行在外部Flash的时候,运行明文和使用OTFDEC运行密文,效率相差无几;要提高代码运行在外部Flash的效率,主要加速措施是使能内核自动的Cache。
$ G) U/ U4 [: u: ?9 Z. S5 m  ^) `

1 x; d. G% D0 N! Z/ ^
赞 收藏 评论1 发布时间:2021-7-5 10:57

举报

1个回答
ghost110 回答时间:2021-7-5 15:17:47
很棒,终于能够有效保护自己的代码和脑力活动成果了!8 i6 q( g4 ^! v8 R0 n) ]) Y7 s

所属标签

相似技术帖

官网相关资源

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