PS:本来想用纯C实现的,但是实现过程遇到了困难。实现过程用了C++的引用
预备知识
- 扩展欧几里得算法
- 一点有限域GF(28)知识
整体实现思路
PS:参考《密码编码学与网络安全——原理与实践》第七版
第6.3节 AES TRANSFORMATION FUNCTIONS
- 初始化S-box,使第
x
x
x行第
y
y
y列的元素为{
x
y
xy
xy}。
(代码实现时这一步和第二步合在一个initialize()函数中完成) - 对S-box中的每个元素求乘法逆元
(求乘法逆元用到扩展欧几里得算法,所以必须要实现GF( 2 8 2^8 28)中的乘法运算) - 对S-box中的公式运用一下公式得到最终的S-box。 b i ′ = b i ⊕ b ( i + 4 ) m o d 8 ⊕ b ( i + 5 ) m o d 8 ⊕ b ( i + 6 ) m o d 8 ⊕ b ( i + 7 ) m o d 8 ⊕ c i b^{\\'}_i=b_i{\oplus}b_{(i+4)mod8}{\oplus}b_{(i+5)mod8}{\oplus}b_{(i+6)mod8}{\oplus}b_{(i+7)mod8}{\oplus}c_i bi′=bi⊕b(i+4)mod8⊕b(i+5)mod8⊕b(i+6)mod8⊕b(i+7)mod8⊕ci其中 ( c 7 c 6 c 5 c 4 c 3 c 2 c 1 c 0 = ( 01100011 ) (c_7c_6c_5c_4c_3c_2c_1c_0=(01100011) (c7c6c5c4c3c2c1c0=(01100011), 即 c = 0 x 63 c=0x63 c=0x63。
先贴代码如下
#include <cstdio>
typedef unsigned char uc;
uc sbox[16][16];
//初始化 sbox[i][j] <- {ij}
void initialize();
//找到非零最高位并返回
uc msb(unsigned short num);
//一个字节的多项式除法,返回商(a/b)
uc divide(unsigned short a, uc b, uc &r);
//GF(2^8)乘法,返回a * b
uc multiply(uc a, uc b);
//扩展欧几里得算法求b在GF(2^8)的乘法逆元
uc inverse(uc b);
//映射
uc map(uc a);
int main()
{
initialize();
uc i, j;
for(i = 0; i <= 0xF; i++)
{
printf("\n");
for(j = 0; j <= 0xF; j++)
{
sbox[i][j] = map(sbox[i][j]);
printf("%02X ",sbox[i][j]);
}
}
return 0;
}
void initialize()
{
uc i, j;
for(i = 0; i <= 0xF; i++)
{
// printf("\n");
for(j = 0; j <= 0xF; j++)
{
sbox[i][j] = inverse((i << 4) + j);
// printf("%02x ",sbox[i][j]);
}
}
}
uc msb(unsigned short num)
{
uc i;
for(i = 0; i <= 8; i++)
{
if(!(num >> (i + 1)))
{
return i;
}
}
}
//a/b
uc divide(unsigned short a, uc b, uc &r)
{
uc a_msb = msb(a);
uc b_msb = msb(b);
if(a < b)
{
// printf("111111\n");
r = a;
// printf("22222\n");
return 0;
}
uc bit = a_msb - b_msb;
unsigned short temp = b;
temp = temp << bit;
a = a ^ temp;
// printf("%0x\n",a);
return (1 << bit) | divide(a, b, r);
}
uc multiply(uc a, uc b)
{
uc res = 0;
if(b & 0x01)
{
res = a;
}
for (uc i = 1; i < 8; i++)
{
if(b & (0x01 << i))
{
uc temp = a;
for(uc j = 0; j < i; j++)
{
if(!(temp & 0x80))
{
temp <<= 1;
}
else
{
temp <<= 1;
temp = temp ^ 0x1B;
}
}
res = res ^ temp;
}
}
return res;
}
uc inverse(uc b)
{
if(b == 0)
return 0;
short r0 = 0x11B;
uc r1 = b, r2, q;
// uc v0 = 1, v1 = 0, v2;
uc w0 = 0, w1 = 1, w2;
q = divide(r0, r1 , r2);
// v2 = v0 ^ multiply(q, v1);//v2 = 1
w2 = w0 ^ multiply(q, w1);//w2 = -q
while(1)
{
// printf("q=%02x,v=%02x,w=%02x\n",q,v2,w2);
if(r2 == 0)
break;
r0 = r1;
r1 = r2;
q = divide(r0, r1, r2);
// v0 = v1;
// v1 = v2;
// v2 = v0 ^ multiply(q, v1);
w0 = w1;
w1 = w2;
w2 = w0 ^ multiply(q, w1);
}
return w1;
}
uc map(uc a)
{
// uc matrix[] = {0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0x3E, 0x7C, 0xF8};
uc c = 0x63;
uc res = 0x0;
uc temp = 0x0;
uc i;
for(i = 0; i < 8; i++)
{
temp = temp ^ ((a >> i) & 0x1) ^ ((a >> ((i + 4) % 8)) & 0x1);//优先级>> 高于 &
temp = temp ^ ((a >> ((i + 5) % 8)) & 0x1) ^ ((a >> ((i + 6) % 8)) & 0x1);
temp = temp ^ ((a >> ((i + 7) % 8)) & 0x1) ^ ((c >> i) & 0x1);
res = res | (temp << i);
temp = 0x0;
}
return res;
}
运行结果
代码片段分析
一些说明
typedef unsigned char uc;
uc sbox[16][16];
- 由于S-box中的元素是字节元素,所以选
unsigned char
作为基本操作类型。为了方便,将其简化声明为uc
类型。 - sbox数组用来存储S-box中的元素。(这个不说应该也会知道吧)
**void initialize();
**
void initialize()
{
uc i, j;
for(i = 0; i <= 0xF; i++)
{
// printf("\n");//测试用
for(j = 0; j <= 0xF; j++)
{
sbox[i][j] = inverse((i << 4) + j);
// printf("%02x ",sbox[i][j]);
}
}
}
其中第9行 (i << 4) + j
是如 整体实现思路第1步 所说,先初始化S-box,使第
x
x
x行第
y
y
y列的元素为{
x
y
xy
xy}(如果不知道为何要这样计算,那就动手举个例子就了解了)。然后如整体实现思路第2步,对各个元素求乘法逆元。
求乘法逆元之前的准备
双字节除法uc divide(unsigned short a, uc b, uc &r)
PS:我觉得这里的实现做得并不好。我本来只用uc
类型实现全部代码,但是扩展欧几里得算法中有一个变量(uc inverse(uc b)
函数中的q
变量)初始化为
q
=
a
/
b
q=a/b
q=a/b,此处
a
,
即
a
(
x
)
,
a,即a(x),
a,即a(x),为
G
F
(
2
8
)
GF(2^8)
GF(28)的本原多项式,十六进制为
0
x
11
B
0x11B
0x11B,即
a
(
x
)
=
x
8
+
x
4
+
x
3
+
x
+
1
a(x)=x^8+x^4+x^3+x+1
a(x)=x8+x4+x3+x+1,
b
b
b为
G
F
(
2
8
)
GF(2^8)
GF(28)的元素,是求乘法逆元的参数。由于
a
a
a为9比特数,至少2个字节才能表示,所以我用了unsigned short
类型来表示。如果哪位有可以只用uc
类型的方法,请联系1617609852lzc@gmail.com,谢谢!
uc divide(unsigned short a, uc b, uc &r)
{
uc a_msb = msb(a);
uc b_msb = msb(b);
if(a < b)
{
// printf("111111\n");
r = a;
// printf("22222\n");
return 0;
}
uc bit = a_msb - b_msb;
unsigned short temp = b;
temp = temp << bit;
a = a ^ temp;
// printf("%0x\n",a);
return (1 << bit) | divide(a, b, r);
}
-
参数及返回值说明 参数 a a a和 b b b分别为被除数和除数,即 a / b a/b a/b;参数 r r r为文章开头说的要用到的C++引用, r r r是存储余数的;返回值是商。(此处余数和商应该可以用一个结构体解决,这样就不用引用的方法了)。
-
被调用函数
uc msb(unsigned short num)
msb函数返回最高非零位的位置(当然是从0开始) -
思路 参照小学的短除法的做法。(无能为力,说不清,只能如此了)
单字节乘法uc multiply(uc a, uc b)
uc multiply(uc a, uc b)
{
uc res = 0;
if(b & 0x01)
{
res = a;
}
for (uc i = 1; i < 8; i++)
{
if(b & (0x01 << i))
{
uc temp = a;
for(uc j = 0; j < i; j++)
{
if(!(temp & 0x80))
{
temp <<= 1;
}
else
{
temp <<= 1;
temp = temp ^ 0x1B;
}
}
res = res ^ temp;
}
}
return res;
}
-
参数及返回值说明 返回 a ∗ b a*b a∗b
-
思路 这里就涉及到预备知识中说的 G F ( 2 8 ) GF(2^8) GF(28)知识了,需要了解的是如何进行乘法,这里只给出结论,需要证明的请自行搜素。 f ( x ) = 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 + b 0 f(x)=b_7x^7+b_6x^6+b_5x^5+b_4x^4+b_3x^3+b_2x^2+b_1x+b_0 f(x)=b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0 x ∗ f ( x ) = { ( b 6 b 5 b 4 b 3 b 2 b 1 b 0 0 ) ⊕ ( 00011011 ) i f b 7 = 1 ( b 6 b 5 b 4 b 3 b 2 b 1 b 0 0 ) i f b 7 = 0 x*f(x)={\lbrace}^{(b_6b_5b_4b_3b_2b_1b_00)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \ b_7=0}_{(b_6b_5b_4b_3b_2b_1b_00){\oplus}(00011011)\ \ \ \ if\ \ b_7=1} x∗f(x)={(b6b5b4b3b2b1b00)⊕(00011011) if b7=1(b6b5b4b3b2b1b00) if b7=0
因为上式 x ∗ f ( x ) x*f(x) x∗f(x)是从 x x x开始的,所以用一个if语句判断 b b b最低位是否为1。接下来的for循环如果看不懂就去搜一下具体乘法做法就明白了。(有时间再补充详细做法)
求乘法逆元uc inverse(uc b)
uc inverse(uc b)
{
if(b == 0)
return 0;
short r0 = 0x11B;
uc r1 = b, r2, q;
// uc v0 = 1, v1 = 0, v2;
uc w0 = 0, w1 = 1, w2;
q = divide(r0, r1 , r2);
// v2 = v0 ^ multiply(q, v1);//v2 = 1
w2 = w0 ^ multiply(q, w1);//w2 = -q
while(1)
{
// printf("q=%02x,v=%02x,w=%02x\n",q,v2,w2);
if(r2 == 0)
break;
r0 = r1;
r1 = r2;
q = divide(r0, r1, r2);
// v0 = v1;
// v1 = v2;
// v2 = v0 ^ multiply(q, v1);
w0 = w1;
w1 = w2;
w2 = w0 ^ multiply(q, w1);
}
return w1;
}
-
参数及返回值说明 返回 b b b的乘法逆元
-
为什么把 v v v的参数注释掉,因为要求的是 b b b的逆元,所以保留 w w w就够了, v v v和 w w w是独立的。(看不懂的先去看懂扩展欧几里得算法)
-
第一个if语句解释 因为0无逆元,返回0是S-box的构造规则要求的。
映射uc map(uc a)
uc map(uc a)
{
// uc matrix[] = {0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0x3E, 0x7C, 0xF8};
uc c = 0x63;
uc res = 0x0;
uc temp = 0x0;
uc i;
for(i = 0; i < 8; i++)
{
temp = temp ^ ((a >> i) & 0x1) ^ ((a >> ((i + 4) % 8)) & 0x1);//优先级>> 高于 &
temp = temp ^ ((a >> ((i + 5) % 8)) & 0x1) ^ ((a >> ((i + 6) % 8)) & 0x1);
temp = temp ^ ((a >> ((i + 7) % 8)) & 0x1) ^ ((c >> i) & 0x1);
res = res | (temp << i);
temp = 0x0;
}
return res;
}
-
参数及返回值说明 将 a a a 通过某种规则映射到另一个数,并返回这个数
-
具体映射规则参考整体实现思路第3步