AES算法逆向分析报告

一、前置知识

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;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值