4、SM4算法在安全世界的实现:SM4加解密核心逻辑、密钥扩展算法、CBC/ECB模式实现与测试

SM4算法,说白了就是咱们国家自己的分组密码标准。我在TrustZone项目里用过很多次,它跟AES有点像,但结构上更简洁。今天咱们就把它的核心逻辑、密钥扩展、还有CBC/ECB模式,一个一个掰开揉碎了讲清楚。

4.1 SM4算法概述

SM4是一个分组密码算法,分组长度128位,密钥长度也是128位。它采用32轮非线性迭代结构。嗯,这里要注意,它不是Feistel结构,而是类似AES的SPN结构,但轮函数更轻量。

核心参数速查表

参数值说明
分组长度128 bit (16字节)输入输出都是16字节
密钥长度128 bit (16字节)加密密钥和解密密钥相同
轮数32轮每轮使用一个轮密钥
轮密钥长度32 bit (4字节)共32个轮密钥
基本运算异或、S盒、循环移位硬件实现友好

我个人习惯把SM4看作一个「轻量级AES」。为什么这么说?因为它没有AES那么复杂的列混合和行移位,轮函数里主要就是异或、查S盒、再加一个线性变换。在TrustZone的安全世界里,这种简洁性意味着更少的侧信道攻击面。

4.2 SM4加解密核心逻辑

SM4的加密和解密,结构完全一样,只是轮密钥的使用顺序相反。加密时用rk0, rk1, ..., rk31,解密时用rk31, rk30, ..., rk0。这一点跟AES的加解密对称性很像。

咱们先看加密流程。输入128位明文,分成4个32位字:X0, X1, X2, X3。然后执行32轮迭代,每轮更新一个32位字。

第i轮的公式很简单:

X_{i+4} = X_i ⊕ T(X_{i+1} ⊕ X_{i+2} ⊕ X_{i+3} ⊕ rk_i)

这里的T函数是SM4的核心,它由两部分组成:非线性变换τ和线性变换L。

  • τ变换:把32位输入分成4个字节,每个字节查S盒。SM4的S盒是固定的8位输入8位输出,跟AES的S盒不同,但设计原则类似。
  • L变换:对τ的输出做循环移位和异或。公式是:L(B) = B ⊕ (B <<< 2) ⊕ (B <<< 10) ⊕ (B <<< 18) ⊕ (B <<< 24)

32轮结束后,把X32, X33, X34, X35拼接起来,就是密文。

我的经验:在TrustZone安全世界里实现SM4时,我建议把S盒放在安全内存里,不要放在普通DRAM。我曾经见过一个项目,S盒被侧信道攻击通过缓存时间泄露了,嗯,那之后我就把S盒锁在TZ的SRAM里了。

4.3 密钥扩展算法

密钥扩展,说白了就是把16字节的原始密钥,变成32个32位的轮密钥。SM4的密钥扩展算法跟加密轮函数很像,但用了不同的常数。

流程是这样的:

  1. 把128位密钥分成4个32位字:MK0, MK1, MK2, MK3
  2. 用系统参数FK做初始异或:K0 = MK0 ⊕ FK0, K1 = MK1 ⊕ FK1, K2 = MK2 ⊕ FK2, K3 = MK3 ⊕ FK3
  3. 然后迭代生成轮密钥:rk_i = K_{i+4} = K_i ⊕ T'(K_{i+1} ⊕ K_{i+2} ⊕ K_{i+3} ⊕ CK_i)

这里的T'函数跟加密的T函数类似,但L变换不同:L'(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)

FK和CK都是固定常数。FK有4个,CK有32个。这些常数在标准文档里都有,我就不列了,你写代码时直接查表就行。

避坑指南:我曾经在移植SM4时,把密钥扩展的CK常数搞错了一个字节,结果加解密结果对不上。调试了整整一天才发现。建议你实现完后,先用标准测试向量验证一下密钥扩展的正确性。

4.4 CBC/ECB模式实现

SM4本身只定义了分组加密,怎么处理长数据呢?这就需要工作模式了。最常用的就是ECB和CBC。

ECB模式

ECB模式最简单:把明文分成16字节一块,每块独立用SM4加密。但我不推荐你在生产环境用ECB。为什么?因为相同的明文块会生成相同的密文块,容易泄露数据模式。我在一个金融项目里见过用ECB加密头像的,结果头像轮廓在密文里还能看出来,嗯,那画面太美我不敢看。

CBC模式

CBC模式引入了IV(初始向量),每个明文块先跟上一个密文块异或,再加密。这样相同的明文块会生成不同的密文块。

