土法炼钢兴趣小组的算法知识备份

【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学

目录

2001 年 11 月 26 日,美国国家标准与技术研究院(NIST)正式发布 FIPS 197 号标准,宣告高级加密标准(Advanced Encryption Standard, AES)的诞生。这一决定结束了历时五年的公开竞赛,也终结了数据加密标准(DES)长达二十余年的统治。如今,AES 保护着从 HTTPS 流量到磁盘加密、从政府机密到即时通讯的几乎一切数字通信。然而,多数使用者只是把它当作一个黑盒来调用——传入明文和密钥,取出密文,从不关心中间究竟发生了什么。

本文的目标是彻底拆开这个黑盒。我们将从 AES 竞赛的历史背景出发,深入到 GF(2^8) 有限域的代数结构,逐一剖析 SubBytes、ShiftRows、MixColumns 和 AddRoundKey 四个基本操作的数学原理,详解密钥扩展算法的设计逻辑,分析已知的最佳攻击,最后讨论硬件加速与量子计算时代下 AES 的未来。这不是一篇教你如何调用 API 的教程,而是一次对对称密码学最核心算法的全面数学解剖。

一、AES 竞赛回顾

为何需要新标准

DES 于 1977 年被采纳为美国联邦加密标准,密钥长度仅为 56 位。到 1990 年代末,摩尔定律已使穷举搜索变得可行:1998 年,电子前沿基金会(EFF)的 “Deep Crack” 专用硬件在不到三天内破解了 DES 密钥。虽然三重 DES(3DES)通过三次加密将有效密钥长度提升至 112 位,但其性能仅为 DES 的三分之一,且 64 位分组长度在大数据量加密时暴露出生日碰撞风险——Sweet32 攻击后来证实了这一点。NIST 意识到,密码学界需要一个全新的、面向二十一世纪的分组密码标准。

十五个候选者

1997 年 1 月 2 日,NIST 发起 AES 征集计划,要求候选算法支持 128 位分组和 128/192/256 三种密钥长度。截止 1998 年 6 月 15 日,共收到来自全球密码学家的 15 个候选方案。这些方案各具特色:IBM 的 MARS 采用异构轮结构以兼顾安全性与效率;Ron Rivest 的 RC6 在 RC5 基础上引入数据依赖旋转和乘法操作以增强扩散;Bruce Schneier 团队的 Twofish 继承了 Blowfish 的密钥依赖 S-Box 设计,在安全余量上极为激进;Ross Anderson 和 Eli Biham 等人的 Serpent 以 32 轮结构提供了最大的安全余量,但速度较慢。

评估标准与最终抉择

NIST 的评估维度涵盖三大类:安全性(Security)、性能(Performance)和简洁性(Simplicity)。安全性方面,所有五个决赛方案在评估期间均未被发现实质性弱点。性能方面,NIST 考察了算法在 32 位和 64 位处理器、智能卡、FPGA 和 ASIC 等多种平台上的表现。简洁性方面,NIST 重视算法设计的透明度——设计者能否清晰解释每一个设计决策的理由,而非依赖”魔术常数”。

2000 年 10 月 2 日,NIST 宣布由比利时密码学家 Joan Daemen 和 Vincent Rijmen 设计的 Rijndael 算法胜出。Rijndael 在所有平台上均展现出优异的性能:在 32 位处理器上的吞吐量领先,在硬件实现中面积与速度的平衡最佳,在智能卡等资源受限环境中表现同样出色。更重要的是,Rijndael 的代数结构极为清晰——每一步操作都能用严格的数学语言解释其对安全性的贡献——这种设计透明度是其他候选者难以匹敌的。Serpent 虽然安全余量更大(32 轮对比 Rijndael 的 10 轮),但在性能上明显落后;Twofish 的密钥依赖 S-Box 虽然增加了代数分析的难度,但也使实现复杂度上升,不利于硬件优化和常量时间实现。

二、GF(2^8) 有限域算术

要理解 AES 的内部运算,必须先掌握 GF(2^8) 有限域(Galois Field)的算术。AES 的设计者选择在这个域上构建所有核心操作,这一选择既赋予了算法严谨的代数结构,又使其能在硬件和软件中高效实现。

多项式表示

GF(2^8) 中的每个元素可以表示为次数不超过 7 的二进制系数多项式。例如,字节 0x57(二进制 01010111)对应多项式 x^6 + x^4 + x^2 + x + 1。这个域恰好包含 256 个元素,与一个字节的取值范围完美对应——这绝非巧合,而是 AES 设计者有意为之的核心决策。

不可约多项式

