借助stl实现的简单且相对高性能的c++ rsa加密算法。1024位以内秘钥可以实现1s内生成,2048位5s内生成

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 借助stl实现的简单且相对高性能的c++ rsa加密算法。1024位以内秘钥可以实现1s内生成,2048位5s内生成

RSA-DIY


一个只借助stl实现的简单且相对高性能的c++ rsa加密算法。1024位以内秘钥可以实现1s内生成,2048位5s内生成。


程序构建和使用说明


硬件要求


由于使用了gcc的内联汇编特性,最终的可执行文件中包含x64汇编指令,请确保使用的cpu能够运行x64指令集。


操作系统要求


开发操作系统为ubuntu 18.04,主要依赖gcc,g++和cmake,绝大多数Linux平台都应该可以支持。


构建


程序的构建采用标准的cmake构建流程:


mkdir build
cd build
cmake ../ -DCMAKE_BUILD_TYPE=Release
make -j


执行完成之后目录下的rsa即为可运行文件,该文件包含一个简单的命令行接口。

使用说明

密钥生成

./rsa keygen length [out_pub][out_private][worker][iter]


其中length表示生成的密钥长度,有[]的表示可选参数,其中,out_pub表示生成的公钥保存的文件名称(默认为id_rsa.pub),out_private表示生成的私钥保存的文件名称(默认为id_rsa),worker表示在用Miller-Rabin检测生成素数时使用线程的个数(默认为0表示使用最大并行线程数),iter表示在Miller-Rabin检测中生成素数时检验数的个数,默认为32。


下面的指令:

./rsa keygen 768 rsa_768.pub rsa_768 816


即生成一个768位的密钥,将公钥保存在当前目录下名为rsa_768.pub的文件中,将私钥保存在rsa_768中,检测素数时使用8个线程同时对每个待检验的素数测试16次。

参考的运行结果如下:

max thread supported: 12, work count set to: 8
iteration count set to: 50
key length: 768
n(modulo): 0xdc...
p: 0x83...
q: 0x1a...
d: 0x7a...
generation time: 342ms


运行结果中包含了机器支持的最大运行线程数以及实际使用的线程数(第一行),输出的n, d, e, p, q的含义都和标准RSA算法一致,输出的值均为16进制。

程序同样还测试了运行的时间(time cost行),对于768位及以下的密钥,绝大多数情况下生成时间都远远小于1s


加密

./rsa encrypt pub_key_file msg_file out

后面的三个参数分别表示keygen指令生成的out_pub文件,要加密的文件和输出文件

1. ./rsa keygen 1024
2. wget www.baidu.com -O index.html
3. ./rsa encrypt id_rsa.pub index.html encrypted


上面三条指令,第一条按照默认配置生成了一个长度为1024位的秘钥,第二条获取了www.baidu.com的首页并将结果存储在index.html中。第三条指令读取生成的1024位公钥,将index.html加密之后将结果保存在名为encrypted的文件中


解密

./rsa  decrypt private_key_file msg_file out


后面的三个参数分别表示keygen指令生成的out_private文件,要解密的文件和输出文件

./rsa keygen 2048
wget www.baidu.com   -O  index.html
./rsa encrypt id_rsa.pub index.html encrypted
./rsa decrypt id_rsa encrypted decrypted
diff decrypted index.html

前三句和加密的过程相同。第四句表示将加密后的encrypted文件用私钥id_rsa解密之后输出到decrypted。最后一句用diff比较了两个文件的decrypted和index.html的区别,没有任何输出,说明decrypted和index之间没有区别。


加速方案介绍


项目中涉及到的加速方案包括:基于汇编的大数加减乘法,基于定点逆的快速幂,多线程素性测试

基于汇编的大数加减乘法


项目中所有的大数均采用二进制进行表示。一方面,这样可以方便地得到大数本身的位数信息;另一方面,借助于底层的汇编指令,我们可以一次性完成多位的加减乘法运算同时获取该次运算的进位/溢出。


加法:


实现在BigInteger.cpp__asm_add函数中。核心的代码为

sahf;
movq(%%r8,%%rcx,8),%%rax;
movq(%%r9,%%rcx,8),%%rbx;
adc%%rax,%%rbx;
lahf;
movq%%rbx,(%%r10,%%rcx,8);

在两个大数按位相加的过程中,每一次相加之前先将上一次相加的进位恢复(sahf),然后借助adc指令将该位上的两个数连同上一次的进位(CARRY FLAG)一起加起来,同时保存本次进位,保存结果之后增加索引进入下一轮相加即可


减法

对于x64指令集,减法借位的效果和加法进位的效果一样,都是设置CARRY FLAG。把上面的adc改为sbb即可,实现在BigInteger::sub

乘法

总体上乘法分成两步。


  • 用一个乘数中的某一个整型去乘以另一个大整数得到一个(移位的)结果
  • 将该结果加到最终的结果上

第一步的核心代码如下:

