一、前置知识
AES加密算法解析:
目前大部分情况下,AES加密使用的密钥长度为128bit,即16字节,分组长度为16字节,加密的轮函数会执行10次。对于16字节的而言密钥,首先会执行一个密钥扩展的函数,先将密钥以字节的形式排列成4×4的矩阵,四列中每一列会被合并为一个原始密钥,即W[0]—W[3],再在此基础上生成W[4]—W[43],如下图所示:
密钥扩展的规则:
如果i不是4的倍数,则W[i]=W[i-4]⨁W[i-1]
如果i是4的倍数,则W[i]=W[i-4]⨁T(W[i-1])
其中函数T由3部分组成:
a.字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0, b1, b2, b3] 变换成[b1,b2,b3,b0]
b.字节代换:对字循环的结果使用S盒进行字节代换
c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
轮常量Rcon[j]是一个字,其值见下表
第一轮的密钥是W[4]—W[7],以此类推
在轮函数之前,先由明文矩阵转换为状态矩阵,4×4的每个方格中,将明文转为其十六进制表示的形式,如a→0x61
接着是字节代换,大致原理是把状态矩阵中每个字节的高4位作为行值,低4位作为列值,取出S盒中对应的元素作为输出。例如,加密时,状态矩阵中为0x12,则查S盒的第0x01行和0x02列,得到值0xc9,然后以此替换状态矩阵中原有的0x12
然后是行移位,变换情况如图:
列混合是这里面跟信息安全数学关联比较大的一个操作,涉及到基于GF(2^8)上的二元运算,基本操作是矩阵相乘,得到混合矩阵:
对于一个8位的二进制数来说,使用域上的乘法乘以(00000010)等价于左移1位(低位补0)后,再根据情况同(00011011)进行异或运算,设S1 = (a7 a6 a5 a4 a3 a2 a1 a0),刚0x02 * S1如下图所示:
数学原理不再赘述
最后一个步骤是轮密钥加,将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,如下图所示。其中,密钥Ki中每个字W[4i],W[4i+1],W[4i+2],W[4i+3]为32位比特字,包含4个字节,轮密钥加过程可以看成是字逐位异或的结果,也可以看成字节级别或者位级别的操作。也就是说,可以看成S0,S1,S2,S3 组成的32位字与W[4i]的异或运算。
二、初步分析
先将源代码编译运行,看一看程序大致是怎么样的
这个加密程序就一个输入,猜测是将输入的值加密后,与已定义的一个字符串相比较,相等即为通过
我们接着拖进IDA分析
main函数之前是先定义了很多访问字节单元和字的变量,而AES正是基于字节的加密方式,继续往下看
这里可以看到,一开始是对两个数据块进行了赋值(中间的两个0是标志),均是字的单位,基址可以看成0A4H,则偏移地址分别是从9H-19H,共16字节,21H-41H,共32字节。
观察一下发现,前者存储的值是31323334...36H,转为十进制是1234567890123456
后者同理,是abcdefghijklmnopqrstuvwxyzabcdef,这两个应该是main函数里面定义的局部变量,有可能是密钥之类的,接着分析
从42H-61H共32个字节,均是字节的单位,但是这里0xFCH这种值转换成十进制后是乱码,于是先持保留意见
这里操作看起来有点复杂,总结起来就是从81H到后面8个dword之间,即32个字节,都被赋值为0
紧接着来到main函数主体,可以看到调用了printf(),scanf()和aesEncrypt()三个子函数,并且易分析得到,scanf()传进来的也就是我们输入的flag存储在var_41这里,而从41H-21H是一个32字节局部变量,应该是被输入覆盖掉了。再看aesEncrypt(),不难看出这个函数共有五个参数,20H即32,var_81即32位为0的变量,var_41即输入的字符串,10H即16,var_19即1234567890123456。
有两组变量满足 长度-值 的对应关系,最后另有一个新变量var_8,赋值为0。
1FH即十进制的31,var_8在中间的loc_40205C代码块执行完后会自增1,然后再与31比较,小于或等于则继续,可以判断这里是一个for循环,如for(i=0;i<=31;i++)。
看一下中间的那个代码块,前后的两个结构的意思是,值为var_8可以看成偏移地址,接着将var_61作为基址,加上var_8的偏移赋值给edx,后面是var_81偏移后赋值给eax,最后两者比较。类似于将两个数组的每一个元素两两比较,再结合比较成功的输出和比较错误的输出函数调用情况来看,如果一次比较没成功则退出,全部比较成功了才算对。
var_81最开始是0,然而最后却将其与var_61进行比较,且均是32位,所以var_81应该是var_41经过AES加密之后得到的32位密文,var_61是内置的那个正确答案,那么var_19就是16位的密钥,尝试解密:
在程序里面输入这个明文
正确
三、进阶分析
刚刚我们解题时跳过了aesEncrypt()这个函数,现从密码学的角度对其进行逆向分析
在进行加密之前,可以看到子函数是先将传进来这五个参数进行了合法值的校验,根据下面print的信息及各自的值,可以把这五个参数对应重命名一下,便于分辨
这里定义了一些局部变量,并且大多赋值为0,不好分析,先往下看
loc_401D56是经判断五个参数均合法后加密的第一层,可以看到调用了memcpy()和keyExpansion()两个函数,后者正是密钥扩展。memcpy()有三个参数,keyLen和key是我们重命名后的已知变量,Dst是未知的局部变量,根据这个函数的定义,意思是截取密钥key的前keyLen位,即16位复制给Dst,那么Dst就是这个子函数里的原始密钥了
再看keyExpansion(),参数分别为Dst,16,var_17C,我们知道密钥扩展的结果是基于原始密钥W[0]-W[3]生成W[4]-W[43],于是这个var_17C就是最终的完整密钥。(我返回上面查看var_17C的定义是发现它只有4位的字节单位,比较疑惑,后来看了源码后发现是取的地址)
1. keyExpansion()
进入keyExpansion()函数,还是先根据报错信息和合法性比较重命名形参
合法性比较通过之后,来到的第一个代码块是loc_4015F7
这里对var_14和var_18两个局部变量(4位dword)进行了赋值,前者取的是aeskey起始地址,后者偏移了0B0H,即44个字,而AES使用的密钥就是44个字,于是var_14就存储着最终扩展的结果
接下来是一个for循环,在main函数里面有同理的分析,这里的循环次数是4,循环的操作是往左,跳出时往右
来看看左边的loc_401611
w是重命名之后的var_14,首先向edx中存入循环变量var_C的数据段基址,w与这个基址add后便是w[var_C]。接着var_C进行了逻辑左移两位的操作,相当于十进制中的乘4,用key与之相加,得到key+4*var_C,等价于取出key的第一个字节,然后ecx左移18H即24位,最终存入edx,这样循环四次(内部)后就可以把全部32位存储完。
最后更新w的数据段基址,循环变量var_C自加1,外部循环四次后就可以初始化原始密钥于w中。
这之后显而易见,进入到for(i=0;i<=9;i++)这个循环中来,可以判定为10轮的密钥迭代扩展,分析左边的loc_401698
结合AES密钥扩展的三个步骤可以判断,前半部分是T函数,前几步是字循环,w基址往后偏移10H=16位是dword里最后一个字的末位,存储在了edx里面,然后又取到了w偏移0CH的地址,这是最后一个字的初始位,接着再进行S盒代换。可以看到,这里先是取了eax的前半部分al中的一个字拷贝到eax里面,再将这个字进行S盒代换,存储到对应位置上,然后接着每次逻辑右移8的倍数,取到每个字节的两个字作为行列,,最终S盒代换结果存到ecx中(这里确实太凌乱了,我是找的规律)。进行S盒代换完毕之后,就同轮常量Rcon进行异或。
这之后,取到w基址+4,即为w[1]的地址存入ecx,再取偏移地址10H=w[4]的地址,存入edx,最后取w的地址+14H=w[5]的地址,edx和ecx做异或运算的值=w[5],存入edx。以此类推,就得到了W[4]-W[7]的值,最后使w的地址整体+10h,循环9轮之后就能得到密钥扩展的结果。
这之后是一个两层循环,外层循环次数为11,内层次数为4,loc_4017A3中,先取到v[var_C]的地址存入edx,再取到w[var_C]的值,复制到edx的位置上,而右下角的外层循环步进规则是:w每次加一个dword单位,v每次减一个,综上可以判断v是解密密钥,
keyExpansion()分析完毕。
返回至aesEncrypt()函数,var_C清零,这里可以看到又是一个两层循环,其中外层循环次数为我们传入加密函数的第五个参数即32,内层次数为10
外层循环里面,第一个代码块是loc_401D9E,调用了loadStateArray()和addRoundKey()两个函数。前者是加载状态矩阵,有data(输入的字符串)和var_1AC两个参数,于是这个函数的作用就是将明文data转换为可供AES进行字节处理的状态矩阵,存入var_1AC中,我们再回去看看它的在栈中的情况。
可以看到,这里是连续四个4位dword的数据段,对应了4×4的状态矩阵。
2. loadStateArray()
进入loadStateArray()中,很明显是一个两层循环,我们先把变量重命名一下,外层循环变量为i,内层为j,循环次数和步进均是1。
主要的部分是loc_401518,如下图:
这里是先取了j的值存入eax,将其×4后存入edx中,然后取得state的初始地址,偏移edx中的值即4j之后存入ecx,于是ecx中的值就是state+4j,对于data,先是把它自身的地址存到了eax中,自加1之后的地址存在edx,等价于指针向后移一位。之后取了i的值存入edx,与ecx相加得到state+4j+i,被赋予eax中的data值。这段代码大致就是state[j][i]=*data;data++;,循环结束后state中便是标准的状态矩阵了。
返回上一级,轮密钥加addRoundKey()调用的第一个参数是var_18,查看定义,发现它在初始化时被赋值为var_17C,而我们在之前已经分析得到var_17C中有最终的密钥矩阵,那么这一步就是取的其中加密时用的密钥矩阵。
3. addRoundKey()
进入分析
跟前面同理,这里是一个两层循环,内层i外层j都是4次,不多说
看一下loc_40180B
先取到key[j],存入edx中,然后计算3-i的值,再左移3位(等价于×8),8*(3-i)存入ecx,接着edx中的key[j]根据这个值向右移位,结果存入ecx。这一步是在进行转换为字节单位的操作。
我们令局部变量var_4为k,这之后取到了k[i][j]存入eax中,把ecx中的值赋值给eax,综合以上,k中存的就是字节流形式的密钥矩阵。后面的部分与前面类似,取到state[i][j]存入edx中,取到k[i][j]存入eax中(这里有一个自减18H的步骤,目的是将矩阵各位置对齐),最终两者进行异或,轮密钥加的结果存在了edx中。
第一次轮密钥加完毕后,就来到了aesEncrypt()的内层循环,可以看到调用了四个函数:subBytes(),shiftRows(),mixColumns(),addRoundKey(),对应了每一轮中的字节代换,行移位,列混合,轮密钥加这四步,刚刚我们已经分析了轮密钥加,现在来看看前三步。
首先将指向密钥矩阵的指针keyMat后移10H=16位,目的是在下一次轮密钥加时用到W[4]-W[7]。
4. subBytes()
subBytes()函数调用的参数是状态矩阵stateMat,进入看一下
整体来看又是一个两层循环,4*4,只需关注左下角的loc_4018CA
这里是先取了stateMat[i][j]到eax中,后面直接以这个地址作为字节指针在S盒里面寻找对应的值,将其覆盖,最后edx中的指针复位后,指向stateMat[i][j]+j即stateMat[i][j+1],如此循环之后完成字节代换。
5. shiftRows()
shiftRows()函数调用的参数是完成字节代换后的状态矩阵。
这个函数的主体部分是一个四次的循环,定义的局部变量不再展示
loc_40194C由先后几套移位的操作组成,每种操作里面各有四次处理。
如上图标记的第一类操作重复了四次,后面三次以or为结束标志,大致是取到了stateMat[i]的四个字节,然后第一次移位18H=24位,第二次16位,以此类推就将一共32位分四字节存入edx中。
第二类操作如上图所示,var_18+4i取得了edx处的值,可以理解为每次循环时,var_18中存有状态矩阵中的一行,它先被存入了edx,后复制到ebx中,对于循环变量i,先是存入eax,而eax进行了一次左移三位的操作存入ecx,相当于8*i,随后var_18向左移8*i位。接着,类似地,,var_18[i]被复制进edx,8*(4-i)存入ecx,var_18[i]右移位8*(4-i)位,最后将对var_18[i]两个相反方向的移位结果进行或运算,存入edx。
第三类操作跟第一类很类似,这里var_18是已经经过前两步移位操作后得到的状态矩阵中的一行,于是现在要将stateMat按照它来更新。先取到stateMat[i]存入edx中,var_18[i]存入eax,再右移18H=24位,相当于取第一个字节,最后将后者的值赋值给前者,后面的同理,每次右移的位数相差8位,4次操作之后,32位的var_18[i]正好全部复制完毕,行移位操作就完成了。
6. mixColumns()
mixColumns()函数调用的参数是完成行移位之后的状态矩阵,这里面会用到伽罗瓦域上的运算操作。
先进入看:
从30H-21H这里连续16个字节,定义了2311123111233112这样一串数字,而在进行列混合时的实质就是矩阵相乘,规模都是4×4,所以应该把这串数字视作矩阵里面的每个元素。
接下来的是循环结构,因为用的循环变量没有更改,所以看起来比较混乱,但是可以根据箭头的指向能分析出两个4×4的双层循环
先来看第一个循环loc_401B2D
这种结构之前分析过很多次了,先是取到stateMat[i][j]的值存入eax,然后再计算var_8+4i+j即取var_8[i][j]的初始地址存入edx,减去多的18H=24位,最后将eax的值赋值给edx,总的来说就是把状态矩阵存入了一个临时变量var_8。
再看第二个循环loc_401B85
我们把存储着目标相乘矩阵的局部变量var_30命名为M,刚刚的临时变量var_8的值传递给了var_20,命名为temp。这里调用了四次GMul函数,在每次进行调用会进行形参的初始化,拿第一次来说,先取到stateMat[i],赋值给ebx,随后取到temp+j这个地址,存到了eax中,也就是temp[0][j],再转移到edx中去,最后是取到M[i][0]于eax中,temp[0][j]赋值给形参var_3C,M[i][0]赋值给var_40,GMul()函数调用这两个形参进行计算。
7. GMul()
进入GMul()函数分析:
注意到在初始化局部变量阶段,M的值被赋给了var_14,temp被赋给了var_18,另外还有两个变量var_4和var_5都初始为0,后分析得var_4是循环变量i
继续往下走
loc_401A95这里,将M与1进行与运算,相当于判断这个值是否为0,若不为0则先将var_5与temp异或,然后再将temp的最高位(这里的运算是GF(2^8)上的运算,最高位用二进制表示就是80H)与80H异或,所得值赋值给var_C,temp向左移一位。接着判断var_C是否为0,若不为0则将temp与1BH进行异或运算,即模x^8 + x^4 + x^3 + x + 1这个不可约多项式。最后将M右移一位,这里总结起来就是,使用GF(2^8)域上的乘法乘以(00000010)等价于左移1位(低位补0)后,再根据情况同1BH进行异或运算,下图可以表达出这个原理
addRoundKey()已经分析过,那么现在整个轮函数就结束了
8. storeStateArray()
最后一轮结束后调用了storeStateArray()这个函数,调用的参数是var_14和stateMat,倒回去看它的初始化情况:
发现var_14就是aesEncrypt()的第四个参数ct
最后点进这个函数看看:
这个结构就比较简单了,显而易见是一个4×4的两层循环,在左下角的loc_40156E里边,分别取了ct的地址存入eax作为指针,然后取stateMat[i][j]的值赋值给ct,最后ct指针自加1,相当于把矩阵重新排列成字符串,于是加密后的字符串就存储在ct中,aesEncrypt()分析结束。
最后这里就是让明文和密文都自加16字节,开始下一个分组的加密
整个加密过程就是如此
You really know!!!!!
附上这个程序的源码:
#include <stdio.h>
#include <Windows.h>
#define BLOCKSIZE 16
#define LOAD32H(x, y) \
do { (x) = ((unsigned int)((y)[0] & 0xff)<<24) | ((unsigned int)((y)[1] & 0xff)<<16) | \
((unsigned int)((y)[2] & 0xff)<<8) | ((unsigned int)((y)[3] & 0xff));} while(0)
#define STORE32H(x, y) \
do { (y)[0] = (unsigned char)(((x)>>24) & 0xff); (y)[1] = (unsigned char)(((x)>>16) & 0xff); \
(y)[2] = (unsigned char)(((x)>>8) & 0xff); (y)[3] = (unsigned char)((x) & 0xff); } while(0)
/* extract a byte */
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)
/* used for keyExpansion */
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32-(n))))
/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
static const unsigned int rcon[10] = {
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
unsigned char 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
};
unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};
typedef struct{
unsigned int eK[44], dK[44]; // encKey, decKey
int Nr; // 10 rounds
}AesKey;
/* copy in[16] to state[4][4] */
int loadStateArray(unsigned char (*state)[4], const unsigned char *in) {
int i;
int j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[j][i] = *in++;
}
}
return 0;
}
/* copy state[4][4] to out[16] */
int storeStateArray(unsigned char (*state)[4], unsigned char *out) {
int i;
int j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
*out++ = state[j][i];
}
}
return 0;
}
int keyExpansion(const unsigned char *key, unsigned int keyLen, AesKey *aesKey) {
int i;
int j;
unsigned int *w;
unsigned int *v;
if (NULL == key || NULL == aesKey){
printf("keyExpansion param is NULL\n");
return -1;
}
if (keyLen != 16){
printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
return -1;
}
w = aesKey->eK;
v = aesKey->dK;
/* keyLen is 16 Bytes, generate unsigned int W[44]. */
/* W[0-3] */
for (i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4*i);
}
/* W[4-43] */
for (i = 0; i < 10; ++i) {
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}
w = aesKey->eK+44 - 4;
for (j = 0; j < 11; ++j) {
for (i = 0; i < 4; ++i) {
v[i] = w[i];
}
w -= 4;
v += 4;
}
return 0;
}
int addRoundKey(unsigned char (*state)[4], const unsigned int *key) {
int i;
int j;
unsigned char k[4][4];
/* i: row, j: col */
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
k[i][j] = (unsigned char) BYTE(key[j], 3 - i); /* copy uint32 key[4] to uint8 k[4][4] */
state[i][j] ^= k[i][j];
}
}
return 0;
}
int subBytes(unsigned char (*state)[4]) {
/* i: row, j: col */
int i;
int j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[i][j] = S[state[i][j]];
}
}
return 0;
}
int shiftRows(unsigned char (*state)[4]) {
int i;
unsigned int block[4] = {0};
/* i: row */
for (i = 0; i < 4; ++i) {
LOAD32H(block[i], state[i]);
block[i] = ROF32(block[i], 8*i);
STORE32H(block[i], state[i]);
}
return 0;
}
/* Galois Field (256) Multiplication of two Bytes */
unsigned char GMul(unsigned char u, unsigned char v) {
int i;
int j;
int flag;
unsigned char p = 0;
for (i = 0; i < 8; ++i) {
if (u & 0x01) { //
p ^= v;
}
flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
}
u >>= 1;
}
return p;
}
int mixColumns(unsigned char (*state)[4]) {
int i;
int j;
unsigned char tmp[4][4];
unsigned char M[4][4] = {{0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02}};
/* copy state[4][4] to tmp[4][4] */
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j){
tmp[i][j] = state[i][j];
}
}
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}
return 0;
}
int aesEncrypt(const unsigned char *key, unsigned int keyLen, const unsigned char *pt, unsigned char *ct, unsigned int len) {
int i;
int j;
AesKey aesKey;
unsigned char *pos = ct;
const unsigned int *rk = aesKey.eK;
unsigned char out[BLOCKSIZE] = {0};
unsigned char actualKey[16] = {0};
unsigned char state[4][4] = {0};
if (NULL == key || NULL == pt || NULL == ct){
printf("param err.\n");
return -1;
}
if (keyLen > 16){
printf("keyLen must be 16.\n");
return -1;
}
if (len % BLOCKSIZE){
printf("inLen is invalid.\n");
return -1;
}
memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey);
for (i = 0; i < len; i += BLOCKSIZE) {
loadStateArray(state, pt);
addRoundKey(state, rk);
for (j = 1; j < 10; ++j) {
rk += 4;
subBytes(state);
shiftRows(state);
mixColumns(state);
addRoundKey(state, rk);
}
subBytes(state);
shiftRows(state);
addRoundKey(state, rk+4);
storeStateArray(state, pos);
pos += BLOCKSIZE;
pt += BLOCKSIZE;
rk = aesKey.eK;
}
return 0;
}
int main() {
int i;
int j;
const unsigned char key[]="1234567890123456";
unsigned char data[40] = "abcdefghijklmnopqrstuvwxyzabcdef";
unsigned char cipher[32] = {0xfc,0xad,0x71,0x5b,0xd7,0x3b,0x5c,0xb0,0x48,0x8f,0x84,0x0f,0x3b,0xad,0x78,0x89,0xd0,0xe7,0x09,0xd0,0xff,0xd3,0x8c,0x6d,0xfe,0xc5,0x5c,0xcb,0x9f,0x47,0x5b,0x01};
unsigned char ct[32] = {0};
printf("Please provide the Flag:");
scanf("%s",data);
aesEncrypt(key, 16, data, ct, 32);
for(i = 0; i < 32; ++i){
if (cipher[i] != ct[i]){
printf("You really don't know!!!!!\n");
break;
}
}
if(i==32){
printf("You really know!!!!!\n");
}
system("pause");
return 0;
}