信息安全-RSA中的计算问题

第1关:快速指数运算

任务描述

本关任务:编写一个能快速实现指数运算的小程序。

相关知识

为了完成本关任务,你需要掌握:1.模运算,2.快速指数运算。

指数运算的优化

启示:如求x16,直接计算的话需做15次乘法。然而如果重复对每个部分结果做平方运算即求x1x2x4x8x16则只需4次乘法。 求am可如下进行,其中a,m是正整数: 将m表示为二进制形式bk​bk−1​…b0​,即 m=bk​2k+bk−1​2k−1+…+b1​21+b0​20 因此 例如: 23=1×24+0×23+1×22+1×21+1×20 a23=a1×24+0×23+1×22+1×21+1×20 =((((a1)2×a0)2×a1)2×a1)2×a1 =(((a2)2×a)2×a)2×a

快速指数算法

am mod n.将m表示为二进制形式bk​bk−1​…b0​

 
  1. d=1;
  2. For i=k Downto 0
  3. DO {
  4. d=(d×d) mod n;
  5. if bi=1 then
  6. { d=(d×a) mod n ; }
  7. }
  8. return d.

编程要求

根据提示,在右侧编辑器补充代码,输入十进制正整数a,m,n.计算并输出am mod n

测试说明

平台会对你编写的代码进行测试:

测试输入:7 560 561; 预期输出: 1 测试输入:20 3 11; 预期输出: 3


开始你的任务吧,祝你成功!

/*
 * quick.c
 * 
 * Copyright 2020 yxm
 
 */

#include <stdio.h>
//ÔÚÏÂÃæBeginºÍEndÖ®¼ä²¹È«´úÂ룬Çó³ö Ä£nÏÂaµÄm´ÎÃÝ
/*********** Begin ***********/
int quick(int a,int m,int n)
{
    int result=1;
    while(m){
        if(m&1){
            result=(result*a)%n;
        }
        a=a*a%n;
        m=m>>1;
    }
    return result;
}

/*********** End ***********/		
int main(int argc, char **argv)
{
	int a,m,n,d;
	scanf("%d%d%d",&a,&m,&n);
	d=quick(a,m,n);
	printf("%d",d);
	return 0;
}

第2关:扩展的欧几里得算法求乘法逆元

任务描述

欧几里德算法是用来求两个正整数最大公约数的算法。古希腊数学家欧几里德在其著作《TheElements》中最早描述了这种算法,所以被命名为欧几里德算法

本关任务:用扩展欧几里得算法求解乘法逆元。

相关知识

为了完成本关任务,你需要掌握:1.乘法逆元,2.欧几里得算法,3.扩展的欧几里得算法。

乘法逆元

如果gcd(a,b)=1,那么: 存在a−1,使a∗a−1≡1 mod b 存在b−1,使b∗b−1≡1 mod a 这里,把a−1称为a模b的乘法逆元,b−1称为b模a的乘法逆元 下表是模7的乘法运算情况,可以看出1-6均存在乘法逆元,因为7和它们均为互素 下表中w−1列即为模7下w的乘法逆元 下表列出了模为8的乘法运算情况,可以看出,其中1、3、5、7存在模8的乘法逆元,2、4、6、8则不存在模8的乘法逆元。 下表中w−1列即为模8下w的乘法逆元

欧几里得算法

欧几里得(Euclid)算法是数论中的一个基本技术,是求两个正整数的最大公因子的简化过程。而推广的Euclid算法不仅可求两个正整数的最大公因子,而且当两个正整数互素时,还可求出其中一个数关于另一个数的乘法逆元。 算法基于以下定理: 对于任意非负整数a和任意正整数b,有 gcd(a,b)=gcd(b,amod b) 例如: gcd(55,22)=gcd(22,55mod22)=gcd(22,11)=11 得到欧几里得算法的简单描述如下:

 
  1. Euclid(a,b)
  2. if(b=0) then return a;
  3. else return Euclid(b,a mod b);

Euclid算法的具体实现 Euclid算法可以给出两个正整数a和b的最大公因子,Euclid算法首先令r0​为a,令r1​为b(假设a>b),然后执行如下除法运算: 例如:对于gcd(1180,482) 1180=2∗482+216gcd(1180,482)482=2∗216+50gcd(482,216)216=4∗50+16gcd(216,50)50=3∗16+2gcd(50,16)16=8∗2+0gcd(16,2) 所以gcd(1180,482)=2 算法进一步描述如下: 由此得出下面的伪代码:

 
  1. Euclid(a, b) //求最大公因子
  2. X←a; Y←b;
  3. if Y=0 then return X=gcd(a,b);
  4. R=X mod Y;
  5. X=Y;
  6. Y=R;
  7. goto 3。

扩展的Euclid算法先求出gcd(a, b),当gcd(a, b)=1时,则返回b的逆元。

扩展的Euclid算法

