文章目录
- 1. Introduction
- 3. Notation and Conventions
- 3.2 Bytes
- 3.4 State
- 4. Mathematical Preliminaries
- 4.1 Addition
- 4.2 Multiplication
- 4.2.1 Multiplication by x-xtime()
- 4.3 Polynomials with Coefficients in GF(2^8^)
- 5. Algorithm Specification
- 5.1 Cipher
- 5.1.1 SubBytes()Transformation
- 5.1.2 ShiftRows() Transformation
- 5.1.3 MixColumns() Transformation
- 5.1.4 AddRoundKey() Transformation
- 5.2 Key Expansion
- 5.3 Inverse Cipher
- 5.3.5 Equivalent Inverse Cipher
- 实现
- 参考资料
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)。
输入的比特流在字节中以大端序存储。
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
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中的不可约多项式如下:
也可记为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,则变成了如下公式:
乘以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字节整型)操作。
其中乘法c(x)无法表示为4字节整型,在AES中需要取余操作:
计算的结果为d(x):
如果a(x)是一个固定的多项式,则可以写成矩阵(matrix)形式:
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)套用到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)
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