mov%%r8,%%rax;  //小乘数保存在r8中
mov(%%rsi,%%rcx,8),%%rbx; //取出大乘数
mul%%rbx;
mov%%rax,%%rbx;
ASM_RESTOREadc%%r14,%%rbx;  //本轮结果即本次乘法+上次乘法溢出+上次加法溢出
ASM_LOADmov%%rdx,%%r14; //mul指令溢出保存在r14中
mov%%rbx,(%%rdi,%%rcx,8);

这一步结果中的每一个整型都是小乘数乘以大乘数中的一个整型加上上一次相乘的溢出(包括乘法和加法一起)得到的。每次mul指令的乘法溢出被保留到r14寄存器中。ASM_RESTORE和ASM_LOAD是一对宏,其效果和sahf以及lahf相同,只不过是将CARRY FLAG的值保存到r15而非ah中,因为rax要保存mul的乘数。


第二步的基本思路仍然是借助于adc指令。但是每次相加的长度不需要等于最终结果的长度,只要等于第一步长度的最大值加1即可。


实现请参看BigInteger::mul


除法

项目中实现的除法为长除法(BigInteger::div),注意到在确定商的时候由于除数可能存在很多(> 64)位,所以不能使用汇编的整型除法指令。故这里实现的实际上是基于二进制的整形除法,每次确定商的一个比特位。长除法中的乘法全部被移位所替代,因为每次除数需要乘的都是2的幂次,这在一定程度上加快了除法的速度。然而,考虑到在RSA生成密钥以及加密的过程中取模运算是不可避免的,完全借助长除法来实现取模仍然会带来比较大的开销。所以项目中还实现了更高效的快速幂算法。


基于定点逆的快速幂


基本的想法是借助Newton-Raphson算法,用乘法和加减法来代替除法。在计算$x \pmod{n}$时先计算并保留$\frac 1 n$,并考虑将$\lfloor \frac 1n \times x \rfloor$作为x除以n的商。这里将$\frac 1 n$以定点数的形式保存,这样可以调用之前整数的运算来实现小数和小数,小数和整数之间的加法,减法和乘法。


关于定点数的精度,对于计算 $ a^b \pmod{n}$,在使用快速幂算法进行迭代计算的过程中,每一步实际上都是对于某个$x \le n$计算$x^2 \pmod n$,所以实际上被除数不大于$n^2$。如果$n$有$p$位,则$n^2$不超过$2p$位,从而对于这样的$x^2$,逆的精度只要精确到小数点后$2p$位即误差不超过$2^{-2p}$即可保证与被除数相乘与商的误差不超过1。


注意到如果将$\lfloor \frac 1n \times x \rfloor $作为商,即使误差本身不超过1,向下取整之后可能会达到1(向下取整在这里实现为移位运算),从而按上述方法计算的结果可能需要减去n才能得到真正比n小的余数。


实现参看BigInteger::fastExponentNewton,另外一个BigInteger::fastExponent是用长除法完成的快速幂,速度慢不知道到哪去了...


多线程素性测试


在使用Miller-Rabin检测生成随机素数时,项目中开启多个线程并行地生成随机数并进行检测,多个线程之间共享一个标志变量,一旦有一个线程检查到标志变量则修改该变量,其他线程检测到修改之后停止查找并退出。考虑到当位数比较多的时候,不同线程之间找到相同数的概率非常小,故没有设置一个互斥的容器来存储所有线程检查过的数,能够有效地增加并行度。

参看BigInteger.cpp中的getPrimeWorker


收获


  • 复习并进一步学习了汇编语言。本科的时候学习的是基于x86的MSAM,这次作业使用的是x64的汇编,且使用AT&T的语法,对gcc的内联汇编特性也有了一定的了解。
  • 课外自主学习了Miller-Rabin检测以及Newton-Raphson除法,自主推导了逆的定点数精度,通过移位和反转等操作借助已有的整数加减乘法操作实现定点小数和整数的加减乘法
  • 在调试一些非常深的bug时增加了经验
  • 长除法一开始会在结尾莫名其妙的出错,用gdb断点一步一步跟进之后最后发现是逻辑右移64位会导致结果不变而不是全零
  • 大量的向量角标索引操作难免会出现越界访问和修改。很多时候结果都正确但是退出的时候报错。有一次是free函数出了问题,最后是在gdb中获取了free的chunk地址,在chunk的头部打了硬件断点之后监视每次的修改最后定位到具体的越界访问位置。


相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
121 10
|
8天前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
28 4
2023/11/10学习记录-C/C++对称分组加密DES
|
9天前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
24 7
|
27天前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
51 4
|
28天前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
67 5
|
25天前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
43 1
|
28天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
50 2
|
1月前
|
存储 算法 Linux
【c++】STL简介
本文介绍了C++标准模板库(STL)的基本概念、组成部分及学习方法,强调了STL在提高编程效率和代码复用性方面的重要性。文章详细解析了STL的六大组件:容器、算法、迭代器、仿函数、配接器和空间配置器,并提出了学习STL的三个层次,旨在帮助读者深入理解和掌握STL。
51 0
|
13天前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
23 0
|
2月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
66 2