GF(2^8) 的构造需要一个 8 次不可约多项式(Irreducible Polynomial)作为模多项式,其地位类似于整数模运算中的模数。AES 选用的不可约多项式为 m(x) = x^8 + x^4 + x^3 + x + 1,对应十六进制值 0x11B。所谓”不可约”,是指这个多项式不能分解为两个次数更低的非常数多项式之积——就像素数不能分解为更小的因子一样。这保证了 GF(2^8) 中的每个非零元素都有唯一的乘法逆元。

为什么选择这个特定的多项式?8 次不可约多项式并不唯一——GF(2) 上共有 30 个 8 次不可约多项式。AES 选择 x^8 + x^4 + x^3 + x + 1 是因为它是一个五项式(pentanomial),具有最低的非零系数权重之一,且在硬件实现中模约简操作的效率较高。

加法:异或

GF(2^8) 中的加法定义为对应系数在 GF(2) 中相加,即逐位异或(XOR)。例如 0x57 + 0x83 = 0xD4。加法的逆运算也是异或——换言之,GF(2^8) 中的加法和减法是同一运算。这一性质使得 AES 的实现无需区分加法和减法,大幅简化了编码。

乘法

GF(2^8) 中的乘法稍微复杂:先按普通多项式乘法计算两个多项式之积,再对不可约多项式 m(x) 取模。例如,计算 0x57 * 0x83 时,首先将 (x^6 + x^4 + x^2 + x + 1) 与 (x^7 + x + 1) 相乘得到一个 13 次多项式,然后对 m(x) 取模,将结果约简回 GF(2^8) 的范围内。

在软件实现中,GF(2^8) 乘法通常通过”xtime”操作来实现——这是一个将多项式乘以 x 的基本操作。xtime 的具体步骤是:将字节左移一位,若原字节最高位为 1(意味着乘以 x 后次数超过 7),则将结果异或 0x1B(即 x^4 + x^3 + x + 1,不可约多项式去掉 x^8 项后的低位部分)。任意两个元素的乘积可以通过反复调用 xtime 并累加中间结果来计算——本质上是”乘法转移位加法”的二进制展开(类似于整数乘法的”移位累加”算法)。

乘法逆元

GF(2^8) 中每个非零元素都有唯一的乘法逆元,即对任意非零 a,存在唯一的 b 使得 a * b = 1。逆元的计算可通过扩展欧几里得算法(Extended Euclidean Algorithm)在多项式环上实施,也可以利用费马小定理的推广:在 GF(2^8) 中,a(28 - 1) = a^255 = 1,因此 a^(-1) = a^254。后者可通过反复平方法在不到 15 次乘法内完成,且是常量时间的。在实际的 AES 实现中,最常见的做法是预计算全部 256 个逆元并存入查找表。

三、SubBytes:S-Box 的代数构造

SubBytes 是 AES 中唯一的非线性操作,也是抵抗密码分析的核心所在。它通过一个 8 位到 8 位的替换盒(Substitution Box, S-Box)对状态矩阵中的每个字节独立进行变换。

两步构造

AES 的 S-Box 由两个步骤复合而成:

第一步:GF(2^8) 乘法求逆。 将输入字节视为 GF(2^8) 中的元素,计算其乘法逆元;零元素映射到自身(因为零没有逆元,特别定义 0 的像为 0)。乘法求逆提供了优异的非线性特性,因为逆映射 x -> x^(-1) 在 GF(2^8) 上具有接近最优的差分均匀度(Differential Uniformity)和非线性度(Non-linearity)。

第二步:GF(2) 上的仿射变换(Affine Transformation)。 对求逆后的结果执行一个固定的线性变换加一个常数向量。具体地,设 b = (b_7, b_6, …, b_0) 为求逆后的字节,则仿射变换为:

b’i = b_i XOR b(i+4 mod 8) XOR b_(i+5 mod 8) XOR b_(i+6 mod 8) XOR b_(i+7 mod 8) XOR c_i

其中 c = 0x63(即 01100011)。这个仿射变换等价于一个循环矩阵乘法加上一个常数异或。

为何选择这种构造

单独的乘法逆元映射虽然非线性度高,但它本身是一个简洁的代数表达式——攻击者可能利用这种代数结构发动插值攻击(Interpolation Attack)或代数攻击(Algebraic Attack)。仿射变换的引入正是为了打破这种代数规律性。仿射变换本身是线性的,不增加非线性度,但它使得 S-Box 整体的代数描述变得更为复杂,有效防止了攻击者构建 GF(2^8) 上的低次多项式逼近。

差分与线性特性

