GMP大数库
GMP大数库学习
了解
大数库
在网络安全技术领域中各种加密算法的软件实现始终有一个共同话题是如何在普通的PC机上实现大数运算。普通的PC机内部字长最多时32位或64位,但各种加密算法中为了达到一定安全强度,都要求在128位、512位或1024位字长下进行加减乘除等数学运算,这叫做“大数运算”。
在此前提下,如何在普通的PC机上高效快速的实现大数运算成为加密算法在普通PC机上软件实现的重要问题。如python等语言都内建大数计算机制,但C/C++语言既没有内建大数运算机制,也没有对应的标准库实现。
基于此问题,许多优秀的大数运算库随之而来,下面介绍几种常用常见的大数库:
- GMP
GMP大数库是GUN项目的一部分,诞生于1991年,作为一个任意精度的大整数运算库,它包含了任意精度的整数、浮点数的各种基础运算操作。它是一个C语言库,并提供了C++的包装类,主要应用于密码学应用和研究、互联网安全应用、代数系统、计算代数研究等。
GMP库运行速度非常快,官网上称自己是地球上最快的大数库,但GMP只提供了基础数学运算,并没有提供密码学的相关运算。
- Miracl
miracl库的使用许可针对教育科学研究或非商业目的的应用是免费的,它是C语言库,同时提供了较为简单C++包装类。在功能上它不但提供了高精度的大整数和分数的各种数学运算操作,且提供了许多密码学算法的功能模块,如RSA、AES、DSA等底层操作。尤其还提供了许多椭圆曲线密码学体制中的底层功能模块。
由于miracl库的内部实现采用了很多汇编代码,故运行速度是非常快的。
- Crypto++
Crypto++库是一个C++开源库,提供很多密码算法的实现。
- OpenSSL
OpenSSL是一个C语言库,实现了SSL及相关加密技术,可以实现消息摘要、文件的加解密、数字证书、数字签名和随机数生成等。它的主要特征不是大数据,而是SSL协议的实现和应用。
GMP库
在线文档:https://gmplib.org/manual/
官方手册:https://gmplib.org/gmp-man-6.2.1.pdf
安装
见「参考1」
️:由于GMP是C语言库,但也提供了C++的包装类,在编译时,如果要增加C++支持,./configure时加上--enable-cxx参数,这样才能使用c++库gmpxx.h。
以下面例子为例:
//test.cpp
#include <gmpxx.h>
#include <iostream>
using namespace std;
int main()
{
mpz_t a, b, c;
mpz_init(a);
mpz_init(b);
mpz_init(c);
gmp_scanf("%Zd%Zd", a, b);
mpz_add(c, a, b);
gmp_printf("c= %Zd\n", c);
return 0;
}
编译运行:
g++ test.cpp -o test -lgmp
./test
使用
使用GMP库:
- C
- 一般头文件引入<gmp.h>,该头文件同时适用于C和C++
- 如果在gmp中用到FILE *的函数,需要在<gmp.h>之前引入<stdio.h>
- 如果在gmp中用到了va_list的函数,需要在<gmp.h>之前引入<stdarg.h>
- gmp编译出来的是libgmp库,所以在编译时需要加上-lgmp的标志
- C++
- 一般头文件引入<gmpxx.h>
- 编译出的链接库为libgmpxx,所以在编译时需要加上-lgmpxx的标志,如「2」
基础介绍
数据类型
基本数据类型 | 函数类型 |
---|---|
mpz_t(整数) | mpz_开头 |
mpq_t(有理数) | mpq_开头 |
mpf_t(浮点数) | mpf_开头 |
如何使用呢?以浮点数为例分五步:
- 声明变量:
mpf_t fnum;
- 初始化变量:
mpf_init(fnum); //或mpf_init(fnum,20),此函数指针对类型mpf_t有效
- 变量赋值:
mpf_set_str(fnum,"1.23",10); //以10为进制数,表示浮点数的字符串来赋值fnum
//mpf_set_str的原型
int mpf_init_set_str (mpf_ptr, const char *, int);
其中三个参数表示为多精度浮点数变量、字符串、进制。
- 变量计算:
mpf_mul(fnum,fnum,tmp); //fnum和tmp都是mpf_t类型的变量
- 释放变量:
mpf_clear(fnum);
下面使用GMP:求10000!
//test2.cpp
#include <gmp.h> //先引入gmp头文件
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
mpz_t z_i, z_s, z_o; //定义多精度整数类型
//用1初始化变量
mpz_init_set_str(z_i, "1", 10);
mpz_init_set_str(z_s, "1", 10);
mpz_init_set_str(z_o, "1", 10);
int i;
//z_s=1*2*3*....*10000
for (i = 0; i < 10000; i++)
{
mpz_mul(z_s, z_s, z_i); //z_s=z_s*z_i
mpz_add(z_i, z_i, z_o); //z_i=z_i+z_o
}
gmp_printf("%Zd\n", z_s); //输出和printf类似
//释放空间
mpz_clear(z_i);
mpz_clear(z_s);
mpz_clear(z_o);
getchar();
return 0;
}
️:一个变量只需初始化一次,如果非要多次初始化,在每次初始化操作之间释放变量。初始化后的变量可以进行任意次赋值,但为了效率,应避免过多的变量初始化和释放操作。
下面详细分析数据类型
gmp类型变量的本质是长度为1的结构体数组,变量的值存储在地址 _mp_d 对应的值中
typedef struct
{
int _mp_alloc; /* Number of *limbs* allocated and pointed
to by the _mp_d field. */
int _mp_size; /* abs(_mp_size) is the number of limbs the
last field points to. If _mp_size is
negative this is a negative number. */
mp_limb_t *_mp_d; /* Pointer to the limbs. */
} __mpz_struct;
typedef __mpz_struct mpz_t[1];
其中:
- 一个高精度数中等于字一个机器字长的部分叫做一个“limb”,类型为mp_limb_t,通常一个limb是32位或64位
- 一个高精度数中包含的limb的个数,类型为_mp_size,一般是int
- 一个高精度数的位数表示mp_bitcnt_t,一般为unsigned long
- 随机状态表示gmp_randstate_t
- mp_bitcnt_t:用于位的计数和表示范围
- size_t:用于字节和字符计数
如果自己编写的函数要返回结果,应该仿照GMP的库函数,使用输出参数(即函数的参数有部分参数用于输出值),如果直接使用return语句,返回的仅仅是个指针。
//test1.c
#include <stdio.h>
#include <gmp.h>
void foo(mpz_t result, const mpz_t param, unsigned long n)
{
unsigned long i;
mpz_mul_ui(result, param, n);
for (i = 1; i < n; i++)
mpz_add_ui(result, result, i * 7);
}
int main(void)
{
mpz_t r, n;
mpz_init(r);
mpz_init_set_str(n, "123456", 0);
foo(r, n, 20L);
gmp_printf("%Zd\n", r);
return 0;
}
其中,上面foo函数的输入参数param设置为const。
GMP自己会管理自己申请的内存:
- mpz_t和mpq_t类型的变量会申请更大的内存,但从不会减小内存,为了效率
- mpf_t类型的变量使用固定的内存,内存大小根据初始化时的精度设置确定
函数类型
除了上述「3.1.1」中介绍的整数函数、有理数函数和浮点数函数外,还有:
- 底层函数:在自然数上运算,调用上述三种函数
- 杂项函数:包括自定义内存分配函数和随机数生成函数
另外,GMP的大部分函数一般把输出参数放在输入参数之前,此约定是受赋值运算符的启发,例如:gmz_mul(x,x,x):此调用计算整数x的平方然后把计算结果再存入x中。
整数函数
- void mpz_init(mpz_t x):初始化x并设初值为0
- void mpz_inits(mpz_t x, ...):初始化参数列表中的变量,初值设为0,最后一个参数用NULL,表示结束
- void mpz_clear(mpz_t x), void mpz_clears(mpz_t x, ...):释放变量,使用mpz_clears更方便!把多个变量一次clear,如mpz_clears(a,b,c,...,NULL)
转换函数
- 把GMP整数(mpz_t)转换成标准C语言类型
- mpz_get_ui():将mpz_t类型的数转换为signed long类型
- mpz_get_si():将mpz_t类型的数转换为unsigned long类型
- mpz_get_d():将mpz_t类型的数转换为double类型
- mpz_get_str():将mpz_t类型的数转换为char *str类型
- C类型转换到GMP整数
待补充
示例
RSA加解密
#include <iostream>
#include <gmpxx.h>
#include <cstdlib>
using namespace std;
mpz_class randbits(int bits) // base = 2
{
gmp_randclass a(gmp_randinit_default);
a.seed(rand());
mpz_class l(bits);
return a.get_z_bits(l);
}
inline mpz_class randprime(int bits)
{
mpz_class a = randbits(bits);
mpz_class ret;
mpz_nextprime(ret.get_mpz_t(), a.get_mpz_t());
return ret;
}
void setKey(mpz_class &n, mpz_class &e, mpz_class &d, const int nbits, int ebits = 16)
{
if (nbits / 2 <= ebits)
{
ebits = nbits / 2;
}
mpz_class p = randprime(nbits / 2); //随机取p
mpz_class q = randprime(nbits / 2); //随机取q
n = q * p; //计算n=p*q
mpz_class fn = (p - 1) * (q - 1); //计算欧拉数
mpz_class gcd;
do
{
e = randprime(ebits); //随机取e
mpz_gcd(gcd.get_mpz_t(), e.get_mpz_t(), fn.get_mpz_t()); //判断gcd(e,fn)=1是否成立
} while (gcd != 1);
//mpz_gcdext(g, s, t, a, b): g = as + bt
mpz_gcdext(p.get_mpz_t(), d.get_mpz_t(), q.get_mpz_t(), e.get_mpz_t(), fn.get_mpz_t()); //计算d=e^{-1} mod fn
}
inline mpz_class encrypt(const mpz_class &m, const mpz_class &e, const mpz_class &n)
{
mpz_class ret;
mpz_powm(ret.get_mpz_t(), m.get_mpz_t(), e.get_mpz_t(), n.get_mpz_t()); //ret=m^e mod n
return ret;
}
inline mpz_class decrypt(const mpz_class &c, const mpz_class &d, const mpz_class &n)
{
return encrypt(c, d, n); //m=c^d mod n
}
int main()
{
int nbits;
cout << "输入大数比特数:";
cin >> nbits;
mpz_class n, e, d;
setKey(n, e, d, nbits); //密钥生成
cout << "公钥:(e=" << e.get_str() << ", n=" << n.get_str() << ")" << endl;
cout << "私钥:(d=" << d.get_str() << ", n=" << n.get_str() << ")" << endl;
cout << "输入加密数据:";
string s;
cin >> s;
mpz_class m(s);
mpz_class c;
c = encrypt(m, e, n); //加密
cout << "加密后:" << c.get_str() << endl;
c = decrypt(c, d, n); //解密
cout << "解密后:" << c.get_str() << endl;
if (c == m)
cout << "加/解密成功!" << endl
<< endl;
else
cout << "加/解密失败!" << endl
<< endl;
string q;
cout << "是否继续(Y/N):";
cin >> q;
if (q == "y" || q == "Y")
main();
return 0;
}
其中,mpz_class类型是有符号整数,具体参考:C++ GMP常用函数
待补充
参考
GMP大数库的更多相关文章
- 在64位Linux上安装32位gmp大数库
前期准备: 如果没有安装32位gcc和g++环境的话,可能会导致安装失败,此时请参考上一篇博文 http://www.cnblogs.com/weir007/p/5977759.html,根据系统版本 ...
- C/C++大数库简介
在网络安全技术领域中各种加密解密算法的软件实现上始终有一个共同的问题就是如何在普通的PC机上实现大数的运算.我们日常生活中所应用的PC机内部字长多是32位或64位,但是在各种加密解密的算法中为了达到一 ...
- GMP-C/C++(大数库)使用方法
The GNU Multiple Precision Arithmetic Library(GNU 高精度算数库)是一个用于任意精度算术的免费库,可处理带符号整数,有理数和浮点数.除了运行GMP机器中 ...
- GMP大法教你重新做人(从入门到实战)
一.引言 GMP(The GNU Multiple Precision Arithmetic Library)又叫GNU多精度算术库,是一个提供了很多操作高精度的大整数,浮点数的运算的算术库,几乎没有 ...
- Python第三方库(模块)"scikit learn"以及其他库的安装
scikit-learn是一个用于机器学习的 Python 模块. 其主页:http://scikit-learn.org/stable/. GitHub地址: https://github.com/ ...
- 转: 工作中用的C++库
转:https://www.mhftz.com/archives/42.html 个人学习C/C++的开源代码: 0.STL 1.osmium 2.leveldb 3.glog 4.redis 个人使 ...
- PHP中操作任意精度大小的GMP扩展学习
对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了.其实,这也是一种精度越界之后产生的精度丢失问题.在我们的 PHP 代码中,最大的整数非常大,我们可以通过 PHP_INT ...
- 实现 RSA 算法之改进和优化(第三章)(老物)
第三章 如何改进和优化RSA算法 这章呢,我想谈谈在实际应用出现的问题和理解. 由于近期要开始各种忙了,所以写完这章后我短时间内也不打算出什么资料了=- =(反正平时就没有出资料的习惯.) 在讲第一章 ...
- 使用RSA非对称密钥算法实现硬件设备授权
一.硬件设备授权 即用户在硬件设备输入一个序列号(或一个包含授权信息的文件),然后硬件设备便可正常使用. 二.授权方案 构思授权方案时,参考了下面网址的思路: http://bbs.csdn.n ...
- 1. Linux系统介绍
1. 什么是操作系统? 定义:操作系统是计算机系统中必不可少的基础系统软件,它的作用是负责管理和控制计算机系统中的硬件和软件资源,合理地组织计算机系统的工作流程,以便有效地利用资源为使用者提供一个功能 ...
随机推荐
- 关于C++当中全局变量的释放问题
一.由来 主要是在修改公司的一个MFC项目的时候遇到的问题,我在MFC页面的析构函数当中对一个全局图像变量进行了释放,具体如下: ai_engine_OCR::~ai_engine_OCR() { / ...
- Windows 自动色彩管理(ACM)
在一些笔记本上Win11可以看到设置里有"自动管理应用的颜色"选项,有些笔记上没有.这里讲下"自动管理应用的颜色"的显示规则 看华为MetaBook E设置界面 ...
- PTA题目集4~6的总结性Blog
· 前言 本次的三个作业,由答题判题程序- 4.家居强电电路模拟程序- 1.家居强电电路模拟程序 -2组成. 答题判题程序-4是对前三次判题程序的最后升级,设计多个子类继承于基础题类来实现对每种题型的 ...
- 我和JVM初次约会
前言:该篇是参考结合<高手深度解析:JVM是什么>,自己剔除添加了一些内容整理的而来,这里感谢<高手深度解析:JVM是什么>的文章的指点讲解. JVM的生命周期 "J ...
- Django之添加prometheus监控
1.首先需要在prometheus.yml配置文件中配置targets: - job_name: "test-server-191" static_configs: - targe ...
- Elasticsearch之常见问题
一. 聚合操作时,报Fielddata is disabled on text fields by default GET /megacorp/employee/_search { "agg ...
- ZCMU-1133
emm就直接看的前辈的了. 唉 #include <stdio.h> #include <string.h> #include <algorithm> //我不成熟 ...
- uniapp 消息推送
1.前言 作为一个非原生App的开发者,对于手机系统的推送机制了解是是非有限的,只有了解清楚这些机制,后期的开发才会少踩很多坑,本文将对推送机制逻辑进行一个简单的梳理与记录 2.推送流程 推送流程1. ...
- Winform窗体控件双向绑定数据模拟读写PLC数据
1.用Modbus工具模拟PLC 2.创建一个实体类 点击查看代码 internal class Data : INotifyPropertyChanged { ushort[] ushorts = ...
- 获取n级父目录名称
DirectoryInfo GetPrant(DirectoryInfo path, int level) { DirectoryInfo temp = null; if (level > 1) ...