PS:本来想用纯C实现的,但是实现过程遇到了困难。实现过程用了C++的引用
PS:参考《密码编码学与网络安全——原理与实践》第七版
第6.3节 AES TRANSFORMATION FUNCTIONS
#include
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];
unsigned char
作为基本操作类型。为了方便,将其简化声明为 uc
类型。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 行第 y 列的元素为{ xy }(如果不知道为何要这样计算,那就动手举个例子就了解了)。然后如整体实现思路第2步,对各个元素求乘法逆元。
uc divide(unsigned short a, uc b, uc &r)
PS:我觉得这里的实现做得并不好。我本来只用uc
类型实现全部代码,但是扩展欧几里得算法中有一个变量(uc inverse(uc b)
函数中的q
变量)初始化为 q=a/b ,此处 a,即a(x), 为 GF(28) 的本原多项式,十六进制为 0x11B ,即 a(x)=x8+x4+x3+x+1 , b 为 GF(28) 的元素,是求乘法逆元的参数。由于 a 为9比特数,至少2个字节才能表示,所以我用了unsigned short
类型来表示。如果哪位有可以只用uc
类型的方法,请联系[email protected],谢谢!
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 和 b 分别为被除数和除数,即 a/b ;参数 r 为文章开头说的要用到的C++引用, 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
思路 这里就涉及到预备知识中说的 GF(28) 知识了,需要了解的是如何进行乘法,这里只给出结论,需要证明的请自行搜素。
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 的乘法逆元
为什么把 v 的参数注释掉,因为要求的是 b 的逆元,所以保留 w 就够了, v 和 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 通过某种规则映射到另一个数,并返回这个数
具体映射规则参考整体实现思路第3步