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置换,在最后输出时再进行一次白化操作,得到最后的密文。
密钥扩展
每次使用的密钥即与明文进行轮密钥加异或操作的为密钥的高
64
64
64位,即
K
63
K
62
⋯
K
0
=
k
79
k
78
⋯
k
16
K_{63}K_{62}\cdots K_0=k_{79}k_{78}\cdots k_{16}
K63K62⋯K0=k79k78⋯k16
密钥扩展流程如下:
上一轮的密钥为
k
79
k
78
⋯
k
1
k
0
k_{79}k_{78}\cdots k_1k_0
k79k78⋯k1k0
循环左移
61
61
61位,即向高位方向循环移动
61
61
61位变为:
[
k
79
k
78
⋯
k
1
k
0
]
=
[
k
18
k
17
⋯
k
0
k
79
k
78
⋯
k
19
]
[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]
循环左移结束后对最高位的半字节即
4
4
4位进行字节代换:
[
k
79
k
78
k
77
k
76
]
=
S
[
k
79
k
78
k
77
k
76
]
[k_{79}k_{78}k_{77}k_{76}]=S[k_{79}k_{78}k_{77}k_{76}]
[k79k78k77k76]=S[k79k78k77k76]
对其中的
k
19
k
18
k
17
k
16
k
15
k_{19}k_{18}k_{17}k_{16}k_{15}
k19k18k17k16k15与加密轮数计数器
r
o
u
n
d
_
c
o
u
n
t
e
r
round\_counter
round_counter(
r
o
u
n
d
_
c
o
u
n
t
e
r
∈
1
∼
31
round\_counter \in 1\sim 31
round_counter∈1∼31)进行异或:
[
k
19
k
18
k
17
k
16
k
15
]
=
[
k
19
k
18
k
17
k
16
k
15
]
⊕
r
o
u
n
d
_
c
o
u
n
t
e
r
[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++实现时,对于明文和密钥的数据结构我都使用的
b
i
t
s
e
t
bitset
bitset进行,
b
i
t
s
e
t
bitset
bitset支持对二进制位直接以下标访问,并支持位运算。
第一步中的循环左移我们可以我们对密钥
k
e
y
key
key左移
61
61
61和右移
19
19
19的结果按位或后得到,其他的我们按照规则直接模拟即可。
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];
}
轮密钥加
轮密钥加,将加密的中间状态
s
t
a
t
e
state
state与密钥的高
64
64
64位进行异或后输出:
s
t
a
t
e
i
=
s
t
a
t
e
i
⊕
k
e
y
i
state_{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];
}
字节代换
字节代换是将输入的
4
4
4位二进制传入
S
B
O
X
SBOX
SBOX中,以对应下标输出
S
B
O
X
SBOX
SBOX中的值,其为非线性变化。在PRESENT中以每一个半字节即
4
4
4位为一组进行字节代换,假设中间状态
s
t
a
t
e
state
state为
b
63
b
62
⋯
b
0
b_{63}b_{62}\cdots b_{0}
b63b62⋯b0,可以看作
16
16
16个半字节数据即
w
15
w
14
⋯
w
0
w_{15}w_{14}\cdots w_{0}
w15w14⋯w0,其中
w
i
=
b
4
∗
i
+
3
b
4
∗
i
+
2
b
4
∗
i
+
1
b
4
∗
i
w_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
[
w
i
]
S[w_i]
S[wi]。
加密时的
S
S
S盒如下:
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 |
解密时的 I n v _ S Inv\_S Inv_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置换
P
P
P置换实际上为位移操作,
P
P
P置换可以用查表的方式也可以直接使用公式,我这里直接使用公式有:
P
[
i
]
=
{
16
×
i
m
o
d
63
0
≤
i
≤
63
63
i
=
63
P[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
即有加密时
P
P
P置换为(以当前状态
s
t
a
t
e
state
state为例):
s
t
a
t
e
[
i
×
16
m
o
d
63
]
=
{
s
t
a
t
e
[
i
]
0
≤
i
≤
63
s
t
a
t
e
[
63
]
i
=
63
state[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
解密时
P
P
P置换为:
s
t
a
t
e
[
i
]
=
{
s
t
a
t
e
[
i
×
16
m
o
d
63
]
0
≤
i
≤
63
s
t
a
t
e
[
63
]
i
=
63
state[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;
}
整体代码
我们将整个过程进行整合一下就可以得到不超过 100 100 100行的代码了。
#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
用密文为
0
0
0和密钥为
0
0
0测试时加密二进制结果从高位到低位输出为:
0101010101111001110000010011100001111011001000101000010001000101
0101010101111001110000010011100001111011001000101000010001000101
0101010101111001110000010011100001111011001000101000010001000101
其化为
16
16
16进制为:
0
x
5579
c
1387
b
228445
0x5579c1387b228445
0x5579c1387b228445
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]));
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)