考虑到,根据前面欧几里得算法计算最大公因子的过程: 12345=1∗11111+1234(1234=12345−1∗11111)11111=9∗1234+5(5=11111−9∗1234)1234=246∗5+4(4=1234−246∗5)5=1∗4+1(1=5−1∗4)4=4∗1+0 反向得出: 1=5−1∗4=5−1∗(1234−246∗5)=247∗5−1∗1234=247∗(11111−9∗1234)−1∗1234=247∗11111−2224∗1234=247∗11111−2224∗(12345−1∗11111)=2471∗11111−2224∗12345 得到:11111−1mod 12345=2471 12345−1mod 11111=−2224mod 11111=8887 扩展欧几里德算法的基本形式 扩展的欧几里得算法伪代码描述:

 
  1. Extended EUCLID(a,b):
  2. (X1,X2,X3) ←(1,0,b);(Y1,Y2,Y3)←(0,1,a)
  3. 如果Y3=0 返回; 此时 X3=gcd(a,b);无逆元
  4. 如果Y3=1 返回; 此时 Y3=gcd(a,b);Y2即为模b下a的乘法逆元
  5. Q=max_int(X3/Y3)
  6. (T1,T2,T3) ←(X1-Q*Y1,X2-Q*Y2,X3-Q*Y3)
  7. (X1,X2,X3) ←(Y1,Y2,Y3)
  8. (Y1,Y2,Y3) ←(T1,T2,T3)
  9. 回到 3

编程要求

根据提示,补全右侧编辑器中 Begin-End 区间的代码,根据输入的 a 和 b,使用扩展欧几里得定理求解 a 关于 b 的逆。具体要求如下:

  • 从后台获取两个数字 a 和 b,计算并输出 a 关于 b 的最小正整数逆(保证 gcd(a,b)=1)。

测试说明

平台会对你编写的代码进行测试:

测试输入:2 3 预期输出: 2 测试输入:28 75 预期输出: 67


开始你的任务吧,祝你成功!

/* E_eculid.c 
 * Copyright 2020 yxm>
 */

#include <stdio.h>

//ÔÚÏÂÃæBeginºÍEndÖ®¼ä²¹È«´úÂ룬¶ÔÊäÈëµÄÁ½¸öÕûÊýa,bÇóa mod bµÄ³Ë·¨ÄæÔª
/*********** Begin ***********/
int ex_gcd(int a,int b,int *x,int *y){
    if(b==0){
        *x=1;
        *y=0;
        return a;
    }
    else{
        int result=ex_gcd(b,a%b,x,y);
        int k=(*x);
        (*x)=(*y);
        (*y)=k-(a/b*(*y));
        return result;
    }
}
int e_Eculid(int a,int b){
    int x,y;
    x=0,y=0;
    int result=ex_gcd(a,b,&x,&y);
    if(result==1){
        return (x+b)%b;
    }
    else{
        return 0;
    }
}



/*********** End ***********/	

int main(int argc, char **argv)
{
	int a,b;
	int x;
	scanf("%d%d",&a,&b);
	x=e_Eculid(a,b);
	if(x==0) printf("ûÓг˷¨ÄæÔª\n");
	else printf("%d",x);
	return 0;
}

第3关:素性检验

任务描述

本关任务:编写一个能判断输入的十进制整数是否素数的小程序。

相关知识

为了完成本关任务,你需要掌握:如何进行素性检验

素性测试

素性测试主要使用两个定理: 很不幸这个定理并不充分 所以我们再补充一个定理来加强它。 我们现在有了数学公式,但是问题并没有解决,因为p可能非常大,枚举所有的a或x都是不可能的。

于是我们开始猜,我们可以任取一个a并对ap−1测试,而且,我们计算ap−1应该使用O(logp)的算法,即第一关的快速指数运算,这个算法中有平方的步骤,于是我们可以将二次探测放在这一步骤中,以节省时间。

算法返回false时,整数n一定是合数。而当算法返回值为true时,整数n在高概率意义下是素数。可能存在合数n,对于随机选取的基数a,算法返回true。

如何检验一个数是否素数

任取一个a (0<a<n),求an−1mod n。根据费马小定理,若n为素数,结果应该是1。为了更准确,在过程中嵌入二次探测,返回n的素性测试结果。

 
  1. Witness(a,n)
  2. for i=k downto 0 do
  3. {
  4. x←d;
  5. d←(d×d) mod n;
  6. if d=1 and(x≠1)and(x≠n-1)then return False;
  7. if bi=1 then d←(d×a) mod n
  8. }
  9. if d≠1 then return False;
  10. return True.

编程要求

根据提示,在右侧编辑器补充代码,利用费马小定理和二次检测定理判断输入的数是否素数。

测试说明

平台会对你编写的代码进行测试:

测试输入:4; 预期输出: 0

测试输入:5; 预期输出: 1


开始你的任务吧,祝你成功!

/*
 * witness.c
 * 
 * Copyright 2020 yxm
 * 利用费马小定理和二次测试,对输入的正整数n进行素性检测
 */
//在下面Begin和End之间补全代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int ksm(int a, int b, int n){
    int res = 1;
    while(b){
        if(b&1){
            res = res * a;
            res = res % n;
        }
        a = a * a;
        a = a % n;
        b = b >> 1;
    }
    return res;
}
int witness(int a,int n)
{
/*********** Begin ***********/
int flag=ksm(a,n-1,n);
if(flag==1){
    for(int x=2;x<n;x++){
        if(x*x%n==1){
            return 1;
        }
    }
    return 0;
}
return 0;



/*********** End ***********/	
}
	
int main(int argc, char **argv)
{
	int a,n,d;
	scanf("%d",&n);
	srand((unsigned)time(NULL)); 
    //生成(1,n)范围内的随机数a
	/*********** Begin ***********/
    a=rand()%n+1;
    

   /*********** End ***********/	    
	d=witness(a,n);
	printf("%d",d);
	return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值