AES S-Box 的差分均匀度为 4——这意味着对于任意非零输入差分 Δx,输出差分 Δy 的出现次数最多为 4(在 256 种可能的输入中)。线性逼近方面,S-Box 的最大线性偏差(Maximum Linear Bias)为 2^(-3),即最佳线性逼近的偏差不超过 16/256。这两项指标在 8 位 S-Box 中均达到或接近理论最优——APN(Almost Perfect Nonlinear)函数的差分均匀度为 2,但在 GF(2^8)(偶数特征域)上是否存在 APN 置换至今仍是公开问题。Rijndael 的设计者在 2002 年证明,在所有由幂映射后接仿射变换构成的 GF(2^8) S-Box 中,求逆映射给出了最优的差分和线性特性。

S-Box 的透明性

值得强调的是,AES S-Box 没有任何不可解释的”魔术常数”。每一个设计选择——不可约多项式、求逆映射、仿射矩阵、常数 0x63——都有明确的数学动机。这种设计透明度是 Kerckhoffs 原则在算法内部结构层面的体现:即便攻击者完全了解 S-Box 的构造细节,也无法利用这些信息找到比穷举更快的攻击。

四、ShiftRows 与 MixColumns

如果 SubBytes 提供了字节级的非线性(混淆, Confusion),那么 ShiftRows 和 MixColumns 共同提供了字节间的线性扩散(Diffusion)。二者配合,确保明文或密钥中任何一位的改变都能在少数几轮内扩散到整个状态。

ShiftRows:行移位

AES 的内部状态是一个 4×4 的字节矩阵(4 行 4 列,共 16 字节 = 128 位)。ShiftRows 对每一行执行不同偏移量的循环左移:第 0 行不移动,第 1 行左移 1 字节,第 2 行左移 2 字节,第 3 行左移 3 字节。

这个看似简单的操作蕴含着精妙的扩散设计。SubBytes 是逐字节独立操作的,MixColumns 则是逐列操作的——如果没有 ShiftRows,同一列中的字节在多轮加密后仍然只会受到同一列原始字节的影响,列与列之间不会发生信息交换。ShiftRows 通过将不同行的字节移动到不同列中,使得下一轮 MixColumns 操作时,每一列都包含来自上一轮不同列的字节。这种”列间扩散”确保了经过少数几轮后,输出的每个字节都依赖于输入的所有 16 个字节。

移位量的选择(0, 1, 2, 3)并非随意。Rijndael 的设计者通过理论分析和搜索证明,这组偏移量使得任意两个不同位置的字节在经过 4 轮完整加密后必然互相影响——这是 128 位分组下的最优选择。对于 Rijndael 的 192 位和 256 位分组变体(注意这是分组大小的变体,与 AES 标准不同),移位量会相应调整以维持最优扩散。

MixColumns:列混合

MixColumns 是 AES 中计算量最大的线性操作。它将状态矩阵的每一列视为 GF(2^8) 上的 4 维向量,并左乘一个固定的 4×4 矩阵。这个矩阵是:

| 02 03 01 01 |
| 01 02 03 01 |
| 01 01 02 03 |
| 03 01 01 02 |

其中 020301 均为 GF(2^8) 中的元素,乘法和加法按 GF(2^8) 的规则执行。01 是乘法单位元(即不变),02 等价于 xtime 操作,03 = 02 + 01(即 xtime 后再异或原值)。

MDS 矩阵与最优扩散

这个矩阵并非随意选取。它是一个最大距离可分(Maximum Distance Separable, MDS)矩阵的循环形式。MDS 矩阵的关键性质是:对于任意非零输入向量,输入中非零字节数与输出中非零字节数之和至少为 5(即列长度加 1)。换言之,如果输入中只有 1 个字节非零,则输出中至少有 4 个字节非零;如果输入中有 2 个非零字节,输出中至少有 3 个非零字节——以此类推。

这一性质被称为分支数(Branch Number)为 5,它是 4×4 MDS 矩阵能达到的理论最大值。分支数直接决定了差分密码分析和线性密码分析中”活跃 S-Box”(Active S-Box)的最小数量——活跃 S-Box 越多,攻击所需的数据量和计算量就越大。

更具体地说,Rijndael 的 MixColumns 矩阵是一个循环矩阵(Circulant Matrix):每一行都是上一行的循环右移。循环矩阵的运算可以等价地表示为多项式乘法模 x^4 + 1(在 GF(2^8)[x] 上),这进一步简化了实现。矩阵元素仅包含 010203——这是满足 MDS 性质的最小元素集合之一,使得 MixColumns 的运算仅涉及异或和 xtime,不需要一般的 GF(2^8) 乘法,极大地降低了硬件复杂度和软件延迟。

