惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
云风的 BLOG
云风的 BLOG
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
A
About on SuperTechFans
WordPress大学
WordPress大学
B
Blog
Martin Fowler
Martin Fowler
Jina AI
Jina AI
I
InfoQ
P
Proofpoint News Feed
小众软件
小众软件
S
SegmentFault 最新的问题
V
V2EX
B
Blog RSS Feed
量子位
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
MongoDB | Blog
MongoDB | Blog
美团技术团队

博客园 - longyue

[BF学院_卷1] -- 第30讲:mixTable() 总结 —— Mixer 完整控制模型 [BF学院_卷1] -- 第29讲:applyMixToMotors() —— 最终 Motor Output 生成 [BF学院_卷1] -- 第28讲:applyMixerAdjustment() —— 为什么 Mixer 需要重新分配控制权限 [BF学院_卷1] -- 第27讲:PID Output 如何进入 Mixer —— 从控制需求到 motorMix [BF学院_卷1] -- 第26讲:calculateThrottleAndCurrentMotorEndpoints() —— Mixer 如何确定油门基础范围 [BF学院_卷1] -- 第25讲:mixTable() 总览 —— Mixer 在控制循环中的位置 [BF学院_卷1] -- 第24讲:mixer_init.c —— 飞控启动时如何准备 Mixer [BF学院_卷1] -- 第23讲:Mixer 数学模型 —— motorMixer_t 如何形成 Motor Output [BF学院_卷1] -- 第22讲:Betaflight 如何描述 Mixer —— mixer.h 数据结构 [BF学院_卷1] -- 第21讲:四旋翼为什么可以通过电机控制姿态 —— Mixer 背后的物理基础 [BF学院_卷1] -- 第20讲:从 PID Output 到 Motor Command(电机指令)——为什么飞控需要 Mixer [BF学院_卷1] -- 第19讲:PID Controller(PID控制器)——飞控到底如何理解误差并控制飞机? [BF学院_卷1] -- 第18讲:PID Controller 的最后一环——为什么飞控还需要 Feedforward(前馈控制)? [BF学院_卷1] -- 第17讲:D Term(微分控制项)——飞控如何提前感知运动变化? [BF学院_卷1] -- 第16讲:I Term(积分控制项)——飞机为什么总是差一点? [BF学院_卷1] -- 第14讲:Rate Error(角速度误差)——PID 真正控制的到底是什么? [BF学院_卷1] -- 第15讲:P Term(比例控制项)——飞控是如何利用误差开始控制飞机的? [BF学院_卷1] -- 第13讲:Outer Loop 的终点——为什么还要读取 Current Angular Rate(当前角速度)? [BF学院_卷1] -- 第十二讲:Yaw Compensation(偏航补偿) [BF学院_卷1] -- 第12讲脚本 [BF学院_卷1] -- 第十一讲:Current Angular Rate(当前角速度)——飞控怎样知道飞机现在转得有多快? [BF学院_卷1] -- 第十讲:Angle Error(姿态角误差)如何变成 Desired Angular Rate(目标角速度) [BF学院_卷1] -- 第九讲:Current Angle(当前姿态角)遇见 Target Angle(目标姿态角) [BF学院_卷1] -- 第八讲:飞手打杆以后,飞控首先得到什么? [BF学院_卷1] -- 第七讲:为什么 Pitch Flip 只在 180° 看起来正确 [BF学院_卷1] -- 第7讲脚本 [BF学院_卷1] -- 第6讲:为什么 Pitch Flip 会骗人 [BF学院_卷1] -- 第6讲脚本 [BF学院_卷1] -- 第五讲:第一次修正——Pitch Flip 为什么看起来有效 [BF学院_卷1] -- 第5讲脚本 - longyue
[算法 - 加密] SM4 算法的优化
longyue · 2026-04-05 · via 博客园 - longyue

一、先把问题说清楚

在 SM4 里,每一轮核心是:T(x) = L( Sbox(x) )

展开就是:
把 x 拆成4个字节 → 过 S盒(查表)
拼回32位
再做一堆循环移位 + XOR(L)

T_table优化就是把这两步合成一步。

=================================================
二、核心思想(一句话)

L 是线性的,而 Sbox 是逐字节的 → 可以“提前算好所有结果”

=================================================
三、一步一步拆开

1、把 x 拆成4个字节
x = (a << 24) | (b << 16) | (c << 8) | d

