一个 能抵抗侧信道攻击的SM4白盒实现
本文档主要介绍新设计的SM4白盒实现,包括设计原理、代码实现的详细流程、安全性分析以及与现有SM4白盒实现的比较。该SM4白盒实现在满足一定安全性(抵抗侧信道攻击)的前提下,具有较低的存储复杂度和较高的加密速度。
1 设计原理
根据前期调研,我们选择了非线性的字节置换编码来保护白盒实现内部查找表的输入和输出,并且在SM4的S盒变换和线性变换之后添加混淆矩阵以混淆SM4的线性变换,并使T表的输出更均匀。由于选择非线性置换,因此查找表输出的异或操作需要使用XOR表,单个XOR表输入16bit、输出8bit,需存储空间64KB,为节约白盒实现查找表的存储空间,我们采用XOR表的复用策略以降低存储。下面我们以加密函数为例解释设计原理(解密函数类似)。
1.1 T表与I型XOR表
SM4标准算法轮函数的核心步骤包括了32bit的非线性变换(4个并列的8bit S盒)以及32bit的循环移位变换。白盒实现的核心就是将这两个函数复合成查找表称为T表,如下图所示:
图1 SM4白盒实现的T表示意图
其中,我们将32bit的线性移位变换L拆分成4部分,分别用32×8的矩阵表示。LB表示我们设置的混淆矩阵。在生成T表之前需要将128bit的用户密钥通过SM4的密钥扩展算法生成各轮所需的32bit轮密钥,因此T表是依赖密钥的,所以32轮轮函数需要128张T表,单一T表(8bit输入、32bit输出)需存储空间1KB,全部T表需128KB。图1中的d-NB和e-NB分别表示查找表的输入编码和输出编码(后文都这样表示),其中d-NB不重复利用。 由于我们将线性变换L表示成矩阵形式并进行了拆分,所以T表只实现了一部分矩阵运算,后续还需要进行异或运算,因此需要XOR表。XOR表的输入长度是2字节,输出是1字节,每个输入输出字节都需要编码,因此,XOR表之间是否相同是由这三个非线性编码决定的。一个XOR表的存储大小是一个T表的64倍,因此为了节约空间,我们需要考虑XOR表的重复利用。XOR表不包含关键信息(如密钥),而且其输入输出都有非线性编码保护(编码也难以被恢复),因此重复利用异或表并不会导致关键信息的泄露从而降低安全性。重复利用XOR表,在于编码的重复利用,例如T表的输出编码与其对应的XOR表的输入编码每轮都固定,那么每轮T表对应的XOR表就可以重复利用。图2展示了一类XOR表:
图2 I型XOR表
I型XOR表由2层异或表构成,第一层的8个XOR表将4个T表的输出异或为2个32bit字,再经过第二层的4个XOR表得到1个32bit字,从而实现了SM4的L变换。观察图1和图2不难发现编码的颜色和标注都是对应的,对应位置的编码只需要生成一次即可,不需要每轮都生成新的编码,字母A、B、C等表示编码所在位置也表示编码的型号,例如B代表的编码位于T表的输出端和I型异或表第一层的输入,这对编码相互 抵消来保证加密函数的功能性。既然可以重复利用编码,那为什么同一类型的编码不重复利用?比如B和C类型编码只生成一次,这样第一层XOR表只需要1个,还能再压缩空间。回答这个问题,需要考虑白盒实现的安全性,由于我们采用混淆矩阵是精心设计的,这样T表输出的每个字节都会与S盒对应且输出的每个字节都是均匀的(遍历输入会得到256种取值,对基于Walsh变换的谱分析[3]和DCA[4]等侧信道攻击有较高防御能力),若只采用一个编码,攻击者可以利用频率分析攻击每一轮的T表。而且XOR表的输入端的两个编码不能相同,若相同的话攻击者容易得到关于0的映射信息。在这种设计原则下,I型XOR表需存储空间为12×64KB=768KB。
1.2 混淆矩阵逆变换查找表与II型XOR查找表
我们在T表中添加了混淆矩阵LB,使T表的输出更均匀且提高了白盒实现抵御DCA和谱分析的能力。为了保证算法的标准性,我们需要将LB矩阵抵消掉。由于是矩阵运算,因此我们按照和构造T表一样的方式,将LB的逆矩阵进行拆分,并通过XOR表实现完整的矩阵乘法运算。如下图所示:
图3 混淆矩阵逆变换查找表
在这里,我们需要强调,混淆矩阵是秘密生成的,每一轮的混淆矩阵可以相同也可以不同,这并不会对安全性产生影响,我们的方案选择固定的LB矩阵,因此32轮的查找表大小为4KB,若每轮选择新鲜的LB矩阵,那么查找表大小为128KB。图4是II型XOR表:
图4 II型XOR表
II型XOR表的构造原则与I型相同,需要注意的是,无论是否重复利用混淆矩阵LB都不会影响II型XOR表的生成与存储,因为XOR表只与编码有关。I型XOR与II型XOR表在输出位置都保持了编码的新鲜性,使S盒对应位置的输出都是均匀的,保障了各个位置抵抗DCA和谱分析的能力。II型XOR表需存储空间12×64KB=768KB。
1.3 混淆矩阵的选择
混淆矩阵LB是32×32的矩阵,表示为:
其中表示8×8的矩阵,为使S盒对应位置的输出都是均匀的,要求LB除了可逆还需满足三个条件: (1)满足T表输出的均匀性:
T0:
T1:
T2:
T3:
需要保证上述16个方程对应的16个8×8的矩阵都是满秩的,其中H表示SM4线性变换矩阵L的分块矩阵; (2)I型XOR表第一层输出的均匀性: T0+T1:
T2+T3:
需要保证上述8个8*8的矩阵都是满秩的; (3)I型XOR表第二层输出的均匀性: T0+T1+T2+T3:
需要保证上述4个8×8的矩阵都是满秩的。这样精心设计挑选的混淆矩阵能为T表提供抵御侧信道攻击更高的安全性。
1.4 轮函数的输入阶段和输出阶段
SM4算法每一轮都会生成一个新的32bit字,它是由当前轮的一个输入32bit字和SM4的F函数生成的32bit字异或得到的,如图5所示,因此轮函数的最后阶段也是由异或表实现的。需要注意的是,这类型异或表的输出编码必须保证每一轮的新鲜性,以抵御代数攻击。
图5 SM4分组密码算法加密流程示意图
进入F函数的32bit字是由当前轮的3个输入32bit字异或得到的,因此也需要XOR表来实现。首先,我们先构造输入阶段的查找表,如图6所示:
图6 输入查找表
其中,输入IN是根据前一轮的输出编码OUT生成的(除了前四轮中32bit字对应的输入编码是另外生成),因此,为了复用异或表,输入阶段应该再额外添加一层编码,即H编码,这样输入阶段的3个32bit字的异或就可以用8个异或表表示,其中第一层异或表可以进行复用,第二层异或表的输入编码可以复用,但是要保证输出编码A每轮新鲜,如图7所示:
图7 III型XOR表
这样,输入查找表每轮都需要重新生成(IN新鲜,H型编码复用)共需存储空间32×4KB=128KB,III型异或表,共需存储空间4×64KB+4×64KB×32=8MB+256KB。最后还剩下轮函数的输出阶段,如图8所示:
图8 输出XOR表
其中,由于H编码和G编码都可以复用,但是输出编码OUT必须保证每一轮新鲜,因此输出异或表不能复用,需存储空间32×4×64KB=8MB。 综上,全部SM4白盒实现的查找表共需存储空间为:T表(128KB)+I型异或表(768KB)+LB逆变换查找表(4KB)+II型异或表(768KB)+III型异或表(8MB256KB)+输入查找表(128KB)+输出异或表(8MB)共计18MB4KB。注意,这个存储空间是根据本文设计方案所需的最小空间,如果不进行表或者编码亦或混淆矩阵的重复利用,或者每隔几轮再重复利用,那么所需存储空间还会增多。
2 代码实现的详细流程
SM4白盒实现的代码包括查找表的生成和加密算法的实现两部分。代码由C语言编写,矩阵运算库为WBMatrix,代码主要参考了Xiao-Lai白盒SM4实现[1]和Bai-Wu白盒SM4实现[2]和SM4的实现。本文对应的代码实现链接为WBSM4-ResistDCA。
2.1 查找表的生成
构成查找表的元素包括8bit的非线性置换编码、混淆矩阵、轮密钥和S盒.
(1)8bit非线性置换编码的生成与XOR表的构造
采用随机置换的方式生成,但是需要加个约束条件:不能将0映射到0,限制敌手对于输出位置为0的判断。代码封装成函数:
void Gen_BytePer(uint8_t *permutation, uint8_t *inverse)
由于各类查找表的输入输出都由非线性编码进行保 护,因此在生成查找表前将所有非线性编码全部生成,需要注意输入查找表的IN编码和输出查找表的OUT编码与输入输出的32bit字的对应关系,并且同时生成对明文的进行编码和对密文进行解码的外部编码,对明文进行编码的外部编码作用于服务端,对密文进行解码的外部编码作用于客户端。由于XOR表只与输入和输出编码有关,因此在生成所有编码后就可以构造XOR表了,需要注意表与最终加密算法计算流程以及编码顺序的对应。
(2)轮密钥的生成
参考SM4的实现,封装成函数:
void sm4_setkey_enc(sm4_context *ctx, unsigned char *key)
(3)混淆矩阵的生成
根据1.3节的约束,对随机生成的32bit矩阵进行筛选,封装成函数:
void Gen_LB(M32 *LB, M32 *LB_inv)
(4)构造T表和混淆矩阵逆变换查找表的构造
根据图1和图3,按照顺序组合各种元素,需注意编码的选择,要与前序编码和后序编码对应。矩阵按照列来切割,需要注意主体32bit矩阵与分块8bit矩阵位置的对应关系。根据国家标准《GB/T 32907-2016》,SM4的解密函数与加密函数仅有的区别就是轮密钥的顺序,即解密函数的轮密钥是加密函数轮密钥的逆序,因此生成解密函数的T表只跟加密函数的T表的轮密钥顺序相反,其余各部分均相同。
将所有生成查找表和外部编码的代码封装为函数:
void wbsm4_gen(uint8_t *key,unsigned char *whitebox,size_t *whitebox_len)
其中参数key为传入的16字节原始密钥(用户密钥),指针参数whitebox是要返回的白盒查找表与外部编码的地址,whitebox_len是返回的白盒查找表与外部编码的大小,在调用wbsm4_gen函数时,函数会做指针参数whitebox是否为空的判断,若指向空指针则先返回whitebox_len,并不会进行生成白盒的操作,用户拿到白盒大小的参数whitebox_len后,可根据白盒大小进行内存分配,之后再调用wbsm4_gen函数才会生成白盒。
2.2 加/解密函数的实现
SM4分组密码的分组长度是128bit,共32轮,每轮生成一个32bit字并与当前轮输入的3个32bit字一起进入下一轮,因此加密轮函数的代码主要维护一个长度为16字节的队列。由于SM4的解密函数与加密函数仅有的区别就是轮密钥的顺序,即解密函数的轮密钥是加密函数轮密钥的逆序,对应到查找表中就只有T表的不同,因此我们选择将加解密操作封装成函数:
void wbsm4_crypt(uint8_t input[16], uint8_t output[16] , unsigned char *whitebox, int mode)
参数whitebox指向的是由wbsm4_gen提前生成的白盒查找表,参数mode判断是加密操作还是解密操作。由于加密算法主要就是查表操作,因此在代码实现时需要注意查找表的顺序,还需注意SM4加密算法最后是逆序变换。最终加密函数
void wbsm4_encrypt(uint8_t input[16], uint8_t output[16],unsigned char *whitebox)
和解密函数
void wbsm4_decrypt(uint8_t input[16], uint8_t output[16],unsigned char *whitebox)
就是在调用wbsm4_crypt函数。
3 安全性分析
方案的安全性分析包括侧信道场景的安全性分析和白盒场景的安全性分析以及白盒多样性与白盒含混度。
3.1侧信道分析
综合现有的侧信道安全性分析,如谱分析[3]ADCA[4]选择对T表进行攻击,我们可以将T表抽象成一个8->8的映射: