2. Feistel 网络基础:对称结构、轮函数、加解密对称性

聊 FF1 和 FF3 之前,咱们得先把地基打牢。这个地基就是 Feistel 网络。说白了,它是一种构造分组密码的经典框架。很多现代算法——比如 DES、Blowfish,还有咱们要讲的 FF1 和 FF3——都是在这个骨架上长出来的肉。

我个人习惯把 Feistel 网络看作一个「分而治之」的瑞士军刀。你想想看,把一个长长的数据块直接做加密,数学上很麻烦。但要是把它劈成两半,每轮只折腾其中一半,另一半原地不动?那事情就简单多了。

2.1 对称结构:左半和右半

Feistel 网络的结构其实非常直观。咱们来看它的核心设计:

  • 分块处理:输入的数据块会被切成两等份——左半(L)和右半(R)。
  • 迭代轮数:这两块数据会经过多次相同的操作,每一轮叫一个「轮(Round)」。
  • 角色互换:每一轮结束后,左半和右半会交换位置,然后进入下一轮。

我在项目中调试过一些自己造的 Feistel 原型,有个坑让我印象很深:千万别把左右两半的大小弄成不对称的。要是左半比右半长一截,那后面几轮的平衡性会彻底乱掉。FF1 和 FF3 都要求左右两半长度差不超过 1,就是这个道理。

关键点:Feistel 的对称性来源于左右两半的结构完全相同。加密和解密共用一套轮函数,只是密钥使用的顺序相反。

2.2 轮函数:整个网络的引擎

轮函数 F 是 Feistel 网络里最核心的部分。它接收右半数据 Ri 和本轮密钥 Ki 作为输入,输出一个固定长度的伪随机串。
然后这个输出会和左半数据 Li 做异或(XOR),结果成为下一轮的右半 Ri+1。

公式很简单:

Li+1 = Ri
Ri+1 = Li ⊕ F(Ri, Ki)

为什么要强调这个公式?因为它揭示了一个重要的特性:轮函数 F 可以是任意函数,甚至不要求可逆。加密时怎么算,解密时也怎么算,完全不用修改 F 本身。嗯,这里要注意:很多人会误以为 F 需要可逆,其实不需要。这也是 Feistel 架构精巧的地方。

我记得有一次优化一个旧版 FF3 的实现,瓶颈就出在轮函数 F 对数字和字母混合域的处理上。当时改了改 F 内部的查表策略,性能提升了 30% 还多。轮函数的设计,直接决定了加密速度和安全性。

小技巧:在实际实现中,轮函数 F 通常包含:密钥扩展、非线性替换(比如 S 盒)、线性置换(比如 P 盒)三个步骤。FF1 和 FF3 的 F 内部还嵌入了 AES 或 SM4 这样的标准分组加密算法,用来生成伪随机掩码。

2.3 加解密对称性:一模一样的结构

Feistel 网络最让我觉得「优雅」的地方,就是它的加解密对称性。加密和解密走的是同一套流程,唯一的区别在于子密钥的使用顺序。

咱们来看:

  • 加密流程:使用密钥 K0, K1, ..., Kn-1 依次应用每一轮。
  • 解密流程:使用密钥 Kn-1, Kn-2, ..., K0 也就是逆序应用即可。

你可能会问:为什么可以这样?咱们推导一下。给定一轮加密:

Li+1 = Ri
Ri+1 = Li ⊕ F(Ri, Ki)

解密时,已知 (Li+1, Ri+1),想恢复 (Li, Ri):

Ri = Li+1
Li = Ri+1 ⊕ F(Li+1, Ki)

你看,F 函数被直接复用了,连实现代码都不用动。我曾经在一个嵌入式项目中为了省 ROM 空间,加密和解密模块直接用了同一个函数,只传不同的密钥顺序参数。硬件资源紧张的时候,这种对称性能省下不少事。

避坑指南:虽然 Feistel 解密只改了密钥顺序,但有些库(比如某些旧版 PyCryptodome)在实现 DES 时,解密和加密的轮函数内部有微妙的边界检查差异。我曾经被这种“对称但不完全对称”的 bug 坑过一整天。所以不管你用哪个库,做加解密对称性验证时,一定得写单元测试,用多组随机数据跑一遍。

2.4 两种常见的 Feistel 变体

特性 传统 Feistel (FH-X) 非平衡 Feistel (FH-P)
左右分块大小 严格相等 可以不相等
典型应用 DES, FF1 FF3
轮函数设计难度 相对较低 需要更复杂的域处理
安全性证明 经典成熟 轮数要求更高

FF1 用的是传统的平衡 Feistel,左右两半大小一致,数学证明很成熟。FF3 用的是非平衡 Feistel(也叫 FH-P),左右两半在第二轮之后会出现长度差异。我个人觉得,非平衡 Feistel 虽然更灵活,但也更容易踩坑——比如左右两半的域处理函数必须不同,否则会出现不安全的相关性。

2.5 一图看懂 Feistel 网络

下面这张图展示了 Feistel 网络一轮加密的完整流程。你注意看左右两半的流动方向,还有轮函数 F 的输入输出位置。

Feistel 网络一轮加密流程(典型结构) 输入数据块 Li (左半) Ri (右半) 输入 轮函数 F (Ri, Ki) 本轮密钥 Ki ⊕ F(Ri, Ki) 输出 Ri+1 = Li ⊕ F(Ri, Ki) Li+1 = Ri 注意: • 左右交换后进入下一轮 • F 函数不需要可逆 • 加解密共用同一套 F

2.6 实际应用中的感悟

我个人觉得,Feistel 网络最大的贡献不是数学上的突破,而是工程上的「可复制性」。你只要把轮函数 F 设计好,整个加密算法的骨架几乎不用变。FF1 和 FF3 虽然是针对格式保持加密设计的,但它们的核心还是这个老骨架。

最后,我有个温馨提醒:无论是研究 FF1 还是 FF3,先把 Feistel 网络的左移右移、异或、密钥顺序这三个基本动作刻在脑子里。后面几十章的内容,都是在这个框架上做文章。