AES

  • AES简介
  • AES加密及解密流程
  • AES加密的几个模块及详解
  • 前言
  • 数组的定义:
  • 轮密钥加
  • 算法原理
  • c代码实现思路
  • c代码实现
  • 字节代替(s盒)
  • 算法原理
  • c代码实现思路
  • c代码实现
  • 行移位
  • 算法原理
  • c代码实现思路
  • c代码实现
  • 列混淆
  • 算法原理
  • c代码实现思路
  • 列混淆中的有限域GF(2^8)上的乘法
  • 列混淆中的有限域GF(2^8)上的乘法c代码实现
  • 矩阵乘法
  • 矩阵乘法c代码实现
  • 列混淆c代码实现
  • 密钥扩展
  • 算法原理
  • c代码实现思路
  • g-s盒实现
  • 1*4数组的加法
  • g函数
  • c代码实现
  • 主函数设计思路
  • 主函数
  • AES c语言实现



在这里虽然有提及到AES解密,但先只讲加密,解密会后续补上。

AES简介

AES是一种分组加密算法,明文分组大小为128位,密钥长度有128、192、256位三种。AES加密在加密前先将明文分成128位一组的若干组,再分组进行加密。在AES加密中一个字由四个字节组成。

AES加密及解密流程

aes加密 android 算法 aes加密算法实现_c代码

AES加密的几个模块及详解

前言

在对AES加密模块及其代码进行讲解前先对c语言中关于数组的使用方法及c实现AES过程中所涉及到的一些运算符进行一些讲解:

数组的定义:

一维数组:

int a[4];//定义一个一维长度为四的数组
int b[4]={0,1,2,3};//定义一个一维长度为四的数组并初始化为{0,1,2,3}
int *p=&b[0];//定义一个int类型指针指向b[0]
*p=4;//令指针p指向的b[0]=4
*(p+1)=5;//令指针p指向的b[0]的下一个数组元素b[1]=5;

二维数组:

int a[4][4]=
{
	0,1,2,3,
	4,5,6,7,
	8,9,10,11,
	12,13,14,15
};
//定义一个4*4的二维数组并初始化为:
//{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
int *p=&a[0][0];
*(p+2*4+1)=5;//令a[2][1]=5
//指针(p+j*4+i)的意义是:
//相对于二维数组a[4][4],指针(p+j*4+i)等价于a[j][i]

运算符:
“^”为异或运算,
任意两个整数a,b进行异或运算a^b的结果与将其转换成二进制后再进行异或运算的结果是相同的。
实际上数是客观存在的,数的进制表示只是数的表现形式,数的任何操作都是对数本身进行操作,故16进制数0x16与0x23异或运算等价于二进制数0b00010110与0b00100011异或运算。

轮密钥加

算法原理

AES加密在第一轮加密前先进行了一次轮密钥加,第一轮加密前的轮密钥加实际上是对明文分组和初始密钥进行了一次异或运算。

c代码实现思路

如何实现用c语言实现轮密钥加呢?

首先,明文分组与密钥都为128位的其实等于16个字节,将明文与密钥分别对应的16个字节行向优先填入两个4*4的矩阵中。

先定义两个4*4的二维数组:

int plaintext[4][4],key[4][4];

并将明文分组与密钥行向优先填入这两个数组;
再定义两个指针分别指向plaintext[0][0]、key[0][0]:

int *p=&plaintext[0][0];int *k=&key[0][0];

对于使这两个数组对应的元素准确异或可以用for语句来实现:

for(j=0;j<4;j++)
	for(i=0;i<4;i++)
		plaintext[j][i]=plaintext[j][i]^key[j][i];

因为主函数要将轮密钥加作为副函数来调用,所以将数组指针作为参数来实现这个模块。

c代码实现

void addbyte16(int *p,int *k)//轮密钥加,结果保存在左参数,也为4*4数组异或
{
	int i,j;
	for(j=0;j<4;j++)
		for(i=0;i<4;i++)
			*(p+j*4+i)=*(p+j*4+i)^*(k+j*4+i);
}

可以看出轮密钥加的函数实际上是两个4*4数组进行对应异或,所以对其命名为addbyte16。