ShiftRows 与 MixColumns 的协同

将 ShiftRows 和 MixColumns 视为一个整体,其扩散效果可以量化。经过连续四轮完整加密(SubBytes + ShiftRows + MixColumns + AddRoundKey),差分路径中的活跃 S-Box 数量至少为 25 个(对于 128 位分组)。由于每个 S-Box 的最大差分概率为 2^(-6)(差分均匀度 4 意味着每个差分对的概率至多 4/256 = 2^(-6)),连续 4 轮的差分特征概率上界为 (2(-6))25 = 2^(-150),远低于 2^(-128)——这为 AES 的全轮安全性提供了坚实的理论保障。

五、AddRoundKey 与密钥扩展

AddRoundKey

四个轮操作中最简单的是 AddRoundKey:它将 128 位轮密钥(Round Key)与状态矩阵逐字节异或。尽管简单,AddRoundKey 是 AES 中唯一引入密钥材料的步骤——没有它,加密就变成了一个公开的、与密钥无关的置换,毫无安全性可言。异或操作之所以足够,是因为在 GF(2^8) 中异或就是加法,而 SubBytes 的非线性保证了单纯的线性分析无法还原密钥。

密钥扩展算法

AES 的密钥扩展(Key Schedule)从原始密钥生成所有轮密钥。对于 AES-128,需要从 16 字节原始密钥生成 11 个轮密钥(初始轮 + 10 个加密轮),共 176 字节。密钥扩展以 32 位字(Word)为单位操作,原始密钥被视为 4 个字 W[0] 到 W[3],扩展后生成 W[4] 到 W[43]。

对于 i >= 4,扩展规则如下:

其中 RotWord 将 4 字节循环左移 1 字节,SubWord 对每个字节应用 S-Box 替换,Rcon 是轮常数——Rcon[j] = (rc[j], 0, 0, 0),其中 rc[j] 是 GF(2^8) 中 x 的幂次:rc[1] = 1, rc[2] = 2, rc[3] = 4, …(每次乘以 0x02,溢出时模不可约多项式)。

RotWord 和 SubWord 的作用是引入非线性和位置依赖性,防止相邻轮密钥之间呈现简单的线性关系。Rcon 的作用是破坏不同位置的对称性——如果没有 Rcon,所有位置为 4 的倍数的字将使用完全相同的变换,可能导致相关密钥攻击。

AES-192 和 AES-256 的差异

AES-192 的密钥为 24 字节(6 个字),需要生成 13 个轮密钥(初始轮 + 12 个加密轮),共 208 字节。扩展规则中,非线性变换在 i 为 6 的倍数时触发。

AES-256 的密钥为 32 字节(8 个字),需要生成 15 个轮密钥(初始轮 + 14 个加密轮),共 240 字节。扩展规则中,当 i 为 8 的倍数时执行完整的非线性变换(RotWord + SubWord + Rcon);当 i mod 8 = 4 时,额外执行一次 SubWord(不带 RotWord 和 Rcon)。这一额外的 SubWord 是 AES-256 独有的设计,旨在增强长密钥情况下密钥扩展的非线性。

六、AES 的完整加密流程

轮结构

AES-128 的加密过程包含一个初始轮密钥加和 10 轮迭代。整体结构如下:

  1. 初始轮密钥加:将明文与第 0 个轮密钥异或。
  2. 第 1 到第 9 轮(标准轮):依次执行 SubBytes -> ShiftRows -> MixColumns -> AddRoundKey。
  3. 第 10 轮(最终轮):依次执行 SubBytes -> ShiftRows -> AddRoundKey。最终轮省略了 MixColumns。

省略最终轮 MixColumns 的原因是实用性而非安全性:MixColumns 是可逆线性变换,如果保留它,加密的最后一步和解密的第一步可以相互抵消——不省略也不会影响安全性,但省略后加密和解密的结构更对称,且实现更紧凑。

AES-192 和 AES-256 分别使用 12 轮和 14 轮,结构完全相同——差异仅在轮数和密钥扩展算法上。

轮函数数据流示意图

