C++实现AES的S-box

PS:本来想用纯C实现的,但是实现过程遇到了困难。实现过程用了C++的引用

预备知识

  • 扩展欧几里得算法
  • 一点有限域GF(28)知识

整体实现思路

PS:参考《密码编码学与网络安全——原理与实践》第七版
第6.3节 AES TRANSFORMATION FUNCTIONS

  1. 初始化S-box,使第 x 行第 y 列的元素为{ xy }。
    (代码实现时这一步和第二步合在一个initialize()函数中完成)
  2. 对S-box中的每个元素求乘法逆元
    (求乘法逆元用到扩展欧几里得算法,所以必须要实现GF( 28 )中的乘法运算)
  3. 对S-box中的公式运用一下公式得到最终的S-box。
    bi=bib(i+4)mod8b(i+5)mod8b(i+6)mod8b(i+7)mod8ci
    其中 (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];
  1. 由于S-box中的元素是字节元素,所以选 unsigned char 作为基本操作类型。为了方便,将其简化声明为 uc 类型。
  2. 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,使第 x 行第 y 列的元素为{ xy }(如果不知道为何要这样计算,那就动手举个例子就了解了)。然后如整体实现思路第2步,对各个元素求乘法逆元。

求乘法逆元之前的准备

双字节除法uc divide(unsigned short a, uc b, uc &r)

PS:我觉得这里的实现做得并不好。我本来只用uc类型实现全部代码,但是扩展欧几里得算法中有一个变量(uc inverse(uc b)函数中的q变量)初始化为 q=a/b ,此处 aa(x) GF(28) 的本原多项式,十六进制为 0x11B ,即 a(x)=x8+x4+x3+x+1 b GF(28) 的元素,是求乘法逆元的参数。由于 a 为9比特数,至少2个字节才能表示,所以我用了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;
}
  • 参数及返回值说明 返回 ab

  • 思路 这里就涉及到预备知识中说的 GF(28) 知识了,需要了解的是如何进行乘法,这里只给出结论,需要证明的请自行搜素。

    f(x)=b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0
    xf(x)={(b6b5b4b3b2b1b00)                          if  b7=0(b6b5b4b3b2b1b00)(00011011)    if  b7=1

    因为上式 xf(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步

猜你喜欢

转载自blog.csdn.net/snowlyw/article/details/78756055
今日推荐