字节代替(s盒)

算法原理

aes加密 android 算法 aes加密算法实现_aes加密 android 算法_02


s盒是将输入的16字节数每个字节都经过一次查表变换生成密文,而查表变换便是:

将输入的字节例0x16的高四位即0x1作为行值低四位即0x6作为列值查上面的表中的第0x1行0x6列所得的值作为经查表变换后输出的值即0x47。

c代码实现思路

先定义一个16*16(256)的一维数组int s[256];,并将s-box表输入其中;
当输入一个字节0xmn时它在s表中的位置为0xm行0xn列,在一维数组s中的位置为[0xm * 16+0xn],而[0xm * 16+0xn]=0xmn,故可以使输入的字节作为数组s的标号即s[0xmn]作为输出。

c代码实现

void she(int *p)//S盒
{
	int i;
	int s[256] =
	{
	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
	};
	for (i = 0; i < 16; i++)*(p + i) = s[*(p + i)];
}

行移位

算法原理

aes加密 android 算法 aes加密算法实现_aes加密 android 算法_03


行移位是使如上图这样的4*4矩阵第1行左移0位、第2行左移1位、第三行左移2位、第四行左移3位。

c代码实现思路

在c语言中对于4*4的数组的行列是从0~3编号的,即:
第0行左移0位、第1行左移1位、第2行左移2位、第3行左移3位。
对于左移可以先:int b[4][4],在b中保留左移后的结果。通过b[j][i]=*(p+j*4+(i+j)%4);来实现左移。最后再将b中的结果填入到原数组中。

c代码实现

void left(int *p)//行移位
{
	int i, j;
	int b[4][4];
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			b[j][i] = *(p + j * 4 + (i + j) % 4);
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			*(p + i + j * 4) = b[j][i];
}

列混淆

算法原理

aes加密 android 算法 aes加密算法实现_aes加密 android 算法_04


列混淆是将输入的4*4矩阵左乘一个常熟矩阵(常数矩阵是一个常态,固定不变),在这里左乘是矩阵的左乘,但又有所不同,以上图中的矩阵左乘为例:

s'0,0=0x02*s0,0+0x03*s1,0+0x01*s2,0+0x01*s3.0

在上面的式子中,乘法为有限域GF(2^8)上的乘法,
加法为有限域GF(2^8)上的加法即异或。

列混淆的左乘与普通矩阵的左乘不同之处就在于乘法为有限域GF(2^8)上的乘法,
加法为有限域GF(2^8)上的加法即异或。

c代码实现思路

为了便于说明,在接下来的叙述中乘就是有限域GF(2^8)上的乘法。

先对乘法进行定义:

列混淆中的有限域GF(2^8)上的乘法

因为常数矩阵是由0x01、0x02、0x03这三个数组成,所以只需考虑这三个数与其他数相乘。

0x01
0x01与任何数相乘都为这个数本身。

0x02
对于a*0x02(即0000 0010)有两种情况:
(1)、当a小于0x80(即1000 0000)时a*0000 0010等于将a最高位去掉再在末尾补0的值,例:

a=0010 0011,
a*0000 0010=0100 0110;
		 a=0111 0010,
a*0000 0010=1110 0100;

(2)、当a大于等于0x80时a*0000 0010等于将a最高位去掉再在末尾补0后再和0x1b(即0001 1011)异或的值,例:

a=1010 0011,
a*0000 0010=0100 0110
		   ^0001 1011
		   =0101 1101;

0x30:

因为 0x03=0000 0011=(0000 0001^0000 0010),
所以 a*0x03=(a*0000 0001)^(a*0000 0010)=a^(a*0000 0010);

同样的,对于这里的a*0x02(即0000 0010)也是有上面关于0x02的叙述中的两种情况。

列混淆中的有限域GF(2^8)上的乘法c代码实现
int mul(int b,int a)//有限域上的乘法,只定义了01、02、03与其他数相乘
{
	if (b == 0x02)
	{
		if (a < 0x80)a = (a * 0x02);
		else a = ((a % 0x80) * 0x02) ^ 0x1b;
	}
	if (b == 0x03)
	{
		if (a < 0x80)b = (a * 0x02);
		else b = ((a % 0x80) * 0x02) ^ 0x1b;
		a = a^b;
	}
	return a;
}
矩阵乘法