下图以 ASCII 方式展示了 AES 单轮(标准轮,非最终轮)的数据流。4x4 字节的状态矩阵依次经过四个变换,每个变换承担不同的密码学职责:

 ┌─────────────────────────────────────────────────────────────────┐
 │                        AES 标准轮数据流                          │
 │                                                                 │
 │   State (4x4 bytes)                                             │
 │       │                                                         │
 │       ▼                                                         │
 │  ┌──────────┐   非线性替换:每字节独立经过 S-Box                   │
 │  │ SubBytes │   (GF(2^8) 求逆 + 仿射变换)                       │
 │  └────┬─────┘   提供混淆(Confusion)                             │
 │       │                                                         │
 │       ▼                                                         │
 │  ┌───────────┐  行内循环左移:第 0 行不移,                        │
 │  │ ShiftRows │  第 1/2/3 行分别左移 1/2/3 字节                    │
 │  └────┬──────┘  实现跨列扩散                                      │
 │       │                                                         │
 │       ▼                                                         │
 │  ┌────────────┐ 列内 MDS 矩阵乘法:                               │
 │  │ MixColumns │ 每列 4 字节在 GF(2^8) 上                          │
 │  └────┬───────┘ 左乘固定矩阵,分支数 = 5                          │
 │       │         提供扩散(Diffusion)                              │
 │       ▼                                                         │
 │  ┌─────────────┐                                                │
 │  │ AddRoundKey │ 与本轮子密钥逐字节异或                            │
 │  └────┬────────┘ 引入密钥材料                                     │
 │       │                                                         │
 │       ▼                                                         │
 │   Next State                                                    │
 └─────────────────────────────────────────────────────────────────┘

SubBytes 与 ShiftRows + MixColumns 的组合体现了 Shannon 提出的「混淆与扩散」双重原则:SubBytes 提供非线性混淆,使密文与密钥之间的统计关系尽可能复杂;ShiftRows 跨列打散字节位置,MixColumns 在每列内部通过 MDS 矩阵实现最大距离扩散——两者联合保证了一个字节的变化在两轮之内扩散到全部 16 个字节。AddRoundKey 则将密钥信息注入状态,确保不同密钥产生完全不同的加密路径。

从工程实践来看,AES 的代数结构是一把双刃剑——这是理解 AES 安全性时最值得深思的张力。一方面,GF(2^8) 上清晰的代数构造使得设计者能够严格证明扩散特性:MixColumns 的分支数恰好达到 4 字节列的理论上限 5,宽轨迹策略(Wide Trail Strategy)可以精确计算活跃 S 盒的下界,从而给出差分和线性攻击复杂度的可证明下界。这种「可证明的扩散」是 AES 相比 DES 的核心优势——DES 的 S 盒设计准则直到解密后才被 NSA 公开,而 AES 的每一个设计决策都有公开的数学依据。另一方面,正是这种代数规整性引发了对代数攻击的持续担忧:整个 AES-128 加密过程可以被描述为 GF(2) 上约 8000 个变量的 8000 个二次方程组,其结构远比随机方程组更有规律。虽然迄今为止 Groebner 基和 XL 等代数求解方法都未能威胁全轮 AES,但笔者认为这一风险不应被低估——它提醒我们,数学上的优雅并不自动等同于密码学上的强壮,AES 的长期安全性最终取决于代数攻击算法的进展速度是否能跟上分组密码轮数提供的安全余量。

一个经常被忽视的观点是,XSL 攻击路线(Courtois-Pieprzyk,2002)虽然从未产生过实际威胁,但它的存在本身就极具警示意义。XSL 试图利用 AES S-Box 可以用 GF(2) 上的稀疏二次方程描述这一事实,通过 eXtended Sparse Linearization 将方程组的求解复杂度降到亚指数级。这一尝试最终失败了——方程组的实际结构远比论文中的乐观估计更难处理——但它暴露了一个根本性的设计取舍:Rijndael 的设计者选择了代数透明性而非代数混沌性。AES 的每一步操作都可以用干净的数学语言描述,这使得安全性分析成为可能,也使得硬件实现极为高效——但它同时意味着,如果未来出现一种我们今天未曾预见的代数求解技术,AES 将比那些”看起来乱七八糟”的密码(如 Blowfish 的密钥依赖 S-Box)更容易被分析。这是一个二十五年前的赌注,而且到目前为止这个赌注赢了——但值得记住的是,它确实是一个赌注,而非确定性的保证。

解密过程

AES 的解密使用四个操作的逆变换,以相反顺序执行:

| 0E 0B 0D 09 |
| 09 0E 0B 0D |
| 0D 09 0E 0B |
| 0B 0D 09 0E |

这些元素(0x090x0B0x0D0x0E)比加密矩阵中的 010203 大得多,导致 InvMixColumns 的计算开销显著高于 MixColumns。这也是 AES 解密比加密稍慢的主要原因。

标准解密的轮密钥使用顺序与加密相反。此外,存在一种”等价解密”(Equivalent Inverse Cipher)结构,通过对中间轮密钥预先应用 InvMixColumns,可以将解密的轮结构调整为与加密完全对称——这在某些硬件实现中有助于复用加密电路。