2、过 Sbox
S(x) = (S[a]<<24) | (S[b]<<16) | (S[c]<<8) | S[d]
这一步是逐字节独立的

3、关键点来了:L 是线性的
L 定义 : L(x) = x ^ (x<<<2) ^ (x<<<10) ^ (x<<<18) ^ (x<<<24)

这是一个线性变换, 满足:
L(A ^ B) = L(A) ^ L(B)

=================================================
四、最关键的一步(本质)
L(S(x)) = L(S[a]<<24 ^ S[b]<<16 ^ S[c]<<8 ^ S[d])
= L(S[a]<<24) ^ L(S[b]<<16) ^ L(S[c]<<8) ^ L(S[d])

=================================================
五、于是可以预计算
T0[a] = L(S[a] << 24)
T1[b] = L(S[b] << 16)
T2[c] = L(S[c] << 8)
T3[d] = L(S[d])

运行时就变成
T(x) = T0[a] ^ T1[b] ^ T2[c] ^ T3[d]

// ====== T-table ======
static uint32_t T0[256],T1[256],T2[256],T3[256];

// 预读 S盒, 对每个字节先进行 S+L 计算
void init_ttable(){
    for(int i=0;i<256;i++){
        uint32_t b=Sbox[i];
        T0[i]=L(b<<24);
        T1[i]=L(b<<16);
        T2[i]=L(b<<8);
        T3[i]=L(b);
    }
}

// ====== T-table 加密 ======
void sm4_encrypt_t(uint32_t in[4], uint32_t rk[32], uint32_t out[4]) {
    uint32_t x0 = in[0];
    uint32_t x1 = in[1];
    uint32_t x2 = in[2];
    uint32_t x3 = in[3];

    // 32轮计算 X4 = X0 ^ T(X1 ^ X2 ^ X3 ^ rk)
    for (int i = 0; i < 32; i++) {
        // 先整体 XOR
        uint32_t tmp = x1 ^ x2 ^ x3 ^ rk[i];

        // 再拆字节(这是 T-table 的入口)
        uint32_t a = (tmp >> 24) & 0xff;
        uint32_t b = (tmp >> 16) & 0xff;
        uint32_t c = (tmp >> 8)  & 0xff;
        uint32_t d = tmp & 0xff;

        // T-table 查表
        uint32_t t = T0[a] ^ T1[b] ^ T2[c] ^ T3[d];

        // Feistel 更新
        uint32_t newX = x0 ^ t;

        // 滚动
        x0 = x1;
        x1 = x2;
        x2 = x3;
        x3 = newX;
    }

    // 反序输出
    out[0] = x3;
    out[1] = x2;
    out[2] = x1;
    out[3] = x0;
}

// ====== T-table 解密 ======
void sm4_decrypt_t(uint32_t in[4], uint32_t rk[32], uint32_t out[4]) {
    uint32_t x0 = in[0];
    uint32_t x1 = in[1];
    uint32_t x2 = in[2];
    uint32_t x3 = in[3];

    for (int i = 0; i < 32; i++) {
        uint32_t tmp = x1 ^ x2 ^ x3 ^ rk[31 - i];

        uint32_t a = (tmp >> 24) & 0xff;
        uint32_t b = (tmp >> 16) & 0xff;
        uint32_t c = (tmp >> 8)  & 0xff;
        uint32_t d = tmp & 0xff;

        uint32_t t = T0[a] ^ T1[b] ^ T2[c] ^ T3[d];

        uint32_t newX = x0 ^ t;

        x0 = x1;
        x1 = x2;
        x2 = x3;
        x3 = newX;
    }

    out[0] = x3;
    out[1] = x2;
    out[2] = x1;
    out[3] = x0;
}

// ====== 测试 ======
int main(){
    uint32_t MK[4]={0x01234567,0x89abcdef,0xfedcba98,0x76543210};
    uint32_t P[4]={0x01234567,0x89abcdef,0xfedcba98,0x76543210};

    uint32_t rk[32],C[4],D[4];

    key_schedule(MK,rk);
    init_ttable();

    sm4_encrypt_t(P,rk,C);
    sm4_decrypt_t(C,rk,D);

    printf("Plain : %08x %08x %08x %08x\n",P[0],P[1],P[2],P[3]);
    printf("Cipher: %08x %08x %08x %08x\n",C[0],C[1],C[2],C[3]);
    printf("Dec   : %08x %08x %08x %08x\n",D[0],D[1],D[2],D[3]);

    return 0;
}