Caloogy 文章
English

AES 里的有限域:S 盒其实是在 F28\mathbb{F}_{2^8} 里求逆

日期
2025 年 10 月 29 日
系列
密码学
阅读
4 分钟
主题
AESFinite fieldGF(256)S-box

一句话版本

AES 把每个字节看成有限域 F28=F2[x]/(x8+x4+x3+x+1)\mathbb{F}_{2^8} = \mathbb{F}_2[x]/(x^8+x^4+x^3+x+1) 里的一个元素。它的 S 盒就是"求乘法逆元,再做一个仿射变换";列混合(MixColumns)就是一次多项式乘法。 用代数结构而不是随手画的表格来设计,能让 AES 对已知攻击给出可证明的抵抗力。

全文分三层:

  1. 通俗理解:混淆与扩散,AES 每一轮在干什么
  2. 实际用处:AES 无处不在,以及为什么它不怕量子计算机
  3. 数学原理:F28\mathbb{F}_{2^8} 的构造、S 盒、MixColumns,附 FIPS 197 标准里的手算例子

第一层:通俗理解

1.1 对称加密要做两件事

香农在 1949 年提出,好的分组密码要同时做到:

AES 就是一台把这两件事反复做 10 到 14 轮的机器。

1.2 AES 的一轮

AES 把 16 字节的数据排成 4×44 \times 4 的方阵,每一轮做四步:

步骤 做什么 作用
SubBytes 每个字节查 S 盒替换 混淆:唯一的非线性步骤
ShiftRows 第 ii 行循环左移 ii 格 扩散:把字节打散到不同列
MixColumns 每一列乘一个固定矩阵 扩散:一列中每个字节影响全列
AddRoundKey 和本轮子密钥异或 引入密钥

做两轮之后,任意一个输入字节就已经影响到全部 16 个输出字节。

1.3 为什么要用有限域

字节有 256 种取值。我们想对字节做"乘法"和"除法",结果还得是一个字节。普通整数乘法会溢出,模 256 又不是域(2×128≡02 \times 128 \equiv 0,有零因子,不能除)。

办法是把字节看成系数为 0 或 1 的多项式,在模一个不可约多项式的意义下做运算。这样 256 个字节恰好构成一个域:能加减乘除,每个非零元都有逆元。


第二层:实际用处

AES 是 2001 年美国 NIST 通过公开竞赛选出的标准(原名 Rijndael,两位比利时密码学家设计)。今天它几乎无处不在:

硬件加速:Intel、AMD 的 AES-NI 指令和 ARM 的加密扩展直接在芯片里实现一轮 AES,速度可达每秒数 GB。

量子计算机:AES 没有 RSA 那样的代数陷门,Shor 算法用不上;Grover 算法只能把暴力搜索从 21282^{128} 降到约 2642^{64}。所以用 AES-256 就足够应对,详见《后量子密码》。

真正的风险在实现

AES 本身二十多年来没有实际可行的攻击。出事的都是用法:ECB 模式(相同明文块得到相同密文块,"加密后企鹅图依然清晰可见")、GCM 模式重复使用 nonce、查表实现带来的缓存时间侧信道。


第三层:数学原理

3.1 构造 F28\mathbb{F}_{2^8}

取 F2={0,1}\mathbb{F}_2 = \{0, 1\}(加法是异或)。多项式

m(x)=x8+x4+x3+x+1m(x) = x^8 + x^4 + x^3 + x + 1

在 F2\mathbb{F}_2 上不可约,所以 (m(x))(m(x)) 是 F2[x]\mathbb{F}_2[x] 的极大理想,商环

F28=F2[x]/(m(x))\mathbb{F}_{2^8} = \mathbb{F}_2[x] / (m(x))

是一个有 28=2562^8 = 256 个元素的域(抽象代数:F[x]/(f)F[x]/(f) 是域当且仅当 ff 不可约)。

一个字节 b7b6⋯b0b_7 b_6 \cdots b_0 对应多项式 b7x7+⋯+b1x+b0b_7 x^7 + \cdots + b_1 x + b_0。例如 0x57 =010101112= 01010111_2 对应 x6+x4+x2+x+1x^6 + x^4 + x^2 + x + 1。

3.2 手算例子:乘法

{57} · {83} = {C1}(FIPS 197 原文例子)

{57} =x6+x4+x2+x+1= x^6 + x^4 + x^2 + x + 1,{83} =x7+x+1= x^7 + x + 1。

