简介

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

加密流程

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} K63K62K0=k79k78k16
密钥扩展流程如下:
上一轮的密钥为
k 79 k 78 ⋯ k 1 k 0 k_{79}k_{78}\cdots k_1k_0 k79k78k1k0
循环左移 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}] [k79k78k1k0]=[k18k17k0k79k78k19]
循环左移结束后对最高位的半字节即 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_counter131)进行异或:
[ 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=stateikeyi
所以我们有代码:

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} b63b62b0,可以看作 16 16 16个半字节数据即 w 15 w 14 ⋯ w 0 w_{15}w_{14}\cdots w_{0} w15w14w0,其中 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=b4i+3b4i+2b4i+1b4i。经过字节代换后输出为 S [ w i ] S[w_i] S[wi]
加密时的 S S S盒如下:

x0123456789101112131415
S[x]0xC0x50x60xB0x90x00xA0xD0x30xE0xF0x80x40x70x10x2

解密时的 I n v _ S Inv\_S Inv_S盒如下:

x0123456789101112131415
S[x]0x50xE0xF0x80xC0x10x20xD0xB0x40x60x30x00x70x90xA
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×imod63630i63i=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]0i63i=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]0i63i=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]));
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