PS:本来想用纯C实现的,但是实现过程遇到了困难。实现过程用了C++的引用
预备知识
- 扩展欧几里得算法
- 一点有限域GF(28)知识
整体实现思路
PS:参考《密码编码学与网络安全——原理与实践》第七版
第6.3节 AES TRANSFORMATION FUNCTIONS
- 初始化S-box,使第
x 行第y 列的元素为{xy }。
(代码实现时这一步和第二步合在一个initialize()函数中完成) - 对S-box中的每个元素求乘法逆元
(求乘法逆元用到扩展欧几里得算法,所以必须要实现GF(28 )中的乘法运算) - 对S-box中的公式运用一下公式得到最终的S-box。
b′i=bi⊕b(i+4)mod8⊕b(i+5)mod8⊕b(i+6)mod8⊕b(i+7)mod8⊕ci (c7c6c5c4c3c2c1c0=(01100011) , 即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,使第
求乘法逆元之前的准备
双字节除法uc divide(unsigned short a, uc b, uc &r)
PS:我觉得这里的实现做得并不好。我本来只用uc
类型实现全部代码,但是扩展欧几里得算法中有一个变量(uc inverse(uc b)
函数中的q
变量)初始化为
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) 知识了,需要了解的是如何进行乘法,这里只给出结论,需要证明的请自行搜素。f(x)=b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0 x∗f(x)={(b6b5b4b3b2b1b00) if b7=0(b6b5b4b3b2b1b00)⊕(00011011) if b7=1
因为上式x∗f(x) 是从x 开始的,所以用一个if语句判断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 的乘法逆元为什么把
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步