PRESENT加密算法(c++实现)

2023-11-04

简介

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盒如下:

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×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]));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PRESENT加密算法(c++实现) 的相关文章

  • Uni-app登录态管理(vuex)

    转载 https www cnblogs com edward life p 11181139 html 应用中 保持登录状态是常见需求 本文讲解使用uni app框架时如何保持用户登录状态 即 初次进入应用为未登录状态 gt 登录 gt
  • html提交表单 node,Nodejs之http的表单提交

    之前介绍了http模块的请求与响应的过程 也介绍了TCP协议的客户端与服务端的数据传输 http协议是TCP上层协议 这里创建了一个简单的web服务器 并对提交表单数据进行处理 根据了不起的Node js一书总结 POST方法提交表单数据
  • centso7 openssl 报错Verify return code: 20 (unable to get local issuer certificate)

    问题重现 由于centos7 默认的openssl的版本为1 1 0k 本人编译媒体服务时 需要openssl版本1 1 1以上 所有删除的之前的低版本openssl 手动编译了一个1 1 1k的版本 媒体服务正常运行 并且CA验证正常 结

随机推荐

  • hadoop完全分布式一键安装、启动、停止脚本

    hadoop完全分布式一键安装脚本 bin bash 配置HADOOP的安装目录 修改的地方1 脚本可以自己创建 在windows编写的代码可能运行有问题执行以下 1 gt vim redisshell sh 2 gt set ff uni
  • 1.使用SQL语句创建表

    1 创建表的语法 create table 表名 列1 数据类型 1 列2 数据类型 tablespace 表空间 SQL create table student ID NUMBER not null NAME VARCHAR2 20 表
  • 综合能力 ---- 1. 通信职业道德

    1 职业道德内涵 职业义务 职业良心 职业荣誉 职业信誉 职业尊严 职业纪律 2 记忆职业和职业道德概念 职业 人们在社会中所从事的专门业务和对社会所承担的特定职责 并以此作为重要生活来源的社会活动 职业道德 人们从事正当的社会职业 并在其
  • chrome.runtime.sendMessage 回调函数参数为undefined

    chrome runtime sendMessage 回调函数参数为undefined chrome runtime sendMessage的回调函数默认是同步的 而且超时后直接执行 返回undefined 如果要异步执行 必须在处理函数中
  • Vim,人类史上最好用的文本编辑器!从此以后你就是一个善良的极客!

    CSDN 的小伙伴们 大家好 我是沉默王二 写完 Shell 那篇后就想写 Vim 了 因为人类史上最好的文本编辑器就是 Vim 不赞同的请自觉持有保留意见 哈哈哈 Better Stronger Faster 用这三个单词来赞美 Vim
  • iOS(三)实现App底部TabBar的切换:二

    上一篇讲述了iOS自带的TabBar 但在我所见到的很多App源码中大多用了自己写的TabBar 惯例先上图 这只是一个最简单的TabBar 但重在原理 虽然是我懒 HomeViewController h HomeViewControll
  • day17-json和面向对象(总结)

    day17 json和面向对象 姚万里 1 json数据 1 json数据格式的作用 json和xml是两种通用的数据格式 几乎所有的高级编程语言都支持 json和xml数据的格式的存在 是为了让不同编程语言的程序可以进行有效的数据沟通 2
  • VSCode: PlatformIO主页一直显示loading解决方案

    VSCode PlatformIO主页一直显示loading解决方案 Github问题描述 Could not start PIO Home server Error timeout 205 在vscode中打开platformio点击进入
  • 海豚php上传音频方法(引用 layui的 js 与 css)

    1 html代码 div class layui upload div div div
  • 1033. 旧键盘打字(20)--Python

    之前的时候最后一个测试点一直没有通过 后来在网上搜寻了一下答案 发现自己写的逻辑实在是太混乱了 所以看了一下别人的思路 主要是 1 首先判断坏键盘中是否有 若是有的话 使用flag标记一下 2 然后可以循环的判断应该输出的字符串 边遍历边输
  • 遗传算法及Python代码实现、图解

    目录 前言 一 遗传算法 Genetic Algorithm GA 简介 二 遗传算法基本概念 二 1 目标函数 环境 二 2 一组解 最优解 种群 最适宜种群 二 3 解 编码 个体 基因型 二 4 解码 表现型 难点 二 5 交叉 变异
  • 【jdk1.8特性】之Function

    笔者日常 来吧 Function 相关声明 本文按照以下顺序进行说明并给出简单的使用示例 序号 接口 1 Function
  • 【Redis笔记】发布与订阅

    Redis发布与订阅功能由PUBLISH SUBSCRIBE PSUBSCRIBE等命令组成 SUBSCRIBE命令 客户端可以订阅一个或多个频道 从而成为这些频道的订阅者 subscriber 每当有其他客户端向被订阅的频道发送消息时 频
  • 未来十年互联网十大发展趋势

    世界已被互联网占领 互联网未来10年将如何变化与发展 1 互联网全球普及 根据国际电信联盟最近统计 全球互联网用户总数已经达到20亿人 而联合国公布的最新统计数字显示 世界人口在2011年底突破70亿大关 所以到2020年毫无疑问会有更多的
  • Ubuntu下各种压缩与解压的操作

    这篇文章主要给大家介绍了在Ubuntu系统下各种压缩与解压的操作 其中包括 tar gz bz2 bz Z tgz zip rar以及 lha等各个的解压与压缩方法 每一种都给出了示例代码 需要的朋友可以参考学习 一起来看看吧 一 tar
  • vue项目中高德地图根据城市名定位到城市中心位置,并在该位置做标记

    先看效果 首先记得引入高德地图 并且要带上 plugin AMap DistrictSearch 如下
  • 加密方式

    目录 MD5和RSA 1 MD5 a MD5简介 MD5一种不可逆的加密算法 什么意思呢 网站一般会保存用户密码 为了不让数据库管理员看到用户的密码 你输入的密码是这样的 12345 网站加密后的密码可能是这样的 E10ADC3949BA5
  • 缓存穿透,缓存雪崩的四种解决方案

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 前言 设计一个缓存系统 不得不要考虑的问题就是 缓存穿透 缓存击穿与失效时的雪崩效应 缓存穿透 缓存穿透是指查询一个一定不存在的数据 由于缓存是不命中时被动写的 并且出于容
  • [管理与领导-60]:IT基层管理者 - 扩展技能 - 3 - 通过面试招到合适的人选

    目录 前言 一 招聘 1 1 什么是招聘 1 2 招聘 VS 招募 1 3 甄选 1 4 招聘中的重要原则 1 5 招聘的本质 1 6 人才匹配的维度 1 7 人员招聘中的误区 二 面试 2 1 何为面试 2 2 为什么面试 2 3 面试的
  • PRESENT加密算法(c++实现)

    简介 PRESENT加密算法在2007 年由来自德国波鸿鲁尔大学的 Bogdanov 在 CHES 会议中发表 PRESENT加密算法为一种轻量级分组密码算法 采用了 置换网络 SPN 结构 一共迭代 31 轮 分块 组 长度为 64 比特