七、AES 的安全性分析

AES 自标准化以来已经历了超过二十五年的公开密码分析。迄今为止,没有任何攻击在实际操作层面威胁到全轮 AES 的安全性。

Biclique 攻击

2011 年,Bogdanov、Khovratovich 和 Rechberger 提出了针对全轮 AES 的 biclique 攻击。这是目前已知的对全轮 AES 最有效的单密钥攻击:对 AES-128 的计算复杂度为 2^(126.1),对 AES-192 为 2^(189.7),对 AES-256 为 2^(254.4)。虽然在理论上快于穷举搜索,但加速幅度极为微小——仅比暴力破解快约 3 到 5 倍——在实践中毫无意义。biclique 攻击的本质是利用 meet-in-the-middle 思想在密钥空间中构建一种特殊的二部图(biclique)结构来减少计算量,但它需要的数据量和内存同样巨大。

相关密钥攻击

2009 年,Eli Biham 和 Nathan Keller,以及 Biryukov 和 Khovratovich,分别对 AES-256 发表了相关密钥(Related-Key)攻击,复杂度低至 2^99.5。这些攻击假设攻击者可以获取同一明文在多个具有已知关系(如异或差分)的密钥下的密文——这在大多数实际场景中是不现实的假设。然而,这些攻击揭示了 AES-256 密钥扩展算法的一个设计缺陷:相比 AES-128 的密钥扩展,AES-256 的密钥扩展在抵抗相关密钥攻击方面表现较弱。这一发现促使密码学界在后续设计中更加重视密钥扩展的安全性。

Square 攻击与积分密码分析

Square 攻击(也称积分密码分析, Integral Cryptanalysis)最初由 Rijndael 的设计者自己提出,专门针对 AES 类的面向字节的分组密码。其核心思想是跟踪一组精心选择的明文在多轮加密后某些字节位置上的”和”(XOR 和)。对于 4 轮 AES,如果固定 15 个字节而让剩余 1 个字节遍历所有 256 个值,则第 4 轮输出的每个字节的异或和为零——这一”零和”特性(或称”平衡”特性)可用于密钥恢复。Square 攻击对降轮 AES 效果显著(可攻击到 7 轮 AES-128),但对全轮 AES 无效。

代数攻击

AES 的 S-Box 可以用 GF(2) 上的稀疏多项式方程组来描述——事实上,整个 AES 加密过程可以表示为一个包含约 8000 个变量和 8000 个二次方程的方程组。这引发了一个自然的问题:能否通过求解这个方程组来恢复密钥?理论上,Groebner 基方法和 XL(eXtended Linearization)算法可以求解此类方程组,但迄今为止的所有尝试都表明,AES 产生的方程组规模使得这些方法的复杂度远超穷举搜索。代数攻击在概念上引人入胜,但在实际效果上仍然是一个未兑现的威胁。

安全余量

综合以上分析,AES-128 的安全余量可以这样理解:现有最好的攻击将 128 位密钥的有效安全性降低了不到 2 位(从 128 位到 126.1 位),这意味着 AES-128 的安全余量依然充裕。作为对比,SHA-1 在标准化时具有 80 位安全性,而到 2017 年已被 Google 的 SHAttered 攻击实际碰撞——SHA-1 的安全余量不足以抵御二十年的密码分析进步。AES-128 至今仍未暴露出类似的脆弱性,这在密码学史上是极为罕见的。

八、AES-NI 硬件加速

Intel AES-NI

2010 年,Intel 在 Westmere 微架构中首次引入了 AES 新指令集(AES New Instructions, AES-NI),包含六条专用指令:

每条 AESENC/AESDEC 指令在现代处理器上的延迟为 3-4 个时钟周期,吞吐量为每周期一条指令(在流水线满载时)。这意味着一次完整的 AES-128 加密(10 轮)仅需约 40 个时钟周期——相比纯软件实现的数百个周期,提速达一个数量级。

ARM 加密扩展

ARM 在 ARMv8-A 架构中引入了类似的加密扩展(Cryptography Extensions),包含 AESE(加密单轮)、AESD(解密单轮)、AESMC(MixColumns)和 AESIMC(InvMixColumns)指令。ARM 的实现将 SubBytes+ShiftRows 和 MixColumns 分为两条指令,这在流水线调度上提供了更大的灵活性。Apple M 系列和高通 Snapdragon 等主流 ARM 处理器均支持这些指令。

吞吐量对比

