欧几里得方程 模幂运算 模乘运算 蒙哥马利模乘 素数测试

欧几里得方程
在RSA 算法中,往往要在已知A、N的情况下,求 B,使得 (A*B)%N=1。即相当 于求解B、M都是未知数的二元一次不定方程 A*B-N*M=1 的最小整数解。
而针对不定方程ax-by=c 的最小整数解,古今中外都进行过详尽的研究,西方 有著名的欧几里德算法,即辗转相除法,中国有秦九韶的“大衍求一术”。事实上 二元一次不定方程有整数解的充要条件是c为a、b的最大公约数。即当c=1时,a、b 必须互质。而在RSA算法里由于公钥d为素数,素数与任何正整数互质,所以可以通 过欧几里德方程来求解私钥e。
欧几里德算法是一种递归算法,比较容易理解:
例如:11x-49y=1,求x (a) 11 x - 49 y = 1 49=5 -> (b) 11 x - 5 y = 1 11%5 =1 -> (c) x - 5 y = 1 令y=0 代入(c)得x=1 令x=1 代入(b)得y=2 令y=2 代入(a)得x=9
同理可使用递归算法求得任意 ax-by=1(a、b互质)的解。实际上通过分析归 纳将递归算法转换成非递归算法就变成了大衍求一术:
x=0,y=1 WHILE a!=0 i=y y=x-y*a/b x=i i=a a=b%a b=i IF x<0 x=x+b RETURN x
模幂运算
模幂运算是RSA 的核心算法,最直接地决定了RSA 算法的性能。针对快速模幂 运算这一课题,西方现代数学家提出了大量的解决方案,通常都是先将幂模运算转 化为乘模运算。
例如求D=C**15 % N,由于:a*b % n = (a % n)*(b % n) % n,所以:
C1 =C*C % N =C**2 % N C2 =C1*C % N =C**3 % N C3 =C2*C2 % N =C**6 % N C4 =C3*C % N =C**7 % N C5 =C4*C4 % N =C**14 % N C6 =C5*C % N =C**15 % N
即:对于E=15的幂模运算可分解为6 个乘模运算,归纳分析以上方法可以发现 对于任意E,都可采用以下算法计算D=C**E % N:
D=1 WHILE E>=0 IF E%2=0 C=C*C % N E=E/2 ELSE D=D*C % N E=E-1 RETURN D
继续分析会发现,要知道E 何时能整除 2,并不需要反复进行减一或除二的操 作,只需验证E 的二进制各位是0 还是1 就可以了,从左至右或从右至左验证都可 以,从左至右会更简洁,设E=Sum[i=0 to n](E*2**i),0<=E<=1,则:
D=1 FOR i=n TO 0 D=D*D % N IF E=1 D=D*C % N RETURN D
这样,模幂运算就转化成了一系列的模乘运算。
模乘运算
对于乘模运算 A*B%N,如果A、B都是1024位的大数,先计算A*B,再% N,就会 产生2048位的中间结果,如果不采用动态内存分配技术就必须将大数定义中的数组 空间增加一倍,这样会造成大量的浪费,因为在绝大多数情况下不会用到那额外的 一倍空间,而采用动态内存分配技术会使大数存储失去连续性而使运算过程中的循 环操作变得非常繁琐。所以模乘运算的首要原则就是要避免直接计算A*B。
设A=Sum[i=0 to k](A*r**i),r=0x10000000,0<=A<r,则:
C = A*B = Sum[i=0 to n](A*B*r**i) %N
可以用一个循环来处理:
C=0; FOR i=n to 0 C=C*r C=C+A*B RETURN C
这样将一个多位乘法转换成了一系列单位乘法和加法,由于:
a*b %n = (a%n * b%n) %n a+b %n = (a%n + b%n) %n
所以,对于求C=A*B %N,我们完全可以在求C的过程中完成:
C=0; FOR i=n to 0 C=C*r %N C=C+A*B %N RETURN C
这样产生的最大中间结果是A*B 或C*r,都不超过1056位,空间代价会小得 多,但是时间代价却加大了,因为求模的过程由一次变成了多次。对于孤立的乘模 运算而言这种时间换空间的交易还是值得的,但是对于反复循环的乘模运算,这种 代价就无法承受,必须另寻出路。

