PRESENT加密算法(c++实现)
简介PRESENT加密算法在2007 年由来自德国波鸿鲁尔大学的 Bogdanov 在 CHES 会议中发表。PRESENT加密算法为一种轻量级分组密码算法,采用了 置换网络(SPN)结构,一共迭代 31 轮,分块(组)长度为 64 比特位(位),密钥长度支持 80 比特位和 128比特位。PRESENT 密码算法在硬件实现上具有极高的效率且需要较少的逻辑单元。在实际使用中密钥长度通常一般采用80
简介
PRESENT加密算法在2007 年由来自德国波鸿鲁尔大学的 Bogdanov 在 CHES 会议中发表。PRESENT加密算法为一种轻量级分组密码算法,采用了 置换网络(SPN)结构,一共迭代 31 轮,分块(组)长度为 64 比特位(位),密钥长度支持 80 比特位和 128比特位。PRESENT 密码算法在硬件实现上具有极高的效率且需要较少的逻辑单元。在实际使用中密钥长度通常一般采用80 比特位。本文也以80位比特密钥来实现PRESENT。
加密流程

如图PRESENT加密一共有31轮,每轮有3个操作,轮密钥加,字节代换,P置换,在最后输出时再进行一次白化操作,得到最后的密文。
密钥扩展
每次使用的密钥即与明文进行轮密钥加异或操作的为密钥的高646464位,即K63K62⋯K0=k79k78⋯k16K_{63}K_{62}\cdots K_0=k_{79}k_{78}\cdots k_{16}K63K62⋯K0=k79k78⋯k16
密钥扩展流程如下:
上一轮的密钥为
k79k78⋯k1k0k_{79}k_{78}\cdots k_1k_0k79k78⋯k1k0
循环左移616161位,即向高位方向循环移动616161位变为:
[k79k78⋯k1k0]=[k18k17⋯k0k79k78⋯k19][k_{79}k_{78}\cdots k_1k_0]=[k_{18}k_{17}\cdots k_0k_{79}k_{78}\cdots k_{19}][k79k78⋯k1k0]=[k18k17⋯k0k79k78⋯k19]
循环左移结束后对最高位的半字节即444位进行字节代换:
[k79k78k77k76]=S[k79k78k77k76][k_{79}k_{78}k_{77}k_{76}]=S[k_{79}k_{78}k_{77}k_{76}][k79k78k77k76]=S[k79k78k77k76]
对其中的k19k18k17k16k15k_{19}k_{18}k_{17}k_{16}k_{15}k19k18k17k16k15与加密轮数计数器round_counterround\_counterround_counter(round_counter∈1∼31round\_counter \in 1\sim 31round_counter∈1∼31)进行异或:
[k19k18k17k16k15]=[k19k18k17k16k15]⊕round_counter[k_{19}k_{18}k_{17}k_{16}k_{15}]=[k_{19}k_{18}k_{17}k_{16}k_{15}]\oplus round\_counter[k19k18k17k16k15]=[k19k18k17k16k15]⊕round_counter
用c++c++c++实现时,对于明文和密钥的数据结构我都使用的bitsetbitsetbitset进行,bitsetbitsetbitset支持对二进制位直接以下标访问,并支持位运算。
第一步中的循环左移我们可以我们对密钥keykeykey左移616161和右移191919的结果按位或后得到,其他的我们按照规则直接模拟即可。
void keyUpdate(bitset<80> &key,bitset<5> rc)
{
key = (key<<61)|(key>>19);
bitset<4> s=s_box[(key[79]<<3)+(key[78]<<2)+(key[77]<<1)+key[76]];//左移的优先级比加减要低所以要加括号
key[79]=s[3];
key[78]=s[2];
key[77]=s[1];
key[76]=s[0];
key[19]=key[19]^rc[4];
key[18]=key[18]^rc[3];
key[17]=key[17]^rc[2];
key[16]=key[16]^rc[1];
key[15]=key[15]^rc[0];
}
轮密钥加
轮密钥加,将加密的中间状态statestatestate与密钥的高646464位进行异或后输出:
statei=statei⊕keyistate_{i}=state_i\oplus key_{i}statei=statei⊕keyi
所以我们有代码:
void addRoundKey(bitset<64> &state,const bitset<80>key)
{
for(int i=0; i<64; i++)
state[63-i]=state[63-i]^key[79-i];
}
字节代换
字节代换是将输入的444位二进制传入SBOXSBOXSBOX中,以对应下标输出SBOXSBOXSBOX中的值,其为非线性变化。在PRESENT中以每一个半字节即444位为一组进行字节代换,假设中间状态statestatestate为b63b62⋯b0b_{63}b_{62}\cdots b_{0}b63b62⋯b0,可以看作161616个半字节数据即w15w14⋯w0w_{15}w_{14}\cdots w_{0}w15w14⋯w0,其中wi=b4∗i+3b4∗i+2b4∗i+1b4∗iw_i=b_{4*i+3}b_{4*i+2}b_{4*i+1}b_{4*i}wi=b4∗i+3b4∗i+2b4∗i+1b4∗i。经过字节代换后输出为S[wi]S[w_i]S[wi]。
加密时的SSS盒如下:
| x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| S[x] | 0xC | 0x5 | 0x6 | 0xB | 0x9 | 0x0 | 0xA | 0xD | 0x3 | 0xE | 0xF | 0x8 | 0x4 | 0x7 | 0x1 | 0x2 |
解密时的Inv_SInv\_SInv_S盒如下:
| x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| S[x] | 0x5 | 0xE | 0xF | 0x8 | 0xC | 0x1 | 0x2 | 0xD | 0xB | 0x4 | 0x6 | 0x3 | 0x0 | 0x7 | 0x9 | 0xA |
bitset<4> s_box[16]= {0xC,0x5,0x6,0xB,0x9,0x0,0xA,0xD,0x3,0xE,0xF,0x8,0x4,0x7,0x1,0x2};
bitset<4> inv_s_box[16]= {0x5,0xE,0xF,0x8,0xC,0x1,0x2,0xD,0xB,0x4,0x6,0x3,0x0,0x7,0x9,0xA};
可以得到加密时的字节代换代码:
void SubByte(bitset<64> &state)
{
for(int i=0; i<64; i+=4)
{
bitset<4> s=s_box[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
state[i]=s[0];
state[i+1]=s[1];
state[i+2]=s[2];
state[i+3]=s[3];
}
}
可以得到解密时的逆字节代换为:
void InvSubByte(bitset<64> &state)
{
for(int i=0; i<64; i+=4)
{
bitset<4> s=inv_s_box[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
state[i]=s[0];
state[i+1]=s[1];
state[i+2]=s[2];
state[i+3]=s[3];
}
}
P置换
PPP置换实际上为位移操作,PPP置换可以用查表的方式也可以直接使用公式,我这里直接使用公式有:
P[i]={16×imod 630≤i≤6363i=63P[i]=\left\{\begin{matrix} 16\times i \mod 63& 0\leq i\leq 63 \\ 63& i=63 \end{matrix}\right.P[i]={16×imod63630≤i≤63i=63
即有加密时PPP置换为(以当前状态statestatestate为例):
state[i×16mod 63]={state[i]0≤i≤63state[63]i=63state[i\times16\mod 63]=\left\{\begin{matrix} state[i]& 0\leq i\leq 63 \\ state[63]& i=63 \end{matrix}\right.state[i×16mod63]={state[i]state[63]0≤i≤63i=63
解密时PPP置换为:
state[i]={state[i×16mod 63]0≤i≤63state[63]i=63state[i]=\left\{\begin{matrix} state[i\times16\mod 63]& 0\leq i\leq 63 \\ state[63]& i=63 \end{matrix}\right.state[i]={state[i×16mod63]state[63]0≤i≤63i=63
可以得到加密时代码:
void PSub(bitset<64> &state)
{
bitset<64> tmp(0);
for(int i=0; i<63; i++)
tmp[i*16%63]=state[i];
tmp[63]=state[63];
state=tmp;
}
解密时代码:
void InvPSub(bitset<64> &state)
{
bitset<64> tmp(0);
for(int i=0; i<63; i++)
tmp[i]=state[i*16%63];
tmp[63]=state[63];
state=tmp;
}
整体代码
我们将整个过程进行整合一下就可以得到不超过100100100行的代码了。
#include <bits/stdc++.h>
using namespace std;
bitset<4> s_box[16]= {0xC,0x5,0x6,0xB,0x9,0x0,0xA,0xD,0x3,0xE,0xF,0x8,0x4,0x7,0x1,0x2};
bitset<4> inv_s_box[16]= {0x5,0xE,0xF,0x8,0xC,0x1,0x2,0xD,0xB,0x4,0x6,0x3,0x0,0x7,0x9,0xA};
void addRoundKey(bitset<64> &state,const bitset<80>key)
{
for(int i=0; i<64; i++)
state[63-i]=state[63-i]^key[79-i];
}
void SubByte(bitset<64> &state)
{
for(int i=0; i<64; i+=4)
{
bitset<4> s=s_box[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
state[i]=s[0];
state[i+1]=s[1];
state[i+2]=s[2];
state[i+3]=s[3];
}
}
void InvSubByte(bitset<64> &state)
{
for(int i=0; i<64; i+=4)
{
bitset<4> s=inv_s_box[state[i]+(state[i+1]<<1)+(state[i+2]<<2)+(state[i+3]<<3)];
state[i]=s[0];
state[i+1]=s[1];
state[i+2]=s[2];
state[i+3]=s[3];
}
}
void PSub(bitset<64> &state)
{
bitset<64> tmp(0);
for(int i=0; i<63; i++)
tmp[i*16%63]=state[i];
tmp[63]=state[63];
state=tmp;
}
void InvPSub(bitset<64> &state)
{
bitset<64> tmp(0);
for(int i=0; i<63; i++)
tmp[i]=state[i*16%63];
tmp[63]=state[63];
state=tmp;
}
void keyUpdate(bitset<80> &key,bitset<5> rc)
{
key = (key<<61)|(key>>19);
bitset<4> s=s_box[(key[79]<<3)+(key[78]<<2)+(key[77]<<1)+key[76]];
key[79]=s[3];
key[78]=s[2];
key[77]=s[1];
key[76]=s[0];
key[19]=key[19]^rc[4];
key[18]=key[18]^rc[3];
key[17]=key[17]^rc[2];
key[16]=key[16]^rc[1];
key[15]=key[15]^rc[0];
}
bitset<64> encrypt(bitset<64> state,bitset<80> key)
{
for(int RC=1; RC<32; RC++)
{
addRoundKey(state,key);
SubByte(state);
PSub(state);
keyUpdate(key,RC);
}
addRoundKey(state,key);
return state;
}
bitset<64> decrypt(bitset<64> state,bitset<80> key)
{
vector<bitset<80> >k;
k.push_back(key);
for(int i=1;i<32;i++)
{
keyUpdate(key,i);
k.push_back(key);
}
for(int i=31; i>=1; i--)
{
addRoundKey(state,k[i]);
InvPSub(state);
InvSubByte(state);
}
addRoundKey(state,k[0]);
return state;
}
int main()
{
bitset<64> plain = 0;
bitset<80> key = 0;
bitset<64> cipher = encrypt(plain,key);
cout<<cipher<<endl;
cout<<decrypt(cipher,key)<<endl;
return 0;
}
//5579c1387b228445
用密文为000和密钥为000测试时加密二进制结果从高位到低位输出为:
010101010111100111000001001110000111101100100010100001000100010101010101011110011100000100111000011110110010001010000100010001010101010101111001110000010011100001111011001000101000010001000101
其化为161616进制为:
0x5579c1387b2284450x5579c1387b2284450x5579c1387b228445
for(int i=15;i>=0;i--)
printf("%x",((cipher[i*4+3]<<3)+(cipher[i*4+2]<<2)+(cipher[i*4+1]<<1)+cipher[i*4]));
更多推荐



所有评论(0)