CBC加密流程:

C0 = IV
Ci = SM4_Encrypt(Pi ⊕ C_{i-1})

CBC解密流程:

Pi = SM4_Decrypt(Ci) ⊕ C_{i-1}

模式对比

特性ECBCBC
并行加密支持不支持
并行解密支持支持
错误传播仅影响当前块影响当前块和下一块
数据模式泄露有无
IV需求不需要需要,且必须随机
推荐场景测试向量验证生产环境数据加密

4.5 代码实现示例

下面我给出SM4核心加解密和CBC模式的C语言实现片段。注意,这是简化版,去掉了内存管理和边界检查,但核心逻辑是完整的。

// SM4 S盒
static const uint8_t sm4_sbox[256] = {
    0xD6,0x90,0xE9,0xFE,0xCC,0xE1,0x3D,0xB7,0x16,0xB6,0x14,0xC2,0x28,0xFB,0x2C,0x05,
    // ... 完整S盒请参考标准文档
};

// 轮函数T
static uint32_t sm4_t(uint32_t x) {
    uint8_t b[4];
    b[0] = sm4_sbox[(x >> 24) & 0xFF];
    b[1] = sm4_sbox[(x >> 16) & 0xFF];
    b[2] = sm4_sbox[(x >> 8) & 0xFF];
    b[3] = sm4_sbox[x & 0xFF];
    uint32_t y = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
    // 线性变换L
    return y ^ rotl(y, 2) ^ rotl(y, 10) ^ rotl(y, 18) ^ rotl(y, 24);
}

// 加密一个分组
void sm4_encrypt_block(uint32_t rk[32], uint8_t in[16], uint8_t out[16]) {
    uint32_t x[36];
    for (int i = 0; i < 4; i++) {
        x[i] = (in[i*4] << 24) | (in[i*4+1] << 16) | (in[i*4+2] << 8) | in[i*4+3];
    }
    for (int i = 0; i < 32; i++) {
        x[i+4] = x[i] ^ sm4_t(x[i+1] ^ x[i+2] ^ x[i+3] ^ rk[i]);
    }
    for (int i = 0; i < 4; i++) {
        out[i*4]   = (x[35-i] >> 24) & 0xFF;
        out[i*4+1] = (x[35-i] >> 16) & 0xFF;
        out[i*4+2] = (x[35-i] >> 8) & 0xFF;
        out[i*4+3] = x[35-i] & 0xFF;
    }
}

// CBC模式加密
void sm4_cbc_encrypt(uint32_t rk[32], uint8_t iv[16], uint8_t *in, uint8_t *out, size_t len) {
    uint8_t prev[16];
    memcpy(prev, iv, 16);
    for (size_t i = 0; i < len; i += 16) {
        for (int j = 0; j < 16; j++) {
            prev[j] ^= in[i+j];
        }
        sm4_encrypt_block(rk, prev, out+i);
        memcpy(prev, out+i, 16);
    }
}

我的建议:在TrustZone里实现时,记得把轮密钥rk数组放在安全世界的内存里,不要泄露到普通世界。另外,IV必须随机生成,我习惯用安全世界的TRNG来生成IV,而不是用伪随机数。

4.6 测试与验证

实现完了怎么验证对不对?用标准测试向量。SM4的标准测试向量在GMT 0002-2012里有。我贴一个常用的:

密钥:   0123456789abcdeffedcba9876543210
明文:   0123456789abcdeffedcba9876543210
密文:   681edf34d206965e86b3e94f536e4246

你实现完后,用这个向量跑一下。如果结果对不上,先检查密钥扩展,再检查轮函数。我当年调试时,就是先用这个向量把密钥扩展的中间结果打印出来,跟标准值逐轮对比,才找到bug的。

4.7 知识体系总览

下面这张图把SM4在TrustZone安全世界里的实现脉络梳理清楚了。你可以把它当作一个快速索引。

SM4算法在TrustZone安全世界的实现体系 SM4核心算法 密钥扩展 加解密核心 工作模式 FK常数 CK常数 S盒 L变换 32轮迭代 ECB CBC TrustZone安全要点 S盒锁在安全SRAM | 轮密钥不泄露 | IV用TRNG生成

本章小结:SM4的核心就三件事——密钥扩展、32轮迭代、工作模式。在TrustZone里实现时,记住把敏感数据(S盒、轮密钥、IV)放在安全世界,加解密操作在安全世界完成,普通世界只传密文和明文。这样既保证了性能,又守住了安全底线。