在 Intel Core i7(Skylake 及以后)上,使用 AES-NI 的 AES-128-GCM 加密吞吐量可达 5-8 GB/s(单核),而纯软件 T-Table 实现仅约 500-800 MB/s。在 ARM Cortex-A76 上,硬件加速后的吞吐量约为 2-4 GB/s,软件实现约 200-400 MB/s。硬件加速带来的不仅是速度提升,更重要的是常量时间保证——软件实现中的查表操作容易受到缓存时序侧信道攻击(Cache-Timing Side-Channel Attack),而硬件指令的执行时间与数据无关,从根本上消除了这一威胁。

GCM 与 PCLMULQDQ

AES 常与 Galois/Counter Mode(GCM)配合使用以提供认证加密(Authenticated Encryption)。GCM 的认证部分需要在 GF(2^128) 上执行无进位乘法(Carry-less Multiplication)。Intel 的 PCLMULQDQ 指令(以及 ARM 的 PMULL 指令)专门加速这一运算,使 AES-GCM 的认证开销几乎可以忽略不计。AES-NI 与 PCLMULQDQ 的组合使 AES-GCM 成为当今网络协议中性能最优的认证加密方案——TLS 1.3 默认使用的 AES-128-GCM 和 AES-256-GCM 正是得益于此。

九、AES 的历史地位与未来

二十五年的考验

AES 自 2001 年标准化以来已经过了二十五年以上的密码分析检验。在此期间,SHA-1 被碰撞攻击击溃,MD5 早已退役,RC4 因统计偏差被各大浏览器禁用,DES 和 3DES 先后被淘汰——而 AES 始终屹立不倒。全球顶尖的密码分析团队投入了大量人力物力试图攻破 AES,但迄今为止取得的最佳结果——biclique 攻击——仅将 AES-128 的有效安全性从 128 位降低到 126.1 位,在实际层面毫无影响。这种持久的安全性在对称密码学历史上是空前的。

AES 的成功不仅体现在安全性上,还体现在其无处不在的部署上。它被集成到 Intel、AMD 和 ARM 的处理器指令集中,内置于操作系统内核的加密子系统中,嵌入到从智能卡到工业控制器的各类硬件中。TLS、IPsec、SSH、WPA3、BitLocker、FileVault、dm-crypt——几乎所有主流安全协议和磁盘加密工具都以 AES 作为默认或首选的对称加密算法。

量子计算的影响

Grover 算法可以将对称密码的穷举搜索加速到平方根——对 AES-128 而言,量子计算机上的有效安全性从 128 位降至约 64 位。64 位安全性在经典计算中已经不够,因此 AES-128 在量子计算时代将不再安全。

然而,AES-256 在 Grover 算法下仍保有 128 位安全性——这在可预见的未来足以抵御量子攻击。因此,NIST 的后量子密码学标准化工作虽然聚焦于公钥算法(如 ML-KEM/Kyber 和 ML-DSA/Dilithium),但在对称密码方面的建议很简单:将密钥长度翻倍,从 AES-128 迁移到 AES-256。

需要指出的是,Grover 算法在实际量子计算机上的部署面临巨大的工程挑战。Grover 搜索需要顺序执行 O(2^64) 次量子 oracle 查询,无法有效并行化。即使量子门操作速度达到 GHz 量级,完成 2^64 次查询也需要约 600 年。因此,即使大规模容错量子计算机问世,AES-128 的实际安全性也不会在一夜之间崩塌——但出于保守原则,对长期数据保护的应用场景,迁移到 AES-256 是审慎之举。

广泛部署与标准地位

AES 的成功也为密码学标准化树立了典范。NIST 的公开竞赛模式——公开征集、全球同行评审、多轮筛选——后来被 SHA-3 竞赛和后量子密码标准化项目所沿用。这种模式确保了标准算法经受过最广泛的专家审查,极大地增强了公众对密码标准的信任。

展望未来,AES 在对称密码学中的统治地位预计还将持续数十年。轻量级密码学(Lightweight Cryptography)领域虽然涌现了 ASCON 等新标准,但它们的目标场景是极端资源受限的物联网设备,而非取代 AES 在通用计算中的地位。只要 AES-256 的安全性不出现根本性突破,它就将继续作为数字世界的基石——保护我们的通信、数据和隐私。

附录:代码实现

AES-128 SubBytes 与 MixColumns 核心操作(C 语言)

以下 C 代码展示了 AES S-Box 的代数构造和 MixColumns 的 GF(2^8) 矩阵运算:

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

/* ---------- GF(2^8) 算术 ---------- */

/* xtime: 在 GF(2^8) 中乘以 x(即 0x02)
 * 不可约多项式 m(x) = x^8 + x^4 + x^3 + x + 1 => 0x11B */
static uint8_t xtime(uint8_t a)
{
    return (a << 1) ^ ((a >> 7) * 0x1B);
}

