当前位置:文档之家› Witness算法

Witness算法

《应用密码学》课程论文
论文题目: WITNESS算法
姓名:
学号:
专业班级:
联系电话:
2014年10月
WITNES算法
一、算法原理
在实现RSA密码体质的过程中,为使RSA安全,我们需要找到足够大的素数,使其在运算上相当困难。

如何判断一个奇整数n是否是素数呢?这里就需要用到Miller和Rabin提出的WITNESS算法。

在介绍WITNESS算法之前,先介绍两个相关的定理。

Fermat定理:假如a是整数,p是素数,且a,p互质(即两者只有一个公约数
1),那么a的(p-1)次方除以p的余数恒等于1,即a p-1≡1(mod p)。

二次探测定理:如果x是整数,p是素数,且0<x<p,则方程x2≡1(mod p)。

的解为:x=≡1(mod p)或者x≡-1(mod p)。

这两个定理的证明可以参考应用密码学和数论的相关书籍。

下面介绍WITNESS。

WITNESS算法思想:当n 是一个大奇数时,(n-1)是偶数,可表示为2的某次幂与一个奇数的乘积,即(n-1)=2k* q,(k>0,q为奇数),也就是用2除(n-1),直至所得结果为奇数,共做了k次除法。

选一个整数a,0<a<n-1,计算幂序列模n
的余数a q,a2 *q,…,,若n是素数,则序列的第一个元素为1,或该序列中
某元素为n-1。

其理论基础就是上面提到的Fermat定理和二次探测定理。

具体实现可以参考下面的流程框图和用C语言实现的代码。

二、算法实现
1.流程框图
2.代码
/*WITNESS算法思想:
(n-1)是偶数,可表示为2的某次幂与一个奇数的乘积,(n-1)=(2^k) * q,k>0,q为奇数;即用2除(n-1),直至所得结果为奇数,共做了k次除法;
选一个整数a,0<a<n-1,计算幂序列模n的余数a^q ,a^(2 *q) , …, a^((2^k) *q);若n是素数,则序列的第一个元素为1,或该序列中某元素为n-1;
*/
# include <stdio.h>
# include <stdlib.h>
# include <conio.h>
# include <time.h>
int judge( int k, long long int n, int a, int b[]);
void main(){
long long int n;//待测数据n
int *b = 0;// n - 1 的2进制表示
int length = 0;//n - 1的2进制数的长度
int *a = 0;//储存随机数的一个数组
int length_s;//随机数组中随机数的个数
int flag;//标志位,0可能为素数,1 一定是合数
int count;//计数时用的变量
//输入待测数据以及范围界定
printf("请输入需要测试的数n ( 1 < n < 18446744073709551616,共20位) :");
scanf("%lld", &n);
while(n <= 1 || n >= 18446744073709551615){
printf("输入越界,请重新输入需要测试的数n ( 1 < n < 18446744073709551616 ,共20位) :");
scanf("%lld", &n);
}
//输入随机数组的长度s
printf("输入随机数组成的数组的长度length_s 的最大值(1 < length_s <= 100) :");
scanf("%d", &length_s);
while( length_s <= 1 || length_s > 100){
printf("输入错误,请重新输入随机数组成的数组的长度length_s 的最大值(1 < length_s <= 100) :");
scanf("%d", &length_s);
}
//对输入的数据进行判断
if( n == 2 ){//输入2
printf("输入的数%d为素数,按任意键退出", n);
getch();
exit( 0 );//退出
}
else if( n % 2 == 0 ){ //大于2的偶数
printf("输入的数%d是合数,按任意键退出", n);
getch();
exit( 1 );//退出
}
else{//大于2的奇数
//产生随机数
srand(( unsigned int )(time( NULL )));
a = (int *)malloc( length_s * sizeof( int ));//生成储存随机数的数组
for( count = 0; count < length_s; count ++){//将随机数储存在数组中
a[count] = rand() % ( n - 2 ) + 2;//随机数范围[2,n - 1]
//printf("a[%d] = %d\n", count, a[count]);
}
}
//用b k b k-1…b0来表示(n-1)的二进制,b k b k-1…b0分别表示数组b的最末尾到最开始的数字
int temp = n - 1;
//计算temp表示成2进制后的位数
while( temp != 0 ){
temp = temp / 2;
length ++;
}
b = ( int *)malloc( length * sizeof( int ));//生成储存temp的二进制的数组
count = 0;
temp = n - 1;
while(temp != 0){//将temp的二进制数储存在数组中
b[count] = temp % 2;
// printf("b[%d] = %d\n", count, b[count]);
temp = temp / 2;
count ++;
}
//取不同的随机值a[count]进行测试
for( count = 0; count < length_s; count ++){
flag = judge( length - 1, n, a[count], b);
if( flag == 1){
printf("输入的数%d是合数,按任意键退出", n);
getch();
exit( 3 );
}
}
free( b );
free( a );
printf("通过测试,按任意键退出\n");
getch();
}
int judge( int k, long long int n, int a, int *b){
long long int d = 1;
int i;
long long int x;
for( i = k ; i >= 0; i --){
x = d;
d = ( d * d ) % n;
if( d == 1 && x != 1 && x != n - 1 ){
return 1;//合数
}
if( b[ i ] == 1 ){
d = ( d * a ) % n;
}
}
if( d != 1){
return 1;//合数
}
return 0;//可能为素数
}
3.实验结果
2999951素数
2999953合数
2999957素数
三、算法分析
从上面的分析可以知道,Miller-Rabin算法只是一个概率算法,在满足某些条件的情况下,我们可以判断输入的待测试的数据为合数;在不满足该条件时,待测数据可以通过测试,则可能为素数。

在每次测试都通过的情况下,测试的次数越多,是素数的概率越高。

如果测试s次都通过了,则这时n是素数的概率为(1 - 2-s)。

算法的操作集中于循环中,最坏情况是两重循环都没有中途退出,则s轮Miller-Rabin算法的最坏情况时间复杂度为O(lg n)。

相关主题