LED Block Cipher — 算法原理、硬件与软件实现

原著: Jian Guo, Thomas Peyrin, Axel Poschmann, Matt Robshaw(CHES 2011)
本项目: LED-64 / LED-80 / LED-128 的 Verilog HDL 实现与 Python 参考实现,含完整加解密验证。
**开源链接:**https://github.com/inwpu/LED-Block-Cipher/LED Block Cipher

一、背景与算法参数

随着 RFID 标签、无线传感器和嵌入式控制器的大规模普及,如何在极端受限的硬件资源下提供密码保护成为一个现实工程问题。这类设备的可用门电路往往不足 2000 GE,而传统 AES 的最小实现已需要约 3400 GE,其密钥扩展算法本身就占去相当比例。

LED(Light Encryption Device)的核心设计取舍是:完全取消密钥扩展算法,让原始密钥以周期性复用的方式直接参与每一步的密钥异或。这不仅将硬件面积压缩到 966 GE(LED-64),更带来了关键的安全分析优势——由于轮函数结构高度规整,可以在单密钥和相关密钥两种攻击模型下都推导出精确的安全界。

轮函数的设计延续 AES 的 SPN(置换-替换网络)框架,以 PRESENT 密码中经过广泛验证的 S 盒作为非线性层,并采用专为串行硬件实现优化的 MixColumnsSerial 结构。

下表列出三个变种的参数对比。“步”(step)是 LED 特有的结构单元,每步包含 4 轮内部变换和 1 次密钥注入,是安全性证明的基本分析单位。密钥注入次数 = 步数 + 1,因为加密开始前有一次初始注入,每步结束后有一次注入。

版本 块大小 密钥长度 步数 s s s 总轮数 密钥注入次数 运算域 硬件面积(可变密钥)
LED-64 64 bit 64 bit 8 32 9 GF ( 2 4 ) \text{GF}(2^4) GF(24) 966 GE
LED-80 64 bit 80 bit 12 48 13 GF ( 2 4 ) \text{GF}(2^4) GF(24) 1,040 GE(估算)
LED-128 64 bit 128 bit 12 48 13 GF ( 2 4 ) \text{GF}(2^4) GF(24) 1,265 GE

面积数据来自原论文 Table 2(Flexible Keys 列,UMC 0.18 μm 工艺)。LED-80 标注估算值(*)。LED-64 若使用硬连线密钥可降至 688 GE,LED-128 可降至 700 GE。

本项目基于官方规范(ePrint 2012/600)及 Jian Guo 于 2014 年公开的字节级参考实现 led-bytes.c,完成了:

  • Verilog HDL:LED-64、LED-80、LED-128 各提供时序逻辑(每时钟周期完成一轮)和组合逻辑(单周期)两种架构,均支持加解密;
  • Pythonled.py 单文件实现三个变种的完整加解密,90 项测试全部通过;
  • 交叉验证:所有实现的输出均与官方 C 参考逐字节比对一致。

二、算法结构与轮操作

2.1 状态矩阵

LED 将 64 位数据块解释为一个 4 行 × 4 列的 nibble 矩阵,每个格子存储一个 GF ( 2 4 ) \text{GF}(2^4) GF(24) 元素(4 比特)。与 AES 按列优先填充不同,LED 按行优先、高位优先的顺序将明文字节填入,这是面向硬件串行实现的设计选择——串行电路每次只处理一个 nibble,行优先与高位先出的比特流顺序一致。

图 1 状态矩阵布局(64 位明文 → 4×4 nibble 矩阵)

格子 a [ i ] [ j ] a[i][j] a[i][j] 对应 64 位字的位域:

a [ i ] [ j ]    ⟷    bits [ 63 − 4 ( 4 i + j )    :    60 − 4 ( 4 i + j ) ] a[i][j] \;\longleftrightarrow\; \text{bits}\bigl[63-4(4i+j)\;:\;60-4(4i+j)\bigr] a[i][j]bits[634(4i+j):604(4i+j)]

例如 a [ 0 ] [ 0 ] a[0][0] a[0][0] 取最高 4 位(bits[63:60]), a [ 3 ] [ 3 ] a[3][3] a[3][3] 取最低 4 位(bits[3:0])。子密钥 SK n \text{SK}^n SKn 以完全相同的 4 × 4 4\times4 4×4 格式组织,其格位 ( i , j ) (i,j) (i,j) 的值由密钥 nibble 公式 k n [ ( 4 i + j + n × 16 )   m o d   ( key_bits / 4 ) ] kn[(4i+j+n\times16)\bmod(\text{key\_bits}/4)] kn[(4i+j+n×16)mod(key_bits/4)] 确定(详见第三章)。

