#include <stdio.h>
#include <stdint.h>
#include <string.h>

/*-------- 基本工具 --------*/
typedef union {
    uint32_t w;
    uint8_t  b[4];
} word;

#define ROTL32(x, n)  (((x) << ((n) & 31)) | ((x) >> (32 - ((n) & 31))))

/*-------- S 盒 --------*/
static const uint8_t S[256] = {
    0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
    0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
    0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
    0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
    0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
    0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
    0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
    0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
    0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
    0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
    0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
    0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
    0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
    0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
    0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
    0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};

/*-------- 常量 --------*/
static const uint32_t FK[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
static const uint32_t CK[32] = {
    0x00070e15,0x1c232a31,0x383f464d,0x545b6269,0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
    0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
    0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
    0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};

/*-------- 密钥扩展 --------*/
static void sm4_key_expand(const uint8_t mk[16], uint32_t rk[32])
{
    word k[4];
    for (int i = 0; i < 4; ++i)            // 字节序:big-endian
        k[i].w = (mk[i*4]<<24) | (mk[i*4+1]<<16) | (mk[i*4+2]<<8) | mk[i*4+3];

    for (int i = 0; i < 4; ++i) k[i].w ^= FK[i];

    for (int i = 0; i < 32; ++i) 
	{
        word t;
        t.w = k[(i+1)&3].w ^ k[(i+2)&3].w ^ k[(i+3)&3].w ^ CK[i];
		
        // 非线性层
        for (int j = 0; j < 4; ++j) 
			t.b[j] = S[t.b[j]];
		
        // 线性层 L'
        t.w ^= ROTL32(t.w, 13) ^ ROTL32(t.w, 23);
        k[i&3].w ^= t.w;
        rk[i] = k[i&3].w;
    }
}

/*-------- 轮函数 F --------*/
static uint32_t F(word x)
{
    for (int j = 0; j < 4; ++j) x.b[j] = S[x.b[j]];
    return x.w ^ ROTL32(x.w, 2) ^ ROTL32(x.w, 10) ^ ROTL32(x.w, 18) ^ ROTL32(x.w, 24);
}

/*-------- 单块加密/解密 --------*/
static void sm4_block(const uint8_t in[16], uint8_t out[16],
                      const uint32_t rk[32], int enc)
{
    word x[4];
    for (int i = 0; i < 4; ++i)
        x[i].w = (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) {
        int r = enc ? i : (31 - i);
        word t;
        t.w = x[1].w ^ x[2].w ^ x[3].w ^ rk[r];
        x[0].w ^= F(t);
        /* 循环移位 */
        word tmp = x[0]; x[0] = x[1]; x[1] = x[2]; x[2] = x[3]; x[3] = tmp;
    }
    /* 反序 */
    for (int i = 0; i < 4; ++i) {
        word t = x[3 - i];
        out[i*4]   = t.b[0];
        out[i*4+1] = t.b[1];
        out[i*4+2] = t.b[2];
        out[i*4+3] = t.b[3];
    }
}

/*-------- 打印 16 字节 --------*/
static void print16(const char *tag, const uint8_t d[16])
{
    printf("%s: ", tag);
    for (int i = 0; i < 16; ++i) printf("%02x", d[i]);
    printf("\n");
}

/*-------- Demo --------*/
int main(void)
{
    uint8_t key[16] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
                        0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 };
    uint8_t pt [16] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
                        0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 };
    uint8_t ct [16];
    uint8_t out[16];

    uint32_t rk[32];
    sm4_key_expand(key, rk);

    sm4_block(pt, ct, rk, 1);   // 加密
    sm4_block(ct, out, rk, 0);  // 解密

    print16("plain ", pt);
    print16("cipher", ct);
    print16("decryp", out);

    return 0;
}

 

 

1. 加密——逐轮“裹”密钥

初始明文字节:P = 0b1010 1010(十六进制 0xAA)

轮密钥(4 把,每轮 1 字节):

K0 = 0x12
K1 = 0x34
K2 = 0x56
K3 = 0x78
轮次 操作(⊕ 即异或) 中间值(十六进制) 说明
0 C0 = P ⊕ K0 0xAA ⊕ 0x12 = 0xB8 裹上第 1 层
1 C1 = C0 ⊕ K1 0xB8 ⊕ 0x34 = 0x8C 裹上第 2 层
2 C2 = C1 ⊕ K2 0x8C ⊕ 0x56 = 0xDA 裹上第 3 层
3 C3 = C2 ⊕ K3 0xDA ⊕ 0x78 = 0xA2 裹上第 4 层

得到密文:C = 0xA2

2. 解密——倒序“剥”密钥

从密文 0xA2 开始,钥匙反着用:K3 → K2 → K1 → K0

轮次 操作(再异或同一钥匙) 中间值 说明
0 D0 = C ⊕ K3 0xA2 ⊕ 0x78 = 0xDA 剥掉最后一层
1 D1 = D0 ⊕ K2 0xDA ⊕ 0x56 = 0x8C 剥掉第 3 层
2 D2 = D1 ⊕ K1 0x8C ⊕ 0x34 = 0xB8 剥掉第 2 层
3 D3 = D2 ⊕ K0 0xB8 ⊕ 0x12 = 0xAA 剥掉第 1 层

结果 D3 = 0xAA → 与最初明文完全一致!

3. 核心观察

  • 每一步都是“自己”的逆运算:异或;
  • 只要钥匙顺序倒过来,就能逐层抵消之前加进去的扰动;
  • SM4 的 32 轮、32 位、4 分支不过是把这套“剥洋葱”游戏放大而已——原理一模一样。
posted on 2026-01-23 10:48  轩~邈  阅读(3)  评论(0)    收藏  举报