aes加密 android 算法 aes加密算法实现_i++_05


如上图,矩阵C中的元素的标号决定了这个元素由矩阵A与B中哪些元素进行计算求得(行号决定了取A中哪一行的元素,列号决定了取B中哪一列的元素),设C[j][i],则:

C[j][i]=aj,1*b1,i+aj,2*b2,i+aj,3*b3,i+aj,4*b4,i;
矩阵乘法c代码实现
for(j=0;j<4;j++)
	for(i=0;i<4;i++)
		c[j][i]=aj,1*b1,i+aj,2*b2,i+aj,3*b3,i+aj,4*b4,i;

列混淆c代码实现

因为输入的4*4矩阵中的元素不只使用一次,故要不改动输入的矩阵,用一个4*4数组存放运算结果。

void column(int *p, int *q)//列混淆
{
	int i, j;
	int a[4][4];
	for (j = 0; j < 4; j++)for (i = 0; i < 4; i++)
	{
		a[j][i] = mul(*(q + j * 4 + 0), *(p + 0 * 4 + i)) ^ mul(*(q + j * 4 + 1), *(p + 1 * 4 + i)) ^
			mul(*(q + j * 4 + 2), *(p + 2 * 4 + i)) ^ mul(*(q + j * 4 + 3), *(p + 3 * 4 + i));
	}
	for (j = 0; j < 4; j++)for (i = 0; i < 4; i++)*(p + j * 4 + i) = a[j][i];
}

密钥扩展

算法原理

aes加密 android 算法 aes加密算法实现_i++_06


密钥扩展是:在第一轮时将输入的初始密钥数组按上图左上角的方式第一列组成一个竖向数组w0、第二组成一个竖向数组w2、第三列组成一个竖向数组w3、第四列组成一个竖向数组w4,将w3经过g函数变换后与w1异或得到w4,将w4与w1异或得到w5,将w5与w2异或得到w6,将w6与w3异或得到w7,接下来的第二轮、第三轮等重复第一轮的变换得到,w8、w9等。

而g函数是将输入的1*4竖向数组变成横向数组进行左移一位操作后将每个字节进行s盒变换,再将第一个字节与RC数组中的一个字节进行异或,与哪个字节异或取决于当前的轮数。

c代码实现思路

第一步将二维数组分成四个竖向数组,在c语言中没有竖向数组,可以将竖向数组改为横向数组,即将得到的竖向数组按顺序存放在一个一维数组中。即:

for (i = 0; i < 4; i++)a[i] = *(p + i * 4 + 0);
for (i = 0; i < 4; i++)b[i] = *(p + i * 4 + 1);
for (i = 0; i < 4; i++)c[i] = *(p + i * 4 + 2);
for (i = 0; i < 4; i++)d[i] = *(p + i * 4 + 3);
g-s盒实现

g-s盒与s盒很相似,区别在于g-s盒是对1*4数组的变换操作。

void gshe(int *p)//g-S盒
{
	int i;
	int s[256] =
	{
	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
	};
	for (i = 0; i < 4; i++)*(p + i) = s[*(p + i)];
}
1*4数组的加法

1*4数组的加法与4*4数组加法相似,区别在于是对于1*4数组的操作。

void addbyte4(int *p, int *q)//1*4矩阵字节加法,结果保存到左加数
{
	int i;
	for (i = 0; i < 4; i++)
		*(p + i) = *(p + i) ^ *(q + i);
}
g函数

在g函数中定义RC数组,并将加密轮数作为一个参数来决定异或的RC数组中的哪个字节。因要进行左移一位操作,故先将输入的数组第一个元素保存在a,for (i = 0; i < 3; i++)*(p+i) = *(p + i + 1);后再*(p + 3) = a;

void g(int *p,int j)//g函数
{
	int rc[10] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36 };
	int i,a = *p;
	for (i = 0; i < 3; i++)*(p+i) = *(p + i + 1);*(p + 3) = a;
	gshe(p);
	*p = *p^rc[j];
 }

在进行g函数变换和异或变换后将四个一维竖向数组还原成原来的行向4*4的二维数组。

for (i = 0; i < 4; i++)*(p + i * 4 + 0) = a[i];
for (i = 0; i < 4; i++)*(p + i * 4 + 1) = b[i];
for (i = 0; i < 4; i++)*(p + i * 4 + 2) = c[i];
for (i = 0; i < 4; i++)*(p + i * 4 + 3) = d[i];

c代码实现

在这里因为w3、w7等数组要利用两次故定义额外一个数组等于w3、w7等数组。
将加密轮数j作为输入参数。

void keyextension(int *p, int j)//密钥扩展
{
	int a[4], b[4], c[4], d[4], d1[4];
	int i, *q1 = &a[0], *q2 = &b[0], *q3 = &c[0], *q4 = &d[0], *q5 = &d1[0];
	for (i = 0; i < 4; i++)a[i] = *(p + i * 4 + 0);
	for (i = 0; i < 4; i++)b[i] = *(p + i * 4 + 1);
	for (i = 0; i < 4; i++)c[i] = *(p + i * 4 + 2);
	for (i = 0; i < 4; i++)d[i] = *(p + i * 4 + 3);
	for (i = 0; i < 4; i++)d1[i] = *(p + i * 4 + 3);
	g(q5, j);
	addbyte4(q1, q5);
	addbyte4(q2, q1);
	addbyte4(q3, q2);
	addbyte4(q4, q3);
	for (i = 0; i < 4; i++)*(p + i * 4 + 0) = a[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 1) = b[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 2) = c[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 3) = d[i];
}

主函数设计思路

AES中在进行第一轮加密前先进行了一次轮密钥加:

//第一轮前
	addbyte16(p,k);

在十轮加密中除了第十轮加密有所不同外其它九轮是一样的:

//第一轮到第九轮(0~8)
	for(i=0;i<9;i++)
	{
		she(p);
		left(p);
		column(p,q);
		keyextension(k,i);
		addbyte16(p,k);
	}

第十轮相对于其他九轮少了列混淆:

//第十轮
	she(p);
	left(p);
	keyextension(k,9);
	addbyte16(p,k);

主函数

int main(int argc, char *argv[]) {
	int i, j;
	int u[4][4],key[4][4];

//输入明文
	printf("input plaintext:\n");
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			scanf("%x",&u[j][i]);

//输入密钥
	printf("input key:\n");
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			scanf("%x",&key[j][i]);

//列混淆常数矩阵
	int u1[4][4] =
	{
		0x02,0x03,0x01,0x01,
		0x01,0x02,0x03,0x01,
		0x01,0x01,0x02,0x03,
		0x03,0x01,0x01,0x02
	};
	int *p = &u[0][0];
	int *q = &u1[0][0];
	int *k = &key[0][0];

//第一轮前
	addbyte16(p,k);

//第一轮到第九轮(0~8)
	for(i=0;i<9;i++)
	{
		she(p);
		left(p);
		column(p,q);
		keyextension(k,i);
		addbyte16(p,k);
	}

//第十轮
	she(p);
	left(p);
	keyextension(k,9);
	addbyte16(p,k);

//密文输出
	printf("ciphertext:\n");
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)printf("%x ", u[i][j]);
		printf("\n");
	}

	system("pause");
	return 0;
}

AES c语言实现

#include <stdio.h>
#include <stdlib.h>

void she(int *p)//S盒
{
	int i;
	int s[256] =
	{
	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
	};
	for (i = 0; i < 16; i++)*(p + i) = s[*(p + i)];
}

void gshe(int *p)//g-S盒
{
	int i;
	int s[256] =
	{
	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
	};
	for (i = 0; i < 4; i++)*(p + i) = s[*(p + i)];
}

void left(int *p)//行移位
{
	int i, j;
	int b[4][4];
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			b[j][i] = *(p + j * 4 + (i + j) % 4);
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			*(p + i + j * 4) = b[j][i];
}

void addbyte16(int *p, int *q)//4*4矩阵字节加法,结果保存到左加数,也为轮密钥加
{
	int i, j;
	for (j = 0; j < 4; j++)for (i = 0; i < 4; i++)
		*(p + j * 4 + i) = *(p + j * 4 + i) ^ *(q + j * 4 + i);
}

void addbyte4(int *p, int *q)//1*4矩阵字节加法,结果保存到左加数
{
	int i;
	for (i = 0; i < 4; i++)
		*(p + i) = *(p + i) ^ *(q + i);
}

int mul(int b,int a)//有限域上的乘法,只定义了01、02、03与其他数相乘
{
	if (b == 0x02)
	{
		if (a < 0x80)a = (a * 0x02);
		else a = ((a % 0x80) * 0x02) ^ 0x1b;
	}
	if (b == 0x03)
	{
		if (a < 0x80)b = (a * 0x02);
		else b = ((a % 0x80) * 0x02) ^ 0x1b;
		a = a^b;
	}
	return a;
}

void column(int *p, int *q)//列混淆
{
	int i, j;
	int a[4][4];
	for (j = 0; j < 4; j++)for (i = 0; i < 4; i++)
	{
		a[j][i] = mul(*(q + j * 4 + 0), *(p + 0 * 4 + i)) ^ mul(*(q + j * 4 + 1), *(p + 1 * 4 + i)) ^
			mul(*(q + j * 4 + 2), *(p + 2 * 4 + i)) ^ mul(*(q + j * 4 + 3), *(p + 3 * 4 + i));
	}
	for (j = 0; j < 4; j++)for (i = 0; i < 4; i++)*(p + j * 4 + i) = a[j][i];
}

void g(int *p,int j)//g函数
{
	int rc[10] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36 };
	int i,a = *p;
	for (i = 0; i < 3; i++)*(p+i) = *(p + i + 1);*(p + 3) = a;
	gshe(p);
	*p = *p^rc[j];
 }

void keyextension(int *p, int j)//密钥扩展
{
	int a[4], b[4], c[4], d[4], d1[4];
	int i, *q1 = &a[0], *q2 = &b[0], *q3 = &c[0], *q4 = &d[0], *q5 = &d1[0];
	for (i = 0; i < 4; i++)a[i] = *(p + i * 4 + 0);
	for (i = 0; i < 4; i++)b[i] = *(p + i * 4 + 1);
	for (i = 0; i < 4; i++)c[i] = *(p + i * 4 + 2);
	for (i = 0; i < 4; i++)d[i] = *(p + i * 4 + 3);
	for (i = 0; i < 4; i++)d1[i] = *(p + i * 4 + 3);
	g(q5, j);
	addbyte4(q1, q5);
	addbyte4(q2, q1);
	addbyte4(q3, q2);
	addbyte4(q4, q3);
	for (i = 0; i < 4; i++)*(p + i * 4 + 0) = a[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 1) = b[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 2) = c[i];
	for (i = 0; i < 4; i++)*(p + i * 4 + 3) = d[i];
}

int main(int argc, char *argv[]) {
	int i, j;
	int u[4][4],key[4][4];

//输入明文
	printf("input plaintext:\n");
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			scanf("%x",&u[j][i]);

//输入密钥
	printf("input key:\n");
	for (j = 0; j < 4; j++)
		for (i = 0; i < 4; i++)
			scanf("%x",&key[j][i]);

//列混淆常数矩阵
	int u1[4][4] =
	{
		0x02,0x03,0x01,0x01,
		0x01,0x02,0x03,0x01,
		0x01,0x01,0x02,0x03,
		0x03,0x01,0x01,0x02
	};
	int *p = &u[0][0];
	int *q = &u1[0][0];
	int *k = &key[0][0];

//第一轮前
	addbyte16(p,k);

//第一轮到第九轮(0~8)
	for(i=0;i<9;i++)
	{
		she(p);
		left(p);
		column(p,q);
		keyextension(k,i);
		addbyte16(p,k);
	}

//第十轮
	she(p);
	left(p);
	keyextension(k,9);
	addbyte16(p,k);

//密文输出
	printf("ciphertext:\n");
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)printf("%x ", u[i][j]);
		printf("\n");
	}

	system("pause");
	return 0;
}