2.2 加密整体架构

加密过程由 s s s 个**步(step)**串联而成,每步开始前先对状态 XOR 一个子密钥,随后执行 4 轮内部变换;所有步结束后再做最后一次密钥注入(收尾 XOR)。图 2 展示了这一"密钥—步—密钥—步—…—密钥"的交替结构。

图 2 LED 加密整体架构

图中每个 ⊕ \oplus 节点表示一次子密钥 XOR(AddRoundKey)。 SK 0 \text{SK}^0 SK0 注入 step 0 之前 SK 1 \text{SK}^1 SK1 注入 step 0 之后(step 1 之前),以此类推; SK s \text{SK}^s SKs 是最后一次注入(step s − 1 s-1 s1 之后),也称"output whitening"。没有这次最终 XOR,最后一个 MixColumnsSerial 的输出就直接暴露,攻击者可以跳过最后一轮分析整个结构,因此收尾 XOR 是构造完整性的必要组成。

每步(step)由 4 轮(round)顺序执行,每轮依次完成以下 4 个操作(图 3):

图 3 一轮(round)的内部操作顺序(每步重复 4 轮,轮编号 r 递增)

四个操作的排列顺序遵循 SPN 设计原则:AddConstants 在 SubCells 之前注入,避免 S 盒输入具有可预测的对称性;SubCells 提供非线性(混淆);ShiftRows + MixColumnsSerial 共同提供扩散(雪崩)。理论上,经过 4 轮(一步)之后,每个输出 nibble 依赖所有 16 个输入 nibble(全扩散),这是 LED 步结构作为安全分析基本单元的原因。

2.3 AddConstants — 轮常数注入

每轮注入的作用有两个:一是在每轮引入轮差异打破状态的对称性,防止出现固定点或滑动攻击;二是将密钥长度信息编码进状态,使 LED-64 / LED-80 / LED-128 三个变种即使面对相同的明文和密钥也产生完全不同的密文(抵御跨变种的通用攻击)。

每轮使用一个 6 位常数 RC [ r ] \text{RC}[r] RC[r],由 6 位仿射 LFSR 生成,递推规则为:

r c 0 ′ = r c 5 ⊕ r c 4 ⊕ 1 \mathrm{rc}_{0}' = \mathrm{rc}_5 \oplus \mathrm{rc}_4 \oplus 1 rc0=rc5rc41

即寄存器左移一位,新的最低位由旧 r c 5 \mathrm{rc}_5 rc5 r c 4 \mathrm{rc}_4 rc4 和常数 1 异或得到。LFSR 从全零初始化,每轮使用前更新一次,故 RC [ 0 ] = 0x01 \text{RC}[0]=\texttt{0x01} RC[0]=0x01。序列前 8 项验证如下:

初始:000001 = 0x01
步1:新b0 = 0⊕0⊕1=1 → 000011 = 0x03  ✓
步2:新b0 = 0⊕0⊕1=1 → 000111 = 0x07  ✓
步3:新b0 = 0⊕0⊕1=1 → 001111 = 0x0F  ✓
步4:新b0 = 0⊕0⊕1=1 → 011111 = 0x1F  ✓
步5:新b0 = 0⊕1⊕1=0 → 111110 = 0x3E  ✓
步6:新b0 = 1⊕1⊕1=1 → 111101 = 0x3D  ✓
步7:新b0 = 1⊕1⊕1=1 → 111011 = 0x3B  ✓

全部 48 个 RC 值(LED-80 / LED-128 使用全部 48 个,LED-64 使用前 32 个):

RC[0..47] = {
  01 03 07 0F 1F 3E 3D 3B 37 2F 1E 3C 39 33 27 0E
  1D 3A 35 2B 16 2C 18 30 21 02 05 0B 17 2E 1C 38
  31 23 06 0D 1B 36 2D 1A 34 29 12 24 08 11 22 04
}