蒙哥马利模乘
由于RSA 的核心算法是模幂运算,模幂运算又相当于模乘运算的循环,要提高 RSA 算法的效率,首要问题在于提高模乘运算的效率。不难发现,模乘过程中复杂 度最高的环节是求模运算,因为一次除法实际上包含了多次加法、减法和乘法,如 果在算法中能够尽量减少除法甚至避免除法,则算法的效率会大大提高。
设A=Sum[i=0 to k](A*2**i),0<=A<=1,则:
C= A*B = Sum[i=0 to k](A*B*2**i)
可用循环处理为:
C=0 FOR i FROM k TO 0 C=C*2 C=C+A*B RETURN C
若令 C'= A*B*2**(-k),则:
C'= Sum[i=0 to k](A*B*2**(i-k))
用循环处理即:
C'=0 FOR i FROM 0 TO k C'=C'+A*B C'=C'/2 RETURN C'
通过这一算法求A*B*2**(-k)是不精确的,因为在循环中每次除以2都可能有余 数被舍弃了,但是可以通过这一算法求A*B*2**(-k) %N的精确值,方法是在对C'除 2之前,让C'加上C'[0]*N。由于在RSA中N是两个素数的积,总是奇数,所以当C'是 奇数时,C'[0]=1,C'+C'[0]*N 就是偶数,而当C'为偶数时C'[0]=0,C'+C'[0]*N 还是偶数,这样C'/2 就不会有余数被舍弃。又因为C'+N %N = C' %N,所以在计算 过程中加若干次N,并不会影响结果的正确性。可以将算法整理如下:
C'=0 FOR i FROM 0 TO k C'=C'+A*B C'=C'+C'[0]*N C'=C'/2 IF C'>=N C'=C'-N RETURN C'
由于在RSA中A、B总是小于N,又0<=A,C'[0]<=1,所以:
C' = (C'+A*B+C'[0]*N)/2 C' < (C'+2N)/2 2C' < C'+2N C' < 2N
既然C'总是小于2N,所以求C' %N 就可以很简单地在结束循环后用一次减法来 完成,即在求A*B*2**(-k) %N的过程中不用反复求模,达到了我们避免做除法的目 的。当然,这一算法求得的是A*B*2**(-k) %N,而不是我们最初需要的A*B %N。但 是利用A*B*2**(-k)我们同样可以求得A**E %N。
设R=2**k %N,R'=2**(-k) %N,E=Sum[i=0 to n](E*2**i):
A'=A*R %N X=A' FOR i FROM n TO 0 X=X*X*R' %N IF E=1 X=X*A'*R' %N X=X*1*R' %N RETURN X
最初:
X = A*R %N,
开始循环时:
X = X*X*R' %N = A*R*A*R*R' %N = A**2*R %N
反复循环之后:
X = A**E*R %N
最后:
X = X*1*R' %N = A**E*R*R' %N = A**E %N
如此,我们最终实现了不含除法的模幂算法,这就是著名的蒙哥马利算法,而 X*Y*R' %N 则被称为“蒙哥马利模乘”。以上讨论的是蒙哥马利模乘最简单,最容 易理解的二进制形式。蒙哥马利算法的核心思想在于将求A*B %N转化为不需要反复 取模的A*B*R' %N,但是利用二进制算法求1024位的A*B*R' %N,需要循环1024次之 多,我么必然希望找到更有效的计算A*B*R' %N的算法。
考虑将A表示为任意的r进制:
A = Sum[i=0 to k](A*r**i) 0<=A<=r
我们需要得到的蒙哥马利乘积为:
C'= A*B*R' %N R'=r**(-k)
则以下算法只能得到C'的近似值
C'=0 FOR i FROM 0 TO k C'=C'+A*B C'=C'/r IF C'>=N C'=C'-N RETURN C'
因为在循环中每次C'=C'/r 时,都可能有余数被舍弃。假如我们能够找到一个 系数 q,使得(C' + A*B + q*N) %r =0,并将算法修改为:
C'=0 FOR i FROM 0 TO k C'=C'+A*B+q*N C'=C'/r IF C'>=N C'=C'-N RETURN C'
则C'的最终返回值就是A*B*R' %N的精确值,所以关键在于求q。由于:
(C' + A*B + q*N) %r =0 ==> (C' %r + A*B %r + q*N %r) %r =0 ==> (C'[0] + A*B[0] + q*N[0]) %r =0
若令N[0]*N[0]' %r =1,q=(C'[0]+A*B[0])*(r-N[0]') %r,则:
(C'[0] + A*B[0] + q*N[0]) %r = (C'[0]+A*B[0] - (C'[0]+A*B[0])*N[0]'*N[0]) %r) %r = 0
于是我们可以得出r为任何值的蒙哥马利算法
m=r-N[0]' C'=0 FOR i FROM 0 TO k q=(C'[0]+A*B[0])*m %r C'=(C'+A*B+q*N)/r IF C'>=N C'=C'-N RETURN C'
如果令 r=0x100000000,则 %r 和 /r 运算都会变得非常容易,在1024位的运 算中,循环次数k 不大于32,整个运算过程中最大的中间变量C'=(C'+A*B+q*N) < 2*r*N < 1057位,算法效率就相当高了。唯一的额外负担是需要计算 N[0]',使 N[0]*N[0]' %r =1,而这一问题前面已经用欧几里德算法解决过了,而且在模幂运 算转化成反复模乘运算时,N是固定值,所以N[0]'只需要计算一次,负担并不大。

素数测试方法
数论学家利用费马小定理研究出了多种素数测试方法,目前最快的算法是拉宾 米勒测试算法,其过程如下:
(1)计算奇数M,使得N=(2**r)*M+1 (2)选择随机数A<N (3)对于任意i<r,若A**((2**i)*M) % N = N-1,则N通过随机数A的测试 (4)或者,若A**M % N = 1,则N通过随机数A的测试 (5)让A取不同的值对N进行5次测试,若全部通过则判定N为素数
若N 通过一次测试,则N 不是素数的概率为 25%,若N 通过t 次测试,则N 不 是素数的概率为1/4**t。事实上取t 为5 时,N 不是素数的概率为 1/128,N 为素 数的概率已经大于99.99%。
在实际应用中,可首先用300—500个小素数对N 进行测试,以提高拉宾米勒测 试通过的概率,从而提高整体测试速度。而在生成随机素数时,选取的随机数最好 让r=0,则可省去步骤(3) 的测试,进一步提高测试速度。
素数测试是RSA 选取秘钥的第一步,奇妙的是其核心运算与加解密时所需的运 算完全一致:都是模幂运算。而模幂运算过程中中所需求解的欧几里德方程又恰恰 正是选取密钥第二步所用的运算。可见整个RSA 算法具有一种整体的和谐

原文地址:https://www.cnblogs.com/caozhenhai/p/2432410.html