C++实现AES的S-box

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

预备知识

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

整体实现思路

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

  1. 初始化S-box,使第 x x x行第 y y y列的元素为{ x y xy xy}。
    (代码实现时这一步和第二步合在一个initialize()函数中完成)
  2. 对S-box中的每个元素求乘法逆元
    (求乘法逆元用到扩展欧几里得算法,所以必须要实现GF( 2 8 2^8 28)中的乘法运算)
  3. 对S-box中的公式运用一下公式得到最终的S-box。 b i ′ = b i ⊕ b ( i + 4 ) m o d 8 ⊕ b ( i + 5 ) m o d 8 ⊕ b ( i + 6 ) m o d 8 ⊕ b ( i + 7 ) m o d 8 ⊕ c i b^{\\'}_i=b_i{\oplus}b_{(i+4)mod8}{\oplus}b_{(i+5)mod8}{\oplus}b_{(i+6)mod8}{\oplus}b_{(i+7)mod8}{\oplus}c_i bi=bib(i+4)mod8b(i+5)mod8b(i+6)mod8b(i+7)mod8ci其中 ( c 7 c 6 c 5 c 4 c 3 c 2 c 1 c 0 = ( 01100011 ) (c_7c_6c_5c_4c_3c_2c_1c_0=(01100011) (c7c6c5c4c3c2c1c0=(01100011), 即 c = 0 x 63 c=0x63 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 x x行第 y y y列的元素为{ x y xy xy}(如果不知道为何要这样计算,那就动手举个例子就了解了)。然后如整体实现思路第2步,对各个元素求乘法逆元。

求乘法逆元之前的准备
双字节除法uc divide(unsigned short a, uc b, uc &r)

PS:我觉得这里的实现做得并不好。我本来只用uc类型实现全部代码,但是扩展欧几里得算法中有一个变量(uc inverse(uc b)函数中的q变量)初始化为 q = a / b q=a/b q=a/b,此处 a , 即 a ( x ) , a,即a(x), aa(x) G F ( 2 8 ) GF(2^8) GF(28)的本原多项式,十六进制为 0 x 11 B 0x11B 0x11B,即 a ( x ) = x 8 + x 4 + x 3 + x + 1 a(x)=x^8+x^4+x^3+x+1 a(x)=x8+x4+x3+x+1 b b b G F ( 2 8 ) GF(2^8) GF(28)的元素,是求乘法逆元的参数。由于 a a a为9比特数,至少2个字节才能表示,所以我用了unsigned short类型来表示。如果哪位有可以只用uc类型的方法,请联系1617609852lzc@gmail.com,谢谢!

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 a a b b b分别为被除数和除数,即 a / b a/b a/b;参数 r r r为文章开头说的要用到的C++引用, r r 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;
}
  • 参数及返回值说明 返回 a ∗ b a*b ab

  • 思路 这里就涉及到预备知识中说的 G F ( 2 8 ) GF(2^8) GF(28)知识了,需要了解的是如何进行乘法,这里只给出结论,需要证明的请自行搜素。 f ( x ) = b 7 x 7 + b 6 x 6 + b 5 x 5 + b 4 x 4 + b 3 x 3 + b 2 x 2 + b 1 x + b 0 f(x)=b_7x^7+b_6x^6+b_5x^5+b_4x^4+b_3x^3+b_2x^2+b_1x+b_0 f(x)=b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0 x ∗ f ( x ) = { ( b 6 b 5 b 4 b 3 b 2 b 1 b 0 0 ) ⊕ ( 00011011 )      i f    b 7 = 1 ( b 6 b 5 b 4 b 3 b 2 b 1 b 0 0 )                            i f    b 7 = 0 x*f(x)={\lbrace}^{(b_6b_5b_4b_3b_2b_1b_00)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if\ \ b_7=0}_{(b_6b_5b_4b_3b_2b_1b_00){\oplus}(00011011)\ \ \ \ if\ \ b_7=1} xf(x)={(b6b5b4b3b2b1b00)(00011011)    if  b7=1(b6b5b4b3b2b1b00)                          if  b7=0
    因为上式 x ∗ f ( x ) x*f(x) xf(x)是从 x x x开始的,所以用一个if语句判断 b b 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 b b的乘法逆元

  • 为什么把 v v v的参数注释掉,因为要求的是 b b b的逆元,所以保留 w w w就够了, v v v w w 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 a a 通过某种规则映射到另一个数,并返回这个数

  • 具体映射规则参考整体实现思路第3步

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值