先做普通多项式乘法(系数模 2): (x6+x4+x2+x+1)(x7+x+1)=x13+x11+x9+x8+x6+x5+x4+x3+1.(x^6 + x^4 + x^2 + x + 1)(x^7 + x + 1) = x^{13} + x^{11} + x^9 + x^8 + x^6 + x^5 + x^4 + x^3 + 1.

再模 m(x)m(x):用 x8≡x4+x3+x+1x^8 \equiv x^4 + x^3 + x + 1 反复降次,最后得到 x7+x6+1=110000012=C1.x^7 + x^6 + 1 = 11000001_2 = \texttt{C1}.

(已用 Python 验算。)

3.3 S 盒 = 求逆 + 仿射变换

对每个字节 aa:

  1. 求逆:b=a−1b = a^{-1} in F28\mathbb{F}_{2^8}(规定 0−1=00^{-1} = 0);
  2. 仿射变换:把 bb 看成 F28\mathbb{F}_2^8 中的向量,算 s=Mb⊕63s = Mb \oplus \texttt{63},MM 是一个固定的 8×88 \times 8 循环矩阵: si=bi⊕bi+4⊕bi+5⊕bi+6⊕bi+7⊕ci(下标模 8, c=63).s_i = b_i \oplus b_{i+4} \oplus b_{i+5} \oplus b_{i+6} \oplus b_{i+7} \oplus c_i \quad (\text{下标模 } 8,\ c = \texttt{63}).

S(53) = ED(FIPS 197 原文例子)

  • 逆元:{53} 的逆是 {CA},可以验证 {53}·{CA} = {01}
  • 仿射变换后得到 {ED}

另外 S(00) = 63:00 求"逆"仍是 00,只剩下常数 63。(已用 Python 验算。)

为什么用求逆:x↦x−1x \mapsto x^{-1} 在 F28\mathbb{F}_{2^8} 上是高度非线性的。Nyberg(1993)证明了这类映射对差分攻击和线性攻击有接近最优的抵抗力:任意输入差分最多以 4/2564/256 的概率产生某个特定输出差分。

为什么还要加仿射变换:纯求逆的代数结构太"干净",可能被代数攻击利用(比如插值攻击),而且 00 和 11 是不动点。仿射变换打乱这种结构,并保证 S 盒没有不动点和"反不动点"。

3.4 MixColumns = 模 x4+1x^4 + 1 的多项式乘法

每一列的 4 个字节看成系数在 F28\mathbb{F}_{2^8} 中的三次多项式 a(y)=a3y3+a2y2+a1y+a0a(y) = a_3 y^3 + a_2 y^2 + a_1 y + a_0,然后

a(y)↦c(y)⋅a(y) mod (y4+1),c(y)=03 y3+01 y2+01 y+02.a(y) \mapsto c(y) \cdot a(y) \bmod (y^4 + 1),\qquad c(y) = \texttt{03}\,y^3 + \texttt{01}\,y^2 + \texttt{01}\,y + \texttt{02}.

因为 y4≡1y^4 \equiv 1,这是一个循环卷积,写成矩阵就是

(c0c1c2c3)=(02030101010203010101020303010102)(a0a1a2a3).\begin{pmatrix} c_0 \\ c_1 \\ c_2 \\ c_3 \end{pmatrix} = \begin{pmatrix} 02 & 03 & 01 & 01 \\ 01 & 02 & 03 & 01 \\ 01 & 01 & 02 & 03 \\ 03 & 01 & 01 & 02 \end{pmatrix} \begin{pmatrix} a_0 \\ a_1 \\ a_2 \\ a_3 \end{pmatrix}.

注意 y4+1=(y+1)4y^4 + 1 = (y+1)^4 在特征 2 下可约,所以这个商环不是域,但 c(y)c(y) 与 y4+1y^4 + 1 互素,因此可逆,解密时乘它的逆 c−1(y)c^{-1}(y) 即可。

这个矩阵还是一个 MDS 矩阵(和 Reed–Solomon 纠错码里的 MDS 码是同一个概念):输入和输出中非零字节的总数至少是 5。这保证了扩散的速度,也是 AES 设计者"宽轨迹策略"的核心。


总结

层次 要点
直觉 混淆靠 S 盒,扩散靠 ShiftRows 和 MixColumns,反复十几轮
代数 字节 == F2[x]/(m(x))\mathbb{F}_2[x]/(m(x)) 的元素;不可约多项式给出域
设计 S 盒 == 求逆 + 仿射,抵抗差分 / 线性攻击;MixColumns 是 MDS
工程 算法本身很稳;风险在模式误用和侧信道;AES-256 可抗量子