注入只作用于状态矩阵的第 0 列和第 1 列,第 2、3 列完全不变。Col 0 注入"行索引与密钥长度编码的组合"固定常数(所有轮相同),Col 1 注入 RC [ r ] \text{RC}[r] RC[r] 的高 3 位和低 3 位(每轮不同):

  • Col 0:设 $ks_hi = (key_bits >> 4) & 0xFks_lo = key_bits & 0xF`,则行 i i i 注入 i ⊕ ks_hi i \oplus \text{ks\_hi} iks_hi i = 0 , 1 i=0,1 i=0,1)或 i ⊕ ks_lo i \oplus \text{ks\_lo} iks_lo i = 2 , 3 i=2,3 i=2,3)。该值在同一 LED 变种内的所有轮中固定不变,在不同变种间取值不同,以此让三个变种产生不同的常数域。
  • Col 1RC_hi = (RC[r] >> 3) & 0x7(RC 的高 3 位)注入偶数行(行 0、行 2);RC_lo = RC[r] & 0x7(RC 的低 3 位)注入奇数行(行 1、行 3)。每轮 RC [ r ] \text{RC}[r] RC[r] 不同,确保每轮的注入图案唯一。

图 4 AddConstants 注入位置与值\oplus$ 表示将该值异或叠加到格子的现有值上

三个变种在 Col 0 注入的固定值如下表。注意 ( 2 , 0 ) (2,0) (2,0) ( 3 , 0 ) (3,0) (3,0) 对所有变种均为 2 和 3:这是因为 64、80、128 这三个数值的低 4 位( ks_lo \text{ks\_lo} ks_lo)均为 0,导致 2 ⊕ ks_lo = 2 2\oplus\text{ks\_lo}=2 2ks_lo=2 3 ⊕ ks_lo = 3 3\oplus\text{ks\_lo}=3 3ks_lo=3

格位 (row, col) LED-64 ( key_bits = 0x40 \text{key\_bits}=\texttt{0x40} key_bits=0x40) LED-80 ( key_bits = 0x50 \text{key\_bits}=\texttt{0x50} key_bits=0x50) LED-128 ( key_bits = 0x80 \text{key\_bits}=\texttt{0x80} key_bits=0x80)
(0, 0) ks_hi = 4 \text{ks\_hi} = 4 ks_hi=4 ks_hi = 5 \text{ks\_hi} = 5 ks_hi=5 ks_hi = 8 \text{ks\_hi} = 8 ks_hi=8
(1, 0) 1 ⊕ ks_hi = 5 1\oplus\text{ks\_hi} = 5 1ks_hi=5 1 ⊕ ks_hi = 4 1\oplus\text{ks\_hi} = 4 1ks_hi=4 1 ⊕ ks_hi = 9 1\oplus\text{ks\_hi} = 9 1ks_hi=9
(2, 0) 2 ⊕ ks_lo = 2 2\oplus\text{ks\_lo} = 2 2ks_lo=2 2 ⊕ ks_lo = 2 2\oplus\text{ks\_lo} = 2 2ks_lo=2 2 ⊕ ks_lo = 2 2\oplus\text{ks\_lo} = 2 2ks_lo=2
(3, 0) 3 ⊕ ks_lo = 3 3\oplus\text{ks\_lo} = 3 3ks_lo=3 3 ⊕ ks_lo = 3 3\oplus\text{ks\_lo} = 3 3ks_lo=3 3 ⊕ ks_lo = 3 3\oplus\text{ks\_lo} = 3 3ks_lo=3

2.4 SubCells — 非线性替换

SubCells 对状态矩阵中的全部 16 个 nibble 独立并行地做 S 盒查表替换,是整个轮函数中唯一的非线性操作(混淆层)。LED 复用了 PRESENT 密码的 S 盒,该 S 盒基于 GF ( 2 4 ) \text{GF}(2^4) GF(24) 上的仿射变换构造,最大差分概率为 2 − 2 2^{-2} 22,最大线性近似概率为 2 − 2 2^{-2} 22(均为 4 位 S 盒的理论最优值之一)。

正向 S 盒(加密用, S [ x ] S[x] S[x]):

x x x 0 1 2 3 4 5 6 7 8 9 A B C D E F
S [ x ] S[x] S[x] C 5 6 B 9 0 A D 3 E F 8 4 7 1 2

逆向 S − 1 S^{-1} S1 盒(解密用,查逆映射):

x x x 0 1 2 3 4 5 6 7 8 9 A B C D E F
S − 1 [ x ] S^{-1}[x] S1[x] 5 E F 8 C 1 2 D B 4 6 3 0 7 9 A

可以验证 S − 1 [ S [ x ] ] = x S^{-1}[S[x]] = x S1[S[x]]=x,例如 S [ 0 ] = C S[0]=\texttt{C} S[0]=C S − 1 [ C ] = 0 S^{-1}[\texttt{C}]=0 S1[C]=0 ✓; S [ 5 ] = 0 S[5]=0 S[5]=0 S − 1 [ 0 ] = 5 S^{-1}[0]=5 S1[0]=5 ✓。SubCells 在解密时直接替换为查 S − 1 S^{-1} S1 盒,操作结构完全对称,硬件实现可共用查表逻辑。

2.5 ShiftRows — 行移位

ShiftRows 将第 i i i 行向左循环移动 i i i 个 nibble i = 0 , 1 , 2 , 3 i = 0,1,2,3 i=0,1,2,3),第 0 行不动。该操作本身不增加混淆,其目的是为后续的 MixColumnsSerial 准备跨行的扩散条件:ShiftRows 之后,同一列中来自不同原始行的 nibble 被重新排布,MixColumnsSerial 对列做矩阵乘法时就能将多个行的信息混合,实现全状态的雪崩效应。

图 5 ShiftRows 变换:每行逐一展示移位前后的 nibble 排列

ShiftRows 的等价公式为:

a ′ [ r ] [ c ] = a [ r ] [ ( c + r )   m o d   4 ] (行  r  左移  r  位) a'[r][c] = a[r][(c+r) \bmod 4] \qquad \text{(行 } r \text{ 左移 } r \text{ 位)} a[r][c]=a[r][(c+r)mod4](行 r 左移 r 位)

解密逆操作 InvShiftRows:

a [ r ] [ c ] = a ′ [ r ] [ ( c − r + 4 )   m o d   4 ] (行  r  右移  r  位) a[r][c] = a'[r][(c-r+4) \bmod 4] \qquad \text{(行 } r \text{ 右移 } r \text{ 位)} a[r][c]=a[r][(cr+4)mod4](行 r 右移 r 位)

以 Row 1 为例:原来位于 ( 1 , 0 ) (1,0) (1,0) 的元素 a [ 1 ] [ 0 ] a[1][0] a[1][0] 左移 1 位后落到列位置 3,即 a ′ [ 1 ] [ 3 ] = a [ 1 ] [ 0 ] a'[1][3] = a[1][0] a[1][3]=a[1][0]。MixColumnsSerial 作用于列 3 时,会将来自行 0、行 1(移位后)、行 2(移位后)、行 3(移位后)的四个元素全部混合,这正是跨行扩散的机制。

2.6 MixColumnsSerial 与 GF ( 2 4 ) \text{GF}(2^4) GF(24) 域运算

MixColumnsSerial 对状态矩阵的每一列独立执行一次 4 × 4 4\times4 4×4 矩阵乘法:将该列的 4 个 nibble 视为 GF ( 2 4 ) \text{GF}(2^4) GF(24) 上的列向量,乘以 MDS 矩阵 M M M,结果写回该列。图 6 展示了这一过程。
图 6 MixColumnsSerial 矩阵乘法(

矩阵 M M M 与串行步骤矩阵 A A A 的关系(原论文式 ( A ) 4 = M (A)^4 = M (A)4=M):

A = ( 0 1 0 0 0 0 1 0 0 0 0 1 4 1 2 2 ) , M = A 4 = ( 4 1 2 2 8 6 5 6 B E A 9 2 2 F B ) A = \begin{pmatrix} 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ 4 & 1 & 2 & 2 \end{pmatrix}, \qquad M = A^4 = \begin{pmatrix} 4 & 1 & 2 & 2 \\ 8 & 6 & 5 & 6 \\ \mathrm{B} & \mathrm{E} & \mathrm{A} & 9 \\ 2 & 2 & \mathrm{F} & \mathrm{B} \end{pmatrix} A= 0004100101020012 ,M=A4= 48B216E225AF269B

(所有元素均在 GF ( 2 4 ) \text{GF}(2^4) GF(24) 中,以十六进制表示)

矩阵 A A A 对应单次串行移位混合步骤——硬件上每拍将当前列向量 v \mathbf{v} v 更新为 A v A\mathbf{v} Av,连续执行 4 拍即完成 M a M\mathbf{a} Ma,这是 MixColumnsSerial 命名的由来,也使其在逐 nibble 串行硬件中极易实现。展开后的矩阵方程为:

b 0 = 4 ⋅ a 0 ⊕ 1 ⋅ a 1 ⊕ 2 ⋅ a 2 ⊕ 2 ⋅ a 3 b 1 = 8 ⋅ a 0 ⊕ 6 ⋅ a 1 ⊕ 5 ⋅ a 2 ⊕ 6 ⋅ a 3 b 2 = B ⋅ a 0 ⊕ E ⋅ a 1 ⊕ A ⋅ a 2 ⊕ 9 ⋅ a 3 b 3 = 2 ⋅ a 0 ⊕ 2 ⋅ a 1 ⊕ F ⋅ a 2 ⊕ B ⋅ a 3 \begin{aligned} b_0 &= 4\cdot a_0 \oplus 1\cdot a_1 \oplus 2\cdot a_2 \oplus 2\cdot a_3 \\ b_1 &= 8\cdot a_0 \oplus 6\cdot a_1 \oplus 5\cdot a_2 \oplus 6\cdot a_3 \\ b_2 &= \mathrm{B}\cdot a_0 \oplus \mathrm{E}\cdot a_1 \oplus \mathrm{A}\cdot a_2 \oplus 9\cdot a_3 \\ b_3 &= 2\cdot a_0 \oplus 2\cdot a_1 \oplus \mathrm{F}\cdot a_2 \oplus \mathrm{B}\cdot a_3 \end{aligned} b0b1b2b3=4a01a12a22a3=8a06a15a26a3=Ba0Ea1Aa29a3=2a02a1Fa2Ba3

M M M 是一个 MDS(最大距离可分)矩阵,分支数(Branch Number) = 5 = 5 =5。分支数的含义:对于任意非零输入差分向量,输入与输出中被激活(非零差分)的 nibble 数之和至少为 5。换言之,若输入只有 1 个 nibble 发生变化,输出中至少 4 个 nibble 会发生变化,实现强扩散。

GF ( 2 4 ) \text{GF}(2^4) GF(24) 域运算:矩阵系数的乘法运算在有限域 GF ( 2 4 ) \text{GF}(2^4) GF(24) 中进行,该域由不可约多项式 p ( x ) = x 4 + x + 1 p(x) = x^4 + x + 1 p(x)=x4+x+1 定义。 GF ( 2 4 ) \text{GF}(2^4) GF(24) 加法即按位异或( ⊕ \oplus ),乘法是多项式乘法后模 p ( x ) p(x) p(x) 归约,因此 x 4 ≡ x + 1 ( m o d p ( x ) ) x^4 \equiv x+1 \pmod{p(x)} x4x+1(modp(x))。实现上采用"移位-累加"循环:

gf4_mul(a, b):
    r ← 0
    重复 4 次:
        若 b[0] = 1 :r ← r ⊕ a           ← 当前最低位为 1 时累加
        b ← b >> 1
        若 a[3] = 1 :a ← (a << 1) ⊕ 0x3  ← 溢出:x⁴ ≡ x+1,XOR 归约多项式余项 (x+1) = 0x3
        否则        :a ← a << 1
    返回 r(取低4位)

0x3 对应多项式 x + 1 x+1 x+1,即 p ( x ) = x 4 + x + 1 p(x)=x^4+x+1 p(x)=x4+x+1 去掉最高次 x 4 x^4 x4 后的余项。每次 a a a 溢出( a [ 3 ] = 1 a[3]=1 a[3]=1),左移后产生 x 4 x^4 x4,按 x 4 ≡ x + 1 x^4 \equiv x+1 x4x+1 替换,相当于 XOR 0x3 并丢弃溢出位。

三、密钥方案与解密

3.1 统一密钥取用公式

LED 没有独立的密钥扩展算法。第 n n n 步的子密钥 SK n \text{SK}^n SKn 的格位 ( i , j ) (i,j) (i,j) 处的 nibble,直接按下式从原始密钥取出:

SK n [ i ] [ j ] = k n  ⁣ [ ( 4 i + j + 16 n )   m o d   key_bits 4 ] \text{SK}^n[i][j] = kn\!\left[(4i + j + 16n) \bmod \frac{\text{key\_bits}}{4}\right] SKn[i][j]=kn[(4i+j+16n)mod4key_bits]

其中 k n [   ] kn[\,] kn[] 是原始密钥按高位优先展开的 nibble 数组(共 key_bits / 4 \text{key\_bits}/4 key_bits/4 个元素)。 ( 16 n )   m o d   ( key_bits / 4 ) (16n) \bmod (\text{key\_bits}/4) (16n)mod(key_bits/4) 给出了第 n n n 步的"起始偏移",三种密钥长度对应不同的取用规律:

变种 key nibbles 偏移公式 规律
LED-64 16 ( 16 n )   m o d   16 = 0 (16n) \bmod 16 = 0 (16n)mod16=0 所有步偏移为 0,子密钥永远等于原始密钥
LED-80 20 ( 16 n )   m o d   20 (16n) \bmod 20 (16n)mod20 每 5 步一周期,窗口在 20 个 nibble 上循环滑动
LED-128 32 ( 16 n )   m o d   32 (16n) \bmod 32 (16n)mod32 0 与 16 严格交替,对应高/低 64 位两个子密钥

3.2 LED-64 密钥方案

密钥共 16 个 nibble。步偏移 ( 16 n )   m o d   16 = 0 (16n) \bmod 16 = 0 (16n)mod16=0 对所有步恒成立,因此全部 9 个子密钥( SK 0 … SK 8 \text{SK}^0\ldots\text{SK}^8 SK0SK8)均等于原始密钥 K K K 本身。

图 7 LED-64 密钥方案:单一密钥重复注入 9 次

子密钥完全相同表面上看似削弱了安全性,但 LED 的安全论证依赖的是轮函数本身的扩散强度:8 步(32 轮)的 AC+SC+SR+MCS 链提供了足够的混淆和扩散,即使攻击者知道密钥被重复使用,也无法利用这一规律在限定计算量内进行区分攻击。原论文证明,任意差分路径在 LED-64 单密钥场景下至少激活 200 个活跃 S 盒,差分路径概率上界 2 − 400 2^{-400} 2400,这也是 LED-64 需要 8 步而非更少步数的原因。

3.3 LED-80 密钥方案

密钥共 20 个 nibble。步偏移序列 { ( 16 n )   m o d   20 } = { 0 , 16 , 12 , 8 , 4 , 0 , 16 , … } \{(16n) \bmod 20\} = \{0, 16, 12, 8, 4, 0, 16, \ldots\} {(16n)mod20}={0,16,12,8,4,0,16,}每 5 步一周期,子密钥窗口在 20 个 nibble 上循环滑动。LED-80 的 12 步 + 1 次末尾注入共 13 个子密钥( SK 0 … SK 12 \text{SK}^0\ldots\text{SK}^{12} SK0SK12),在 5 种取法( P 0 … P 4 P_0\ldots P_4 P0P4)中完成 2 个完整周期后再取前 3 种。

图 8 LED-80 密钥方案:5步一周期,共 13 个子密钥

每种取法( P 0 … P 4 P_0\ldots P_4 P0P4)是密钥 20 个 nibble 的一个长度为 16 的环形窗口,五个窗口覆盖不同的起始位置,相邻取法之间偏移 4 个 nibble。这种滑动设计引入了有限的子密钥多样性,而不需要任何密钥扩展电路。

3.4 LED-128 密钥方案

密钥共 32 个 nibble。步偏移 ( 16 n )   m o d   32 (16n) \bmod 32 (16n)mod32 0 0 0(偶数步)和 16 16 16(奇数步)之间严格交替,自然产生两个固定子密钥: K 0 K^0 K0(nibbles k [ 0 … 15 ] k[0\ldots15] k[015],对应 key [ 127 : 64 ] \text{key}[127:64] key[127:64])和 K 1 K^1 K1(nibbles k [ 16 … 31 ] k[16\ldots31] k[1631],对应 key [ 63 : 0 ] \text{key}[63:0] key[63:0])。

图 9 LED-128 密钥方案:共 13 个子密钥

LED-128 的安全设计意图:由于初始注入( SK 0 \text{SK}^0 SK0)和末尾注入( SK 12 \text{SK}^{12} SK12)均使用 K 0 K^0 K0,在相关密钥攻击场景下,即使攻击者完全控制 K 0 K^0 K0,他也只能"剥离"头尾共两次 K 0 K^0 K0 注入。中间仍有 11 次注入( SK 1 … SK 11 \text{SK}^1\ldots\text{SK}^{11} SK1SK11)完整地交替引入 K 0 K^0 K0 K 1 K^1 K1,相关密钥差分路径无法贯穿整个密码,原论文证明相关密钥场景下至少激活 150 个活跃 S 盒,差分路径概率上界 2 − 300 2^{-300} 2300

3.5 解密流程

解密是加密的严格逆过程:使用完全相同的密钥方案,但步索引逆序,四个轮操作各自替换为其逆。

图 10 LED 解密整体架构

对比加密(图 2)与解密(图 10)可以看出:两者的密钥注入位置完全对称。加密中 SK n \text{SK}^n SKn 注入在 step n n n 之前;解密中 SK n \text{SK}^n SKn 注入在 inv-step n n n 之后(即处理完 inv-step n n n 后才 XOR SK n \text{SK}^n SKn,然后进入 inv-step n − 1 n-1 n1)。这使得每次 XOR 都精确地撤销了加密时对应的 XOR。

图 11 一个 inv-round 的内部操作顺序(每个 inv-step 包含 4 个 inv-round,轮编号逆向递减)

解密轮函数顺序 InvMCS → InvSR → InvSC → AC 是加密顺序 AC → SC → SR → MCS 的严格逆。由于 XOR(AddConstants)是自逆运算( a ⊕ c ⊕ c = a a \oplus c \oplus c = a acc=a),解密时不需要单独的"逆 AddConstants"——只需用相同 RC [ r ] \text{RC}[r] RC[r] 再 XOR 一次即可撤销。需要注意的是,解密时 AddConstants 放在最后(对应加密时它在最前),轮编号从大到小递减(最后一轮先处理)。

四、实现与验证

本项目的文件结构如下:

LED/
├── README.md                          ← 本文件
├── 2012-600.pdf                       ← 官方论文(Guo et al., CHES 2011)
├── led-cipher.pdf / led-full.pdf      ← 规范文档
│
├── Reference-LED-ePrint/              ← 官方 C 参考实现
│   ├── led-bytes.c                    ← 核心加密函数(Jian Guo, 2014)
│   ├── led-bytes.h
│   ├── gen_vectors.c
│   └── LED2-byte-TestVectors.txt      ← 官方规范测试向量(27 条)
│
├── LED-v2-opt/                        ← 官方优化版 C 实现
│
├── python/                            ← Python 参考实现
│   ├── led.py                         ← LED-64/80/128 加解密核心
│   ├── example.py                     ← 使用示例
│   ├── test_led.py                    ← 90 项测试(Phase 1–4)
│   └── README.md
│
└── verilog/
    ├── LED64/
    │   ├── led64_seq.v                ← 时序逻辑(32 周期/块,enc/dec)
    │   ├── led64_comb.v               ← 组合逻辑(单周期,enc/dec)
    │   ├── led64_*_tb.v               ← 各类测试台
    │   └── README_LED64.md
    ├── LED80/
    │   ├── led80_seq.v / led80_comb.v
    │   ├── led80_*_tb.v
    │   └── README_LED80.md
    └── LED128/
        ├── led128_seq.v / led128_comb.v
        ├── led128_*_tb.v
        └── README_LED128.md

4.1 Verilog 实现

每个变种提供两种架构:

时序逻辑(*_seq.v:每个时钟周期完成一轮的全部四个操作。加密方向(AC→SC→SR→MCS)和解密方向(InvMCS→InvSR→InvSC→AC)的组合逻辑链同时展开,由启动时锁存的 dec_mode 信号在每拍选择写回哪条路径。每 4 拍(round_cnt[1:0] == 2'b11)触发一次子密钥 XOR,完成一个 step。

:原论文的串行硬件实现(每拍处理 4 bits)每轮需要 39 个时钟周期,LED-64 总延迟 1,248 周期,LED-80/128 为 1,872 周期。本项目 Verilog 实现每拍完成整轮并行计算,LED-64 仅需 32 个时钟周期,LED-80/128 需 48 个时钟周期,属于不同的实现权衡。

图 12 时序逻辑实现状态机

状态机中密钥注入公式的说明:加密时每完成 4 轮注入 SK ⌊ round_cnt / 4 ⌋ + 1 \text{SK}^{\lfloor\text{round\_cnt}/4\rfloor+1} SKround_cnt/4+1,从 SK 1 \text{SK}^1 SK1 递增到 SK s \text{SK}^s SKs;解密时每完成 4 轮注入 SK s − 1 − ⌊ round_cnt / 4 ⌋ \text{SK}^{s-1-\lfloor\text{round\_cnt}/4\rfloor} SKs1round_cnt/4,从 SK s − 1 \text{SK}^{s-1} SKs1 递减到 SK 0 \text{SK}^0 SK0。两者都利用同一套密钥取用公式,只是索引方向相反。

组合逻辑(*_comb.v:在 always @(*) 块中用 integer 循环变量展开全部轮次,综合工具将中间 reg 型变量识别为组合逻辑线网,实现单周期完成全部变换:

always @(*) begin : comb_body
    integer r;
    reg [63:0] st;
    st = encrypt ? (plaintext ^ subkey(0)) : (ciphertext ^ subkey(steps));
    for (r = 0; r < ROUNDS; r = r + 1) begin
        if (encrypt) begin
            st = ac_f(st, r);   st = sc_f(st);
            st = sr_f(st);      st = mc_f(st);
            if (r[1:0] == 2'b11) st = st ^ subkey(r/4 + 1);
        end else begin
            st = imc_f(st);     st = isr_f(st);
            st = isc_f(st);     st = iac_f(st, ROUNDS-1-r);
            if (r[1:0] == 2'b11) st = st ^ subkey(steps-1 - r/4);
        end
    end
    result = st;
end

解密路径中 iac_f(st, ROUNDS-1-r) 将轮编号逆向传入 AddConstants,使 RC 序列从 RC [ RN − 1 ] \text{RC}[\text{RN}-1] RC[RN1] 倒序到 RC [ 0 ] \text{RC}[0] RC[0],精确对应加密时各轮所注入的常数。

4.2 Python 实现

led.py 以清晰可读为目标,直接对应算法规范。核心是统一的密钥取用公式,单函数兼容三种密钥长度:

def add_key(state, key_nibbles, step, key_bits):
    n = key_bits // 4                       # 总 nibble 数:16 / 20 / 32
    for i in range(4):
        for j in range(4):
            idx = (4*i + j + step*16) % n   # 环形取 nibble
            state[i][j] ^= key_nibbles[idx]
  • n = 16 n=16 n=16(LED-64): ( 16 ⋅ step )   m o d   16 = 0 (16\cdot\text{step}) \bmod 16 = 0 (16step)mod16=0,所有步偏移为 0,始终取同一组 16 个 nibble
  • n = 20 n=20 n=20(LED-80):5 步一周期, { 0 , 16 , 12 , 8 , 4 } \{0,16,12,8,4\} {0,16,12,8,4} 循环
  • n = 32 n=32 n=32(LED-128): ( 16 ⋅ step )   m o d   32 (16\cdot\text{step}) \bmod 32 (16step)mod32 0 / 16 0/16 0/16 严格交替

4.3 验证方案

本项目采用三层交叉验证:官方 C 实现生成期望密文 → 所有实现的输出与期望密文逐字节比对。图 13 展示了完整的验证链路。

图 13 三层交叉验证链路

图中"期望密文"来自官方 led-bytes.c(Jian Guo 2014 年发布的参考实现),它是唯一的可信基准。Verilog 测试台将期望密文硬编码为 expected 参数,仿真结束后逐 bit 比对;Python test_led.py 同时检验加密(Phase 1/4)、解密还原(Phase 2)和往返一致性(Phase 3)。所有测试均已通过:

实现 测试项数 覆盖内容 结果
Python LED-64/80/128 90 规范向量 enc/dec、自选明文、往返测试 全 PASS
Verilog LED-64 时序/组合 各 18 9 组规范向量 enc + dec 全 PASS
Verilog LED-80 时序/组合 各 18 9 组规范向量 enc + dec 全 PASS
Verilog LED-128 时序/组合 各 18 9 组规范向量 enc + dec 全 PASS
C 交叉验证(3 变种) 各 13 9 组规范 + 4 组自选,与官方 C 逐字节比对 全 PASS

五、参考文献

原始论文:

Jian Guo, Thomas Peyrin, Axel Poschmann, and Matt Robshaw.
“The LED Block Cipher.”
In Cryptographic Hardware and Embedded Systems – CHES 2011,
Lecture Notes in Computer Science, vol. 6917, pp. 326–341.
Springer, Berlin, Heidelberg, 2011.
ePrint: https://eprint.iacr.org/2012/600.pdf(链接有效)

官方参考实现:

Jian Guo.
LED Reference Implementation (led-bytes.c), 2014.
Copyright © 2014 Jian Guo. All rights reserved.
注:论文作者页面:https://sites.google.com/site/ledblockcipher/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