文章目录
1. Introduction
标准为FIPS-197 2001,1997-2000年期间通过竞赛形式选用Daemen和Rijmen两位参选者的算法Rijndael。
其它参选算法:
- MARS, by IBM company
- RC6, by RSA company
- Serpent, by Anderson, Biham, Knudsen
- Twofish, by Counterpane company
AES简介:
- 分组长度:128 bits == 16 bytes.
- 密钥长度:
- AES-128, 128 bits == 16 bytes
- AES-192, 192 bits == 24 bytes
- AES-256, 256 bits == 32 bytes
原Rijndael算法的分组长度和密钥长度则是支持[128 bits, 256 bits, step = 32 bits]
;
3. Notation and Conventions
3.2 Bytes
在AES算法中,字节可表示为有限域(finite field)上的多项式(polynomial)。
b
7
x
7
+
b
6
x
6
+
b
5
x
5
+
b
4
x
4
+
b
3
x
3
+
b
2
x
2
+
b
1
x
1
+
b
0
=
∑
i
=
0
7
b
i
x
i
b_7x^7 + b_6x^6 + b_5x^5 + b_4x^4 + b_3x^3 + b_2x^2 + b_1x^1 + b_0 = \sum_{i=0}^{7} b_ix^i
b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x1+b0=i=0∑7bixi
输入的比特流在字节中以大端序存储。
3.4 State
AES的操作对象是一个状态(State)二维数组(以字节为单位):
- 4行,下标记为r;
- Nb列,Nb * Rows == Block Length,所以Nb取值为16 / 4 == 4,下标记为c;
输入字节和输出结果都是按列进行存储的:
state[r, c] = input[r + 4c];
out[r + 4c] = s[r, c];
在AES中可以把每一列看作一个大端序4字节整型(uint32_t)进行处理,所以Nb既可以理解为列数,也可以理解为word数。
4. Mathematical Preliminaries
Finite field elements can be added and multiplied.
4.1 Addition
其实就是异或操作。
0x57 ^ 0x83 == 0xd4
( x 6 + x 4 + x 2 + x + 1 ) + ( x 7 + x + 1 ) = x 7 + x 6 + x 4 + x 2 (x^6 + x^4 + x^2 + x +1) + (x^7 + x +1) = x^7 + x^6 + x^4 + x^2 (x6+x4+x2+x+1)+(x7+x+1)=x7+x6+x4+x2
4.2 Multiplication
In the polynomial representation, multiplication in GF(28) (denoted by •) corresponds with the multiplication of polynomials modulo an irreducible polynomial(不可约多项式) of degree 8. A polynomial is irreducible if its only divisors(因子) are one and itself.
AES中的不可约多项式如下:
m
(
x
)
=
x
8
+
x
4
+
x
3
+
x
1
+
1
m(x) = x^8 + x^4 + x^3 + x^1 + 1
m(x)=x8+x4+x3+x1+1
也可记为0x011B(==0b100011011)。
The modular reduction by m(x) ensures that the result will be a binary polynomial of degree less than 8, and thus can be represented by a byte.
至于有限域求乘法逆元,则需要用到扩展欧几里得算法(the extended Euclidean algorithm)。
4.2.1 Multiplication by x-xtime()
将字节的多项式乘以x,则变成了如下公式:
b
7
x
8
+
b
6
x
7
+
b
5
x
6
+
b
4
x
5
+
b
3
x
4
+
b
2
x
3
+
b
1
x
2
+
b
0
x
b_7x^8 + b_6x^7 + b_5x^6 + b_4x^5 + b_3x^4 + b_2x^3 + b_1x^2 + b_0x
b7x8+b6x7+b5x6+b4x5+b3x4+b2x3+b1x2+b0x
乘以x,在代码中其实就是左移一位。
若b71,则与AES中的不可约多项式m(x)异或;若b70,则上式就是最简形式。这种操作在AES中叫做xtime()。
以0x57乘以所有高次幂多项式为例:
a = 0x57
mx = 0x011b
print("0x57 * 0x1 == 0x57")
for i in range(1, 8):
a = a << 1;
print("0x57 * %s == xtime(%s)" %(hex(2**i), hex(a>>1)), end="")
if(a & 0x0100):
print(" == %s ^ %s->%s" % (hex(a), hex(mx), hex(a^mx)))
a = a ^ mx
else:
print(" == %s" % hex(a))
# 0x57 * 0x1 == 0x57
# 0x57 * 0x2 == xtime(0x57) == 0xae
# 0x57 * 0x4 == xtime(0xae) == 0x15c ^ 0x11b->0x47
# 0x57 * 0x8 == xtime(0x47) == 0x8e
# 0x57 * 0x10 == xtime(0x8e) == 0x11c ^ 0x11b->0x7
# 0x57 * 0x20 == xtime(0x7) == 0xe
# 0x57 * 0x40 == xtime(0xe) == 0x1c
# 0x57 * 0x80 == xtime(0x1c) == 0x38
0x57乘以0x13的结果可以用结合律计算:
0x57 * 0x13
= 0x57 * (0x01 ^ 0x02 ^ 0x10)
= 0x57 ^ 0xae ^ 0x07
= 0xfe
换成c语言实现:
#include <stdio.h>
#include <stdint.h>
uint8_t xtime(uint16_t a, uint8_t b)
{
static const uint16_t mx = 0x011B;
uint16_t nRet = 0;
int i = 0;
for (i = 0; i < 8; ++i)
{
if (b & 0x01)
{
nRet ^= a;
}
b >>= 1;
a <<= 1;
if (a & 0x0100) // x^8
{
a ^= mx;
}
}
return nRet & 0xFF;
}
int main()
{
uint8_t a = xtime(0x57, 0x13);
printf("%x\n", a); // fe
return 0;
}
4.3 Polynomials with Coefficients in GF(28)
有限域也适用于字(word,4字节整型)操作。
a
(
x
)
=
a
3
x
3
+
a
2
x
2
+
a
1
x
+
a
0
b
(
x
)
=
b
3
x
3
+
b
2
x
2
+
b
1
x
+
b
0
a
(
x
)
+
b
(
x
)
=
(
a
3
+
b
3
)
x
3
+
(
a
2
+
b
2
)
x
2
+
(
a
1
+
b
1
)
x
+
(
a
0
+
b
0
)
c
(
x
)
=
a
(
x
)
∗
b
(
x
)
=
c
6
x
6
+
c
5
x
5
+
c
4
x
4
+
c
3
x
3
+
c
2
x
2
+
c
1
x
+
c
0
a(x) = a_3 x^3 + a_2 x^2 + a_1 x + a_0 \\ b(x) = b_3 x^3 + b_2 x^2 + b_1 x + b_0 \\ a(x) + b(x) = (a_3 + b_3) x^3 + (a_2+ b_2) x^2 + (a_1+ b_1) x + (a_0+ b_0) \\ c(x) = a(x) * b(x) = c_6 x^6 + c_5 x^5 + c_4 x^4 + c_3 x^3 + c_2 x ^2 + c_1 x + c_0
a(x)=a3x3+a2x2+a1x+a0b(x)=b3x3+b2x2+b1x+b0a(x)+b(x)=(a3+b3)x3+(a2+b2)x2+(a1+b1)x+(a0+b0)c(x)=a(x)∗b(x)=c6x6+c5x5+c4x4+c3x3+c2x2+c1x+c0
其中乘法c(x)无法表示为4字节整型,在AES中需要取余操作:
x
i
m
o
d
(
x
4
+
1
)
=
x
i
m
o
d
4
x^i mod(x^4 +1) = x^{i mod 4}
ximod(x4+1)=ximod4
计算的结果为d(x):
d
(
x
)
=
d
3
x
3
+
d
2
x
2
+
d
1
x
+
d
0
d
0
=
(
a
0
∗
b
0
)
+
(
a
3
∗
b
1
)
+
(
a
2
∗
b
2
)
+
(
a
1
∗
b
3
)
=
c
4
+
c
0
d
1
=
(
a
1
∗
b
0
)
+
(
a
0
∗
b
1
)
+
(
a
3
∗
b
2
)
+
(
a
2
∗
b
3
)
=
c
5
+
c
1
d
2
=
(
a
2
∗
b
0
)
+
(
a
1
∗
b
1
)
+
(
a
0
∗
b
2
)
+
(
a
3
∗
b
3
)
=
c
6
+
c
2
d
3
=
(
a
3
∗
b
0
)
+
(
a
2
∗
b
1
)
+
(
a
1
∗
b
2
)
+
(
a
0
∗
b
3
)
=
c
3
d(x) = d_3 x^3 + d_2 x^2 + d_1 x + d_0 \\ d_0 = (a_0 * b_0 ) + (a_3 * b_1 ) + (a_2 * b_2 ) + (a_1 * b_3 ) =c_4+c_0 \\ d_1 = (a_1 * b_0 ) + (a_0 * b_1 ) + (a_3 * b_2 ) + (a_2 * b_3 ) =c_5+c_1 \\ d_2 = (a_2 * b_0 ) + (a_1 * b_1 ) + (a_0 * b_2 ) + (a_3 * b_3 ) =c_6+c_2 \\ d_3 = (a_3 * b_0 ) + (a_2 * b_1 ) + (a_1 * b_2 ) + (a_0 * b_3 ) =c_3 \\
d(x)=d3x3+d2x2+d1x+d0d0=(a0∗b0)+(a3∗b1)+(a2∗b2)+(a1∗b3)=c4+c0d1=(a1∗b0)+(a0∗b1)+(a3∗b2)+(a2∗b3)=c5+c1d2=(a2∗b0)+(a1∗b1)+(a0∗b2)+(a3∗b3)=c6+c2d3=(a3∗b0)+(a2∗b1)+(a1∗b2)+(a0∗b3)=c3
如果a(x)是一个固定的多项式,则可以写成矩阵(matrix)形式:
[ d 0 d 1 d 2 d 3 ] = [ a 0 a 3 a 2 a 1 a 1 a 0 a 3 a 2 a 2 a 1 a 0 a 3 a 3 a 2 a 1 a 0 ] [ b 0 b 1 b 2 b 3 ] \begin{bmatrix} d_0 \\ d_1 \\ d_2 \\ d_3 \end{bmatrix}= \begin{bmatrix} a_0 & a_3 & a_2 & a_1 \\ a_1 & a_0 & a_3 & a_2 \\ a_2 & a_1 & a_0 & a_3 \\ a_3 & a_2 & a_1 & a_0 \\ \end{bmatrix} \begin{bmatrix} b_0 \\ b_1 \\ b_2 \\ b_3 \end{bmatrix} d0d1d2d3 = a0a1a2a3a3a0a1a2a2a3a0a1a1a2a3a0 b0b1b2b3
x4+1 is not an irreducible polynomial over GF(28), multiplication by a fixed four-term polynomial is not necessarily invertible.
乘法运算,一共256*256种情况,可以提前将运算结果硬编码为数组,前提是确保所运行的平台有足够的空间。
5. Algorithm Specification
重申几个常量和变量:
- 分组长度:128 bits == 16 bytes == Nb words(4).
- 密钥长度:
- AES-128, 128 bits == 16 bytes == Nk words(4)
- AES-192, 192 bits == 24 bytes == Nk words(6)
- AES-256, 256 bits == 32 bytes == Nk words(8)
- 轮数:
- AES-128, Nr==10
- AES-192, Nr==12
- AES-256, Nr==14
加密和解密的轮函数(round function)流程可以简述为4步:
- byte substitution using a substitution table (S-box)
- shifting rows of the State array by different offsets
- mixing the data within each column of the State array
- adding a Round Key to the State.
5.1 Cipher
官方伪代码:
Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, w[0, Nb-1]) // See Sec. 5.1.4
for round = 1 step 1 to Nr–1
SubBytes(state) // See Sec. 5.1.1
ShiftRows(state) // See Sec. 5.1.2
MixColumns(state) // See Sec. 5.1.3
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
end for
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
out = state
end
5.1.1 SubBytes()Transformation
通过一个16*16的S-box(substitution table),进行字节替换。
State数组元素的高4位为行索引,低4位为列索引。
static const uint8_t sbox[16][16] = {
{0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
{0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
{0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
{0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
{0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
{0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
{0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
{0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
{0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
{0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
{0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
{0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},{0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
{0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
{0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
};
5.1.2 ShiftRows() Transformation
每行以字节为单位循环左移:
- 第1行不变
- 第2行循环左移1字节
- 第3行循环左移2字节
- 第4行循环左移3字节
5.1.3 MixColumns() Transformation
以列为单位,把每列看成一个在GF(28)域上的多项式,计算公式如下:
S'(x) = a(x) * s(x) mod (x^4+1)
其中a(x)为固定的多项式:
a
(
x
)
=
{
03
}
x
3
+
{
01
}
x
2
+
{
01
}
x
+
{
02
}
[
02
03
01
01
01
02
03
01
01
01
02
03
03
01
01
02
]
a(x) = \{03\} x^3 + \{01\} x^2 + \{01\} x + \{02\} \\ \begin{bmatrix} 02 & 03 & 01 & 01 \\ 01 & 02 & 03 & 01 \\ 01 & 01 & 02 & 03 \\ 03 & 01 & 01 & 02 \\ \end{bmatrix}
a(x)={03}x3+{01}x2+{01}x+{02}
02010103030201010103020101010302
然后将a(x)套用到4.3的公式就行了。
5.1.4 AddRoundKey() Transformation
将State中每列4字节(即一个word)与轮密钥(Round Key)相加(异或)。对应轮密钥下标如下:
round*Nb+c
0 <= round <= Nr
0 <= c <= Nb
Each Round Key consists of Nb words from the key schedule (described in Sec.5.2).
5.2 Key Expansion
The AES algorithm takes the Cipher Key, K, and performs a Key Expansion routine to generate a key schedule.
伪代码:
KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk)
// Nb*(Nr+1) ==
// 4 * 11
// 4 * 13
// 4 * 15
begin
word temp
i = 0
while (i < Nk) // Nk == 4/6/8 words
w[i] = word(key[4*i], key[4*i+1], key[4*i+2], key[4*i+3])
i = i+1
end while
i = Nk
while (i < Nb * (Nr+1))
temp = w[i-1]
if (i mod Nk = 0)
temp = SubWord(RotWord(temp)) xor Rcon[i/Nk]
else if (Nk > 6 and i mod Nk = 4)
temp = SubWord(temp)
end if
w[i] = w[i-Nk] xor temp
i = i + 1
end while
end
SubWord()将一个word根据S-Box进行替换。
RotWord()将一个word循环左移1字节。
Rcon[i], The round constant word array, contains the values given by [xi-1,{00},{00},{00}], as discussed in Sec. 4.2. i starts at 1.
a = 0x01
mx = 0x011b
listRcon = []
for i in range(15):
# print("0x57 * %s == %s" %(hex(i+1), hex(a)) )
if(a & 0x0100):
a = a ^ mx
# print("^ mx->%s" % hex(a))
listRcon.append(a << 24)
a = a << 1;
for i in listRcon:
print("0x%08x" % i, end=",")
# 0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,
# 0x20000000,0x40000000,0x80000000,0x1b000000,0x36000000,
# 0x6c000000,0xd8000000,0xab000000,0x4d000000,0x9a000000,
原文中Rcon下标从1开始,所以为代码中的Rcon[i/Nk]
在实现时应该改成Rcon[(i-1)/Nk]
,
5.3 Inverse Cipher
解密流程官方伪代码:
InvCipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) // See Sec. 5.1.4
for round = Nr-1 step -1 downto 1
InvShiftRows(state) // See Sec. 5.3.1
InvSubBytes(state) // See Sec. 5.3.2
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
InvMixColumns(state) // See Sec. 5.3.3
end for
InvShiftRows(state)
InvSubBytes(state)
AddRoundKey(state, w[0, Nb-1])
out = state
end
InvShiftRows()是ShiftRows()的逆过程。
InvSubBytes()依赖于inverse S-box(逆替换表)。
static const uint8_t aes_invsbox[16][16] = {
{0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB},
{0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB},
{0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E},
{0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25},
{0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92},
{0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84},
{0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06},
{0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B},
{0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73},
{0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E},
{0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B},
{0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4},
{0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F},
{0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF},
{0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61},
{0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D}
};
InvMixColumns()依赖于a(x)的逆多项式a-1(x)
a
−
1
(
x
)
=
{
0
b
}
x
3
+
{
0
d
}
x
2
+
{
09
}
x
+
{
0
e
}
[
0
e
0
b
0
d
09
09
0
e
0
b
0
d
0
d
09
0
e
0
b
0
b
0
d
09
0
e
]
a^{-1}(x) = \{0b\} x^3 + \{0d\} x^2 + \{09\} x + \{0e\} \\ \begin{bmatrix} 0e & 0b & 0d & 09 \\ 09 & 0e & 0b & 0d \\ 0d & 09 & 0e & 0b \\ 0b & 0d & 09 & 0e \\ \end{bmatrix}
a−1(x)={0b}x3+{0d}x2+{09}x+{0e}
0e090d0b0b0e090d0d0b0e09090d0b0e
5.3.5 Equivalent Inverse Cipher
上述解密流程的置换顺序和加密有些不同,但使用了同样的key schedules。
通过修改key schedules,也可以用加密中的置换流程实现解密,并且效率更高。
EqInvCipher(byte in[4*Nb], byte out[4*Nb], word dw[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, dw[Nr*Nb, (Nr+1)*Nb-1])
for round = Nr-1 step -1 downto 1
InvSubBytes(state)
InvShiftRows(state)
InvMixColumns(state)
AddRoundKey(state, dw[round*Nb, (round+1)*Nb-1])
end for
InvSubBytes(state)
InvShiftRows(state)
AddRoundKey(state, dw[0, Nb-1])
out = state
end
新的密钥扩展流程(Key Expansion routine):
for i = 0 step 1 to (Nr+1)*Nb-1
dw[i] = w[i]
end for
for round = 1 step 1 to Nr-1
InvMixColumns(dw[round*Nb, (round+1)*Nb-1]) // note change of type
// two-dimensional array of bytes
// -->
// one-dimensional array of words
end for
实现
https://github.com/C0deStarr/CryptoImp/tree/main/Cipher/BlockCipher
- aes.h
- aes.c
- gf_mul.h
- gf_mul.c
参考资料
FIPS 197, Advanced Encryption Standard | CSRC (nist.gov)
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf