文章目录
一 基础知识
1.c语言如何储存二维数组
在C语言中,数组是一种基本的数据结构,用于存储相同类型的元素集合。数组可以是一维的也可以是多维的(如二维数组)。
一维数组
一维数组是最简单的数组形式,它包含一系列相同类型的元素。
-
定义一个一维长度为四的数组:
int a[4]; // 定义一个名为a的一维数组,包含4个int类型的元素。
这里没有对数组
a
的元素进行初始化,所以它们的值是未定义的。 -
定义一个一维长度为四的数组并初始化:
int b[4] = {0, 1, 2, 3}; // 定义一个名为b的一维数组,并初始化元素为0, 1, 2, 3。
数组
b
的每个元素都被显式地初始化了。 -
定义一个int类型指针指向b[0]:
int *p = &b[0]; // 定义一个指针p,指向数组b的第一个元素b[0]。
这里
&b[0]
是取数组b
第一个元素的地址,然后让指针p
指向这个地址。 -
令指针p指向的b[0]=4:
*p = 4; // 将指针p指向的元素(即b[0])的值设置为4。
由于
p
指向b[0]
,这个操作实际上是将b[0]
的值改为4。 -
令指针p指向的b[0]的下一个数组元素b[1]=5:
*(p+1) = 5; // 将指针p指向的下一个元素(即b[1])的值设置为5。
p+1
是将指针p
向前移动一个元素的大小(因为p
是指向int
的指针,所以移动sizeof(int)
个字节),然后通过解引用操作符*
来修改这个位置的值。
二维数组
二维数组可以看作是数组的数组,即数组的每个元素本身也是一个数组。
-
定义一个4*4的二维数组并初始化:
int a[4][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15} };
这里定义了一个名为
a
的二维数组,它有4行4列,并且每个元素都被显式地初始化了。 -
定义一个int类型指针指向a[0][0]:
int *p = &a[0][0]; // 定义一个指针p,指向二维数组a的第一个元素a[0][0]。
这里
&a[0][0]
是取二维数组a
第一个元素的地址,然后让指针p
指向这个地址。 -
令a[2][1]=5:
*(p + 2*4 + 1) = 5; // 将二维数组a的第3行第2列的元素(即a[2][1])的值设置为5。
这里
p + 2*4 + 1
的计算是基于二维数组在内存中的线性存储方式。2*4
是因为每行有4个元素,所以跳过两行需要移动2*4
个元素的位置,然后+1
是因为我们要访问的是第2列的元素,所以再加上1个元素的位置。
在C语言中,二维数组在内存中是连续存储的,可以看作是一个长度为行数*列数
的一维数组。因此,可以通过计算偏移量来访问二维数组的任意元素。上述代码中的*(p + 2*4 + 1)
实际上是利用了这一点,通过指针算术来访问特定的数组元素。
2.异或运算
异或运算符(^
)是一种位运算符,它对两个数的对应位进行异或操作。异或的规则是:如果两个比较的位相同,则结果是0;如果两个比较的位不同,则结果是1。
在C语言中,异或运算符可以用于整数类型的数据。例如:
int a = 0x16; // 十六进制数,二进制表示为00010110
int b = 0x23; // 十六进制数,二进制表示为00100011
int result = a ^ b; // 异或结果,二进制表示为00110101,即0x35
异或运算在加密算法中非常有用,因为它具有一些特殊的性质,比如:
- 任何数和0进行异或运算结果仍然是原数(自反性)。
- 任何数和自身进行异或运算结果是0(幂等性)。
- 异或运算是可逆的,即
(a ^ b) ^ b = a
。 - 异或运算满足交换律和结合律。
这些性质使得异或运算在数据加密和解密过程中扮演着重要的角色,尤其是在AES加密算法中。AES算法中广泛使用了异或运算来混合数据位。
二 加密第一步——做好分组和异或运算
AES(高级加密标准)是一种对称密钥加密算法,它使用相同的密钥进行加密和解密。AES加密算法的核心是多轮的变换,每一轮都包括四个基本步骤:轮密钥加(AddRoundKey)、替换(SubBytes)、行移位(ShiftRows)和列混淆(MixColumns)。在第一轮加密之前,AES算法会进行一次特殊的操作,即轮密钥加,这一步实际上是将明文分组与初始密钥进行异或运算。
算法原理
在AES中,明文被分割成固定大小的分组(通常是128位,即16个字节),每个字节被视为一个矩阵中的元素。初始密钥也是128位。轮密钥加操作就是将明文分组和初始密钥进行按位异或操作,结果作为第一轮加密的输入。
C语言实现思路
要在C语言中实现轮密钥加,我们可以遵循以下步骤:
-
定义矩阵:由于AES处理的是128位数据,我们可以将其视为一个4x4的字节矩阵。因此,我们需要定义两个4x4的二维数组,一个用于存储明文分组,另一个用于存储密钥。
-
初始化矩阵:将明文分组和密钥的字节分别填充到对应的矩阵中。
-
异或操作:遍历这两个矩阵的每个元素,对它们进行异或操作。
-
函数封装:将上述操作封装成一个函数,以便在主函数中调用。
详细实现步骤
以下是用C语言实现轮密钥加的详细步骤:
这段C代码实现了AES加密算法中的轮密钥加(AddRoundKey)步骤。轮密钥加是AES算法中的一个重要步骤,它涉及将明文块与轮密钥进行按位异或操作
void addbyte16(int *p, int *k) // 轮密钥加,结果保存在左参数,也为4*4数组异或
void
:指定函数没有返回值。addbyte16
:函数名,表示执行16个字节的添加操作,即128位的异或操作。int *p
:第一个参数,指向一个包含明文块的数组的指针。int *k
:第二个参数,指向一个包含轮密钥的数组的指针。
{
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);
}
-
int i, j;
:声明两个循环变量i
和j
,用于遍历数组的行和列。 -
for (j = 0; j < 4; j++)
:外层循环,遍历4行。 -
for (i = 0; i < 4; i++)
:内层循环,遍历每行的4列。 -
*(p + j * 4 + i) = *(p + j * 4 + i) ^ *(k + j * 4 + i);
:执行异或操作。这里p
和k
是指向数组的指针,j * 4 + i
计算出当前要访问的元素的索引。*
操作符用于解引用指针,即访问指针指向的值。^
是C语言中的按位异或运算符。 -
*(p + j * 4 + i)
:访问明文数组中第j
行第i
列的元素。 -
*(k + j * 4 + i)
:访问密钥数组中第j
行第i
列的元素。 -
*(p + j * 4 + i) ^ *(k + j * 4 + i)
:将明文块的当前元素与对应的密钥元素进行异或操作,并将结果存回明文块的当前位置。
这个函数通过两层循环遍历明文块和密钥的每个字节,对每个对应的字节进行异或操作,实现AES算法中的轮密钥加步骤。这种操作是AES加密过程中的第一步,为后续的加密步骤(如字节替换、行移位、列混淆)做准备。
三 S盒子——对每个字节映射更安全
字节代替(S盒)算法原理
在AES加密算法中,字节代替(SubBytes)步骤是将每个字节的值通过查表的方式替换成另一个值。这个表被称为S盒(S-Box)。S盒的目的是提供一个非线性的变换,增加密码的安全性。
- 输入:一个字节,例如0x16。
- 处理:将该字节的高四位(高半字节,例如0x1)作为行索引,低四位(低半字节,例如0x6)作为列索引。
- 查表:在S盒表中查找对应的行和列的交叉点的值,这个值就是输出。例如,0x16的高四位是0x1,低四位是0x6,所以在S盒表中查找第1行第6列的值,得到的输出是0x47。
C代码实现思路
-
定义S盒:首先,需要定义一个16x16的S盒,通常以一维数组的形式实现,因为一维数组可以通过计算
index = row * 16 + column
来访问对应的元素。 -
输入处理:对于输入的每个字节,将其作为数组的索引来获取S盒中的新值。
-
替换操作:将每个字节替换为其在S盒中对应的新值。
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)];
}
int s[256]
:定义了一个包含256个元素的一维数组,用于存储S盒的值。for (i = 0; i < 16; i++) *(p + i) = s[*(p + i)];
:循环遍历输入的每个字节,使用S盒中的值进行替换。*(p + i)
获取当前字节的值,s[*(p + i)]
从S盒中获取对应的新值,并将其赋值回原位置。
通过查表的方式将每个字节的值替换为S盒中对应的值,增加了加密的复杂性和安全性。
四 行位移——逐行递增的移动方式
行移位(ShiftRows)算法原理
行移位是AES加密算法中的一个重要步骤,它涉及到对状态矩阵中的行进行循环左移位操作。具体来说,这个步骤会按照以下规则对4x4的状态矩阵进行处理:
- 第1行(从0开始编号)保持不变,左移0位。
- 第2行左移1位。
- 第3行左移2位。
- 第4行左移3位。
这意味着每一行的每个元素都会根据其行号和列号进行相应的循环位移。例如,第2行的第一个元素(原本位于位置(2,0))会移动到第2行的第二个位置(即原本第2行的第二个元素会移动到第三个位置,以此类推,最后一个元素会绕回到行首)。
C代码实现思路
在C语言中,我们可以通过以下步骤实现行移位:
- 定义临时矩阵:首先,定义一个4x4的临时矩阵
b
,用于存储行移位后的结果。 - 执行行移位:通过循环遍历状态矩阵
p
的每个元素,并根据行移位规则计算其在b
中的新位置。具体来说,对于p
中的每个元素p[j*4+i]
(其中j
是行索引,i
是列索引),其在b
中的新位置为b[j][i] = p[(j*4 + (i + j) % 4)]
。 - 更新原矩阵:将临时矩阵
b
中的元素复制回原矩阵p
,以完成行移位操作。
C代码实现
void left(int *p) { // 行移位
int i, j;
int b[4][4]; // 定义一个4x4的临时矩阵b
// 执行行移位,将结果存储在临时矩阵b中
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
b[j][i] = *(p + j * 4 + (i + j) % 4); // 根据行移位规则计算新位置
}
}
// 将临时矩阵b的结果复制回原矩阵p
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
*(p + i + j * 4) = b[j][i]; // 更新原矩阵p
}
}
}
这段代码首先定义了一个4x4的临时矩阵b
,然后通过两层循环遍历原矩阵p
的每个元素,并根据行移位规则计算其在b
中的新位置。最后,将b
中的结果复制回p
,完成行移位操作。这种方法简单直观,能够有效地实现AES算法中的行移位步骤。
列混淆——跳动在有限域的运算
列混淆(MixColumns)算法原理
列混淆是AES算法中的一个重要步骤,它作用于状态矩阵的每一列。这一步骤通过与一个固定的多项式矩阵相乘来实现,这个矩阵被称为列混淆矩阵。在AES中,列混淆矩阵是固定的,并且定义在有限域GF(2^8)上,这意味着所有的算术运算(包括乘法和加法)都是在模2的条件下进行的,即加法等同于异或操作。
在GF(2^8)中,乘法是通过将两个元素相乘,然后取结果模一个不可约多项式来完成的。AES算法使用的不可约多项式是 ( x^8 + x^4 + x^3 + x + 1 )。这意味着任何乘法操作的结果都必须通过这个多项式来化简。
C代码实现思路
-
定义乘法函数:首先,我们需要定义一个函数
mul
来处理GF(2^8)上的乘法。这个函数需要处理两种情况:当乘数是0x02和0x03时,因为这两种情况涉及到特殊的模运算。 -
矩阵乘法:然后,我们需要实现矩阵乘法。对于状态矩阵的每一列,我们都需要与列混淆矩阵相乘,并将结果存储在一个新的矩阵中。
-
更新状态矩阵:最后,我们将新计算出的矩阵的值复制回原始状态矩阵,以完成列混淆步骤。
C代码实现
int mul(int b, int a) { // 有限域上的乘法
if (b == 0x02) {
if (a < 0x80) a = (a * 0x02);
else a = ((a % 0x80) * 0x02) ^ 0x1b;
} else 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];
}
}
}
-
mul
函数:这个函数处理了GF(2^8)上的乘法。对于0x02和0x03的乘法,它使用了特殊的规则来确保结果符合有限域的性质。 -
column
函数:这个函数实现了列混淆的矩阵乘法。它首先计算了新矩阵的值,然后将这些值复制回原始矩阵。
这种实现方式确保了AES算法的列混淆步骤可以正确执行,同时保持了代码的清晰和效率。
密钥扩展
密钥扩展算法原理
在AES算法中,密钥扩展(Key Expansion)是将初始密钥扩展成一系列轮密钥的过程。这个过程对于AES-128、AES-192和AES-256的密钥长度是不同的。密钥扩展的结果是一个密钥调度,包含了多轮加密所需的所有轮密钥。
对于AES-128,初始密钥是128位,最终会扩展成11个轮密钥,每个轮密钥也是128位。密钥扩展的过程包括以下几个步骤:
-
初始密钥:将初始密钥分成四个16位的单词(4字节),形成一个4x4的矩阵。
-
生成轮密钥:通过一系列的操作生成后续的轮密钥。这些操作包括:
- 轮常数(Round Constants, RC):在G函数中使用,每个轮次使用不同的轮常数。
- 字节替换(SubWord, SW):使用S盒对4个字节进行替换。
- 行移位(RotWord, RCon):将4x4矩阵的第一行进行循环左移。
- 异或(XOR):将生成的4x4矩阵与前一个轮密钥进行异或。
-
G函数:对4x4矩阵的某一行进行变换,生成新的4个字节。
C代码实现思路
-
将二维数组分成四个竖向数组:在C语言中,没有竖向数组,因此需要将二维数组分成四个一维数组。
-
G-S盒实现:G-S盒与S盒类似,但是作用于4字节的数组。
-
1x4数组的加法:实现两个1x4数组的异或操作。
-
G函数:实现G函数,包括左移一位操作和与RC数组的异或。
-
密钥扩展:实现密钥扩展函数,包括对每个轮密钥的生成。
C代码实现
void gshe(int *p) { /* G-S盒 */ }
void addbyte4(int *p, int *q) { /* 1x4数组的加法 */ }
void g(int *p, int j) { /* G函数 */ }
void keyextension(int *p, int 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);
// 复制d数组到d1数组
for (i = 0; i < 4; i++) d1[i] = d[i];
// 应用G函数和轮常数
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算法中的密钥扩展步骤,通过一系列的操作将初始密钥扩展成多轮密钥,为AES加密过程中的每一轮提供所需的密钥。
总设计流程
在AES加密算法中,主函数的设计思路是按照AES的加密流程来组织的。AES加密流程包括多个轮次的处理,每一轮次包括四个主要的步骤:字节替换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。以下是主函数的设计思路和步骤:
1. 初始化和输入
- 明文和密钥输入:首先,程序需要从用户那里获取明文和密钥。明文和密钥都是128位(16字节)的数据,分别存储在4x4的二维数组
u
和key
中。
2. 第一轮前的轮密钥加
- 轮密钥加:在第一轮加密之前,需要将明文与初始密钥进行异或操作,这一步称为轮密钥加。这一步确保了密钥与明文的第一次混合。
3. 循环处理9轮
- 循环处理:对于前9轮,每一轮都包括四个步骤:
- 字节替换(SubBytes):对状态矩阵中的每个字节应用S盒变换。
- 行移位(ShiftRows):对状态矩阵的行进行循环移位。
- 列混淆(MixColumns):对状态矩阵的列进行特定的线性变换。
- 轮密钥加(AddRoundKey):将当前轮的轮密钥与状态矩阵进行异或操作。
4. 第十轮的特殊处理
- 第十轮:第十轮与前九轮的主要区别在于它不包括列混淆步骤。这一轮只包括字节替换、行移位和轮密钥加。
5. 输出密文
- 密文输出:在所有轮次处理完成后,输出最终的状态矩阵,即密文。
6. 代码实现
以下是主函数的代码实现,其中包含了上述所有步骤:
#include <stdio.h>
#include <stdlib.h>
// 假设所有必要的函数(如she, left, column, addbyte16, keyextension等)已定义
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加密算法的核心流程,从输入明文和密钥,到执行多轮加密处理,最终输出加密后的密文。
代码举例
#include <stdio.h>
#include <stdlib.h>
// S盒替换函数
void she(int *p) {
int i;
int s[256] = {
// S盒值,省略了具体值...
};
for (i = 0; i < 16; i++) {
*(p + i) = s[*(p + i)]; // 将每个字节通过S盒进行替换
}
}
// g-S盒替换函数,用于密钥扩展
void gshe(int *p) {
int i;
int s[256] = {
// g-S盒值,省略了具体值...
};
for (i = 0; i < 4; i++) { // g-S盒只作用于4个字节
*(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]; // 将结果写回原矩阵
}
}
}
// 4x4矩阵字节加法函数,用于轮密钥加
void addbyte16(int *p, int *q) {
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);
}
}
}
// 1x4矩阵字节加法函数,用于密钥扩展
void addbyte4(int *p, int *q) {
int i;
for (i = 0; i < 4; i++) {
*(p + i) = *(p + i) ^ *(q + i);
}
}
// 有限域GF(2^8)上的乘法函数
int mul(int b, int a) {
if (b == 0x02) {
if (a < 0x80) a = (a * 0x02);
else a = ((a % 0x80) * 0x02) ^ 0x1b;
} else 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];
}
}
}
// g函数,用于密钥扩展
void g(int *p, int j) {
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);
b[i] = *(p + i * 4 + 1);
c[i] = *(p + i * 4 + 2);
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];
*(p + i * 4 + 1) = b[i];
*(p + i * 4 + 2) = c[i];
*(p + i * 4 + 3) = d[i];
}
}
// 主函数,执行AES加密
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,加密,int,矩阵,++,异或,密钥,数组,原理
From: https://blog.csdn.net/zhuqiyua/article/details/143687070