从AES到Feistel:3分钟搞懂伪随机函数(PRF)如何变身分组密码

张开发
2026/4/4 5:12:31 15 分钟阅读
从AES到Feistel:3分钟搞懂伪随机函数(PRF)如何变身分组密码
从AES到Feistel3分钟搞懂伪随机函数(PRF)如何变身分组密码密码学中有两个看似相似却截然不同的概念伪随机函数PRF和分组密码Block Cipher。它们就像魔术师手中的道具一个能产生看似随机的输出另一个则能实现可逆的加密解密。本文将用工程师的视角通过AES和Feistel结构的实际案例揭示PRF如何通过特定结构转化为分组密码的核心机制。1. 密码学基础PRF与分组密码的本质区别当我们谈论伪随机函数PRF时实际上是在讨论一个黑盒子——给定密钥和输入它能产生一个看起来完全随机的输出。从形式上看PRF可以表示为def PRF(key, message): # 使用密钥处理消息 return output而分组密码或称伪随机置换PRP则额外要求一个关键特性可逆性。这意味着存在对应的解密函数def BlockCipher_Encrypt(key, plaintext): # 加密过程 return ciphertext def BlockCipher_Decrypt(key, ciphertext): # 解密过程 return plaintext核心差异对比表特性PRF分组密码(PRP)可逆性不需要必须存在解密函数输入输出长度可以不同必须相同安全性要求输出看起来随机加密/解密都需安全典型应用密钥派生、MACAES、DES等对称加密在工程实践中AES是最著名的分组密码实例。它采用替换-置换网络SPN结构通过多轮加密实现安全性。而我们要探讨的是另一种经典结构——Feistel网络它能够将任何PRF即使不可逆转化为安全的PRP。提示虽然PRP要求可逆性但有趣的是我们可以用不可逆的PRF构造出可逆的PRP这正是Feistel结构的精妙之处。2. Feistel结构将PRF转化为PRP的魔法框架Feistel结构由IBM密码学家Horst Feistel在1970年代提出其核心思想是通过分而治之的方式实现可逆性。让我们拆解一个最基本的Feistel轮def feistel_round(left, right, round_key): new_left right new_right left ^ PRF(round_key, right) return (new_left, new_right)这个结构的精妙之处在于无论PRF函数内部如何操作整个结构始终可逆解密过程使用完全相同的结构只需调整轮密钥顺序每轮只处理一半的数据降低了实现复杂度3轮Feistel加密流程初始分组(L₀, R₀)第一轮后(L₁R₀, R₁L₀⊕F(K₁,R₀))第二轮后(L₂R₁, R₂L₁⊕F(K₂,R₁))第三轮后(L₃R₂, R₃L₂⊕F(K₂,R₂))安全性研究表明2轮Feistel不安全存在明显的统计特征3轮Feistel可达到PRP安全4轮Feistel可达到强PRP(SPRP)安全3. 从理论到实践AES与Feistel的对比分析虽然Feistel结构理论上很优美但现代最广泛使用的AES却选择了不同的设计路线。让我们对比这两种设计哲学AES (Rijndael) 设计特点采用SPN(Substitution-Permutation Network)结构并行处理整个数据块依赖精心设计的S盒实现非线性每轮操作包括字节替换、行移位、列混淆、轮密钥加Feistel 设计特点串行处理数据的一半可以接受任何PRF作为轮函数加解密结构对称更容易在硬件中实现以下是用Python实现的简化3轮Feistel示例from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend import os def prf(key, data): 使用AES作为PRF示例 cipher Cipher(algorithms.AES(key), modes.ECB(), backenddefault_backend()) encryptor cipher.encryptor() return encryptor.update(data) encryptor.finalize() def feistel_encrypt(block, keys): left, right block[:8], block[8:] # 假设16字节块 for key in keys: new_left right new_right bytes(a ^ b for a, b in zip(left, prf(key, right))) left, right new_left, new_right return left right def feistel_decrypt(block, keys): left, right block[:8], block[8:] for key in reversed(keys): new_right left new_left bytes(a ^ b for a, b in zip(right, prf(key, left))) left, right new_left, new_right return left right # 示例使用 key1 os.urandom(16) key2 os.urandom(16) key3 os.urandom(16) plaintext b16byte plaintext ciphertext feistel_encrypt(plaintext, [key1, key2, key3]) decrypted feistel_decrypt(ciphertext, [key1, key2, key3]) assert decrypted plaintext4. 现代密码学中的实际应用与安全考量Feistel结构虽然不如AES流行但在许多重要密码系统中仍有应用典型应用场景DES/TDES经典的Feistel密码Microsoft的BCrypt密码哈希某些格式保留加密(FPE)方案可搜索加密方案安全实践要点轮数选择3轮是PRP安全的最低要求实际中常用16轮以上轮函数设计应使用密码学安全的PRF如HMAC或AES密钥调度每轮应使用独立密钥可通过密钥派生函数生成实现安全注意时序攻击防护建议使用恒定时间实现对于需要更高安全性的场景可以考虑Feistel的变种Unbalanced Feistel左右分组大小不同Generalized Feistel多个分支并行Alternating Feistel交替处理不同分组在TLS 1.3等现代协议中虽然主要使用AES和ChaCha20但理解Feistel结构仍然重要因为它揭示了如何从较弱的密码学原语构建更强的加密方案。

更多文章