/* GF(2^8) 乘法:通过 xtime 的"移位累加"实现 */
static uint8_t gf_mul(uint8_t a, uint8_t b)
{
    uint8_t result = 0;
    uint8_t hi;
    for (int i = 0; i < 8; i++) {
        if (b & 1)
            result ^= a;
        hi = a & 0x80;
        a <<= 1;
        if (hi)
            a ^= 0x1B;
        b >>= 1;
    }
    return result;
}

/* GF(2^8) 求逆:利用 a^{-1} = a^{254}(费马小定理推广)
 * 254 = 128 + 64 + 32 + 16 + 8 + 4 + 2,通过反复平方法计算 */
static uint8_t gf_inv(uint8_t a)
{
    if (a == 0) return 0;
    uint8_t a2   = gf_mul(a, a);        /* a^2   */
    uint8_t a4   = gf_mul(a2, a2);      /* a^4   */
    uint8_t a8   = gf_mul(a4, a4);      /* a^8   */
    uint8_t a16  = gf_mul(a8, a8);      /* a^16  */
    uint8_t a32  = gf_mul(a16, a16);    /* a^32  */
    uint8_t a64  = gf_mul(a32, a32);    /* a^64  */
    uint8_t a128 = gf_mul(a64, a64);    /* a^128 */
    return gf_mul(gf_mul(gf_mul(a128, a64), gf_mul(a32, a16)),
                  gf_mul(gf_mul(a8, a4), a2));
}

/* ---------- SubBytes ---------- */

/* S-Box 仿射变换 */
static uint8_t affine(uint8_t b)
{
    uint8_t result = 0;
    for (int i = 0; i < 8; i++) {
        int bit = ((b >> i) ^ (b >> ((i+4)%8)) ^ (b >> ((i+5)%8))
                 ^ (b >> ((i+6)%8)) ^ (b >> ((i+7)%8))) & 1;
        result |= (bit << i);
    }
    return result ^ 0x63;
}

/* 计算单个字节的 S-Box 输出 */
uint8_t sub_byte(uint8_t input)
{
    return affine(gf_inv(input));
}

/* 生成完整的 256 字节 S-Box 查找表 */
void generate_sbox(uint8_t sbox[256])
{
    for (int i = 0; i < 256; i++)
        sbox[i] = sub_byte((uint8_t)i);
}

/* ---------- MixColumns ---------- */

/* 对状态矩阵的单列(4 字节)执行 MixColumns
 * 矩阵: | 02 03 01 01 |
 *        | 01 02 03 01 |
 *        | 01 01 02 03 |
 *        | 03 01 01 02 |  */
void mix_column(uint8_t col[4])
{
    uint8_t a[4], b[4];
    for (int i = 0; i < 4; i++) {
        a[i] = col[i];
        b[i] = xtime(col[i]);  /* 乘以 0x02 */
    }
    col[0] = b[0] ^ b[1] ^ a[1] ^ a[2] ^ a[3]; /* 02*a0 + 03*a1 + a2 + a3 */
    col[1] = a[0] ^ b[1] ^ b[2] ^ a[2] ^ a[3]; /* a0 + 02*a1 + 03*a2 + a3 */
    col[2] = a[0] ^ a[1] ^ b[2] ^ b[3] ^ a[3]; /* a0 + a1 + 02*a2 + 03*a3 */
    col[3] = b[0] ^ a[0] ^ a[1] ^ a[2] ^ b[3]; /* 03*a0 + a1 + a2 + 02*a3 */
}

/* 对 4x4 状态矩阵执行 MixColumns(列优先存储) */
void mix_columns(uint8_t state[16])
{
    for (int c = 0; c < 4; c++)
        mix_column(state + 4 * c);
}

AES-NI 使用示例

以下代码片段展示了如何使用 Intel AES-NI 指令执行 AES-128 加密:

#include <wmmintrin.h>  /* AES-NI 内联函数 */

/* 使用 AES-NI 执行 AES-128 加密(单个 128 位分组)
 * round_keys: 预计算的 11 个轮密钥 */
__m128i aes128_encrypt_block(__m128i plaintext,
                             const __m128i round_keys[11])
{
    __m128i state = _mm_xor_si128(plaintext, round_keys[0]);

    for (int i = 1; i <= 9; i++)
        state = _mm_aesenc_si128(state, round_keys[i]);

    state = _mm_aesenclast_si128(state, round_keys[10]);
    return state;
}

密码学百科系列 · 第 07 篇

← 上一篇:分组密码原理 | 系列目录 | 下一篇:分组密码工作模式


By .