信息安全基础综合实验总共包含有二大块的内容,分为小组作业(小组作业有四项任务,为Fermat素性检验算法、中国剩余定理、密钥分配以及SM3的实现)和个人作业(SM2加密算法的实现),我也会分别通过五篇博客来分别记录。先进行小组作业,等把小组作业的四次做完之后,就可以对Miracl库的使用基本上掌握了。
俗话说的好,要做实验,肯定要先搭建环境,
环境两小时,实验五分钟。而实验所需要的环境就是把miracl库添加进来即可。首先编译miracl包,参加我同学W的这个博客:https://blog.csdn.net/Baron_wu/article/details/83151473,里面讲述了如何生成所需要的miracl.lib,如果搞不定的话,那就来问我索取吧(邮箱联系[email protected]);得到了这个包之后,那环境就算是完成了90%了,下面是在Visual Studio中调用此库即可,过程可以继续参考我这个同学W的另外一篇博客:https://blog.csdn.net/Baron_wu/article/details/83177251。通过这两个步骤后,就可以很开心的去编程吧!顺便感谢一下这个同学W,为什么他的出镜率这么高呢,因为我们两个是一组的,当时为了能够成功地调用Miracl库也是费了一番心思。那下面就切入正题,看看如何通过Miracl库来实现Fermat素性检验算法。因为这是第一篇的博客嘛,除了对Fermat素性检验的介绍之外,对于一些Miracl库中的函数也会有一个很简单的介绍。
一、实验目的
在前面的四次小实验中,对我们的考察难度不是很大,四个小实验对我们提出来的要求是,通过完成验证四个定理的过程,让我们能够相比较才学习信息安全数学基础与现代密码学时,能更加详细的了解关于这四个定理的内容。第一次的实验是使用Fermat素性检验算法(这是一个概率性算法),来判断从文本文件中输入进去的大整数是不是一个素数。在平时我们接触到的C语言结构中,最大的表示数值是unsigned int型数据,其最大可以表示数据
,也就是八个字节的大小,即使是这样,对于我们信息安全实验来说,这样的数据类型长度是远远不够的。在实验中,我们需要用到miracl函数库,它定义了两种新的数据类型——表示大整数的big类型和表示有理数的flash(short for floating-slash)类型。通过本次实验,可以让我们熟悉miracl库中的一些基本操作函数,将Fermat素性检验算法在实验中展示出来。
二、方案设计
2.1 Fermat素性检验
也就是说,要判断一个奇整数是不是素数(如果这个数是偶数那肯定就不是素数了),我们可以通过随机选取同m互为质数的一个整数a(如果m和a不互质的话,那么m也肯定不是素数),判断m和a是否满足费马小定理,如果满足的话,m就会以不低于
的概率是一个素数。对于这个定理的证明,我也去网上查找了相关的资料,但是依旧对这个定理的证明不是特别的了解,等到以后能力提升再来读懂此定理的证明,也不在报告里附上此定理的证明了。
下面是Fermat素性检验算法的步骤,这儿我用一张图来阐明Fermat素性检验算法的原理:
2.2 关于Fermat素性检验算法单身的背景以及Carmichael数
人们自然会想,如果考虑了所有小于n的底数a,出错的概率是否就可以降到0呢?没想到的是,居然就有这样的合数,它可以通过所有a的测试。第一个发现这样极端的伪素数,他把它们称作Carmichael数。你一定会以为这样的数一定很大。错。第一个Carmichael数小得惊人,仅仅是一个三位数,561。前10亿个自然数中Carmichael数也有600个之多。Carmichael数的存在说明,我们还需要继续加强素性判断的算法。
比如说,1105就是一个Carmichael数,对于用Fermat素性检验算法来判断1105(十六进制下表示是451)是不是质数,得出的结果是1105能够以98.4375的概率成为一个素数。所以这点很直观的体现出了Fermat素性检验是一个概率性算法。
三、方案实现
3.1 算法流程图3.2 主要函数的介绍
引入了miracl库后,其中定义了两种新的数据类型——表示大整数的big类型和表示有理数的flash(short for floating-slash)类型,因此随之也引入了能够操作big类型和flash类型的操作函数。整体上来说,完成实验所需要的语言是C语言,但是在其中一些关键地方的操作上,我们需要使用库中自己定义的函数,将这些函数的功能了解后,基本上写出Fermat素性检验算法就没有什么问题了。在miracl_5.5.4中,附带着一个manual.doc文件供我们参考,里面有所有函数的定义以及如何使用等,这个文件对于我们了解如何使用miracl库有非常重要的作用。
3.2.1 mirsys()
函数原型: miracl *mirsys(int nd, int nb);
功能说明: 初始化当前程序线程的MIRACL系统,如下所述,必须在尝试使用任何其他MIRACL例程之前调用:
(1)初始化错误跟踪机制。
(2)从nd和nb计算用于每个大/闪存号的计算机字数。
(3)初始化了16个大的工作变量(其中4个为双倍长度)。
(4)某些实例变量被赋予默认初始值。
(5)通过调用irand(0L)启动随机数发生器。
这个函数的返回值是是一个miracl实例指针,通过它可以访问所有实例变量,如果没有足够的内存来创建实例,则为NULL。
操作实例是miracl *mip=mirsys(500,10),意思是我定义的这些变量最大长度都是500位(这个位是后面进制的位数),输入、输出、运算用的进制都是10进制。
3.2.2 实例变量IOBASE
IOBASE是用于控制输入和输出的进制问题的,可以在程序中随意更改, 必须大于或等于2且小于或等于256。使用实例是像这样的:mip->IOBASE=16,这样子输入的变量和输出的变量所使用的进制都是十六进制。
3.2.3 mirvar()
函数原型:flash mirvar(iv)
功能说明:通过为其保留适当数量的内存位置来初始化大/闪存变量,可以通过对函数mirkill()的后续调用来释放该存储器。并且在程序中,每个big型变量都必须赋初始值,否则会出错。
例如上面的这段代码是用来演示没有初始化big型变量带来的错误,如果没有初始化变量,在compile和build阶段系统都会告诉我们没有警告,没有错误,但是一旦执行可执行文件后,系统就会崩溃。出错信息如下:
3.2.4 decr()
函数原型:void decr(x,n,z)
big x,z;
int n;
这个函数实现的功能是对一个big型的变量x减去一个int型的值后得出的big型数据,例如我们在Fermat素性检验算法中,产生的随机数范围在2至m-2中,如果我们需要得出m-2的话,有两种途径:第一,如果将减去的2视作为int类型的话,直接相减是不可行的,相当于是big型减去int型,需要用到这个函数,即decr(m,2,y),返回的结果是big y=m-2;第二,如果将2作为大数来看待的话,就需要先用negify()函数取2的复数,再使用add()函数得到m-2的结果。
3.2.5 bigrand()&bigdig()
函数原型: void bigrand(big w, big x);
功能说明: 使用内置的随机数发生器,产生一个小于w的大数随机数,x
函数原型: void bigdig(int n, int b, big x);
功能说明: 产生一个指定长度的进制的随机数,该函数使用内置的随机数发生器,初始化种子调用irand函数。
这两个函数都是可以生成随机数的,但是它们的功能确实略有差异的。bigrand()是产生一个小于w的大数随机数,x
上面的截图是我测试的bigdig()这个函数,为了验证这个函数是产生一个指定长度的进制的随机数,我在这里只让它生成二位十进制数,并且循环随机生成一千次(这种情况下,从概率角度来说,0-100这些数每个数出现的次数都能平均在10次左右),从输出的结果中看,只会产生10-99这个区间内的数,不会产生01、05这样的一位十进制数,证明了bigdig()只会产生指定长度的进制的随机数。
下面的截图是我测试的bigrand()这个函数的功能,和上面测试的原理是一样的,在这次的输出结果中,可以看到不仅仅有二位十进制数字,还有一位十进制数字,证明了bigrand()是产生一个小于给定数值的大数随机数。
在Fermat素性检验算法中,每轮的算法使用到的底数a是在2至m-2这些数中随机选取一个数作为底数,然后进行下面的算法,因此,在每轮中随机选取出的随机数必须是表现出完全的随机性。因此对于随机函数的选择是十分有必要的,在这里我选择的是bigrand()。
3.2.6 compare()
函数原型: int compare(big x, big y);
功能说明: 比较两个大数的大小。
返回值: x>y时返回+1, x=y时返回0, x
3.2.7 egcd()
函数原型: int egcd(bigx, big y, big z);
功能说明:计算两个大数的最大公约数,z=gcd(x,y)。
3.2.8 powmod()
函数原型: void powmod(big x, big y,big z, big w);
功能说明: 模幂运算。
3.2.9 mirkill()
函数原型:void mirkill(x) big x;
功能说明:通过将其归零并释放其内存来安全地杀死大/闪存数字。
3.2.10 mirexit()
函数原型: void mirexit();
功能说明: 清除MIRACL系统,释放所有内部变量。
3.3 算法实现的主要代码
#include "miracl.h" #include
#include #include #include #define round 6 int Fermatjdu_prime(big obj); main() { FILE *fp; big obj; miracl *mip = mirsys(1500, 16);//定义的这些变量最大长度都是5000位(这个位是后面进制的位数),输入、输出、运算用的进制都是16进制。 mip->IOBASE = 16; obj=mirvar(0); //初始化变量obj,obj是输入的需要判断是否为素数的大数 if((fp=fopen("data.txt","r+"))==NULL){ printf("Open the file failure...\n"); exit(0); } //判断文件是否能够正确打开 while(!feof(fp)){ //检测文件结束符 cinnum(obj, fp); //从文件中读取一个数字进入,并将其强制转化为十六进制表的大数obj cotnum(obj, stdout); //向屏幕上输出一个大数obj if (Fermatjdu_prime(obj)) printf("This number has a %6.4f%% probability of being a prime number.\n", 100 * (1 - pow(0.5, round))); else printf("This number is 100%% definitely a Composite number! \n"); } fclose(fp); mirkill(obj); //释放大数obj所占用的空间 mirexit(); //清楚miracl系统 } int Fermatjdu_prime(big obj) { big radn,trans,mgcd,trans1,r,num1,num2,cons; int i,j; miracl *mip = mirsys(1500, 16); mip->IOBASE=16; radn=mirvar(0);//对函数中使用到的big型变量进行初始化 trans=mirvar(0); mgcd=mirvar(0); trans1=mirvar(0); r=mirvar(0); num1=mirvar(1); num2=mirvar(2); cons=mirvar(0); i=0; j=0; decr(obj,2,trans); //trans=obj-2 decr(obj,1,trans1); //trans=obj-1 srand((unsigned int)time(NULL)); for(i=0;i
四、数据分析
在这部分的数据分析时,还未给老师验收,这部分测试的数据是我自己生成的数。这些数据是我借鉴的一个同学用python脚本写出来的,生成的时候是按一个合数一个素数进行生成的,用写成的算法跑了一下之后,和预期是一样的,用Fermat素性检验算法能够把里面所有的合数都检测出来,里面所有的素数都能够以概率98.4375%确定(轮数是六轮,当然这些数都是自己生成的素数,所以可以判断写出的程序没有问题)。
此外,在查阅资料的过程中,有这样的一种数的存在,即Carmichael数,我用自己的程序也跑了一下,确实这种数对于Fermat素性检验算法来说无法识别。
五、总结
在本次上机实验中,主要需要我们解决的问题有两个方面:一方面是实验环境的搭建,另外一方面是对miracl库中的函数需要一定的熟悉度。我们组在环境的搭建上采用了两条路线,一条是Win10系统+编译器是Visual Studio,另外一条的环境就是和实验室一样的Win7系统+编译器是Visual C++ 6.0。最初拿到实验任务,首先想到的肯定是网上搜索一翻资料看看如何配置这样的环境,在网上看到的一个比较靠谱的教程是叫我们将miracl.h、mirdef.h以及ms32.lib添加到工程下面,miracl.h、mirdef.h这两个文件直接可以在/include文件夹下找到,但是找不到ms32.lib这个链接库,所以我们就去CSDN上下载了一个别人编译过的链接库,但是装入到工程下面后,经过一些简单的调试却出错:告诉我们连接不到这个库,所以从网上下载的ms32.lib是不完善的库。所以我们决定自己编译一下静态链接库。