1 背景
在密码学中,最常见的一类基础运算大概就是模算术(Modular Arithmetic)了,特别地,模乘(Modular Multiplication)是其中最复杂的运算。而且在实际的密码算法中各个运算都是基于大数运算,正常的大数模乘运算计算和存储开销尤其大。以基于模16位数N运算为例,对于加法模运算,两个小于N的数a,b相加,要么a+b小于N,这时a+b的值就是最终模运算结果,如果a+b大于或等于N,则a+b-N即为最终模运算结果,而且a+b最多为一个17位的数,存储和计算开销都较小;但是如果是乘法模运算,a*b最大可以产生32位的数,存储空间直接翻倍,这时再对结果取余要么基于除法运算,要么进行多次减法运算,可以想象存储和运算开销都大大增加。蒙哥马利模乘算法正是基于此背景产生,借助该算法可以大大减小模乘运算开销。
2 原理讲解
蒙哥马利算法并不是一个独立得算法,而是三个相互独立又相互联系的算法集合,其中包括:

2.1 蒙哥马利模乘
先给出算法解决的问题描述,即:
已知正整数 N,欲在不使用“除法运算”的条件下,对输入的两个整数a, b,计算 c = a*b mod N。
模乘是为了计算a*b mod N,普通算法中,在计算模N时,利用的是带余除法,除法运算需要太多次乘法,计算复杂度较高,蒙哥马利算法的思想就是利用进制表示简化除法运算,转化成位运算。

为了计算一开始的a*b mod N,需要用到上面的蒙哥马利形式,令X=a'b',我们可以设计一个函数MontgomeryReduction(X, R, N)来计算XR^-1^ mod N,简单计算发现这个函数的计算结果为:

这样再调用一遍函数计算MontgomeryReduction(X~1~, R, N)就得到我们最终需要的结果X~1~*R^-1^ mod N = a*b mod N,称这个函数运算叫蒙哥马利约减算法,所以说,蒙哥马利约减的产生是为了蒙哥马利模乘计算服务的。
2.2 蒙哥马利约减
蒙哥马利算法的核心在于蒙哥马利约简,而且前面提到,蒙哥马利算法的主要思想是把取模运算变得简单,蒙哥马利约减是计算X*R^-1^ mod N,相当于X/R mod N,由之前约定可知R=2^k^,所以X/R = X>>k(X右移k位),但是X不一定被R整除,右移操作会抹掉X中的低位,这个不是精确计算,而是向下取整除法。为了使得右移位操作不损失精度,我们需要找一个m,使得X+m*N是R的倍数,在模N意义下X+m*N ≡ X mod N,所以增加mN在模N运算下不影响最终结果,但是却能使得X+m*N可以直接进行右移运算且不损失精度,接下来重点分析如何求m。
根据R的定义,gcd(R, N) = 1,根据扩展欧几里德算法如下图,有RR' -- NN' = 1并且有0<N'<R,0<=R'<N<R,这里-N'是N在模R下的逆元,即-N'≡N^-1^ mod R,所以N'=-N^-1^ mod R。

由如下推导可知要是X+m*N≡0 mod R,只要取m≡X*N' mod R即可,这样就求出了使X+m*N能被R整除的m。

约减算法总流程
提前工作:已知a,b,N,确定R,并计算出蒙哥马利形式中的a',b'以及X。下面给出蒙哥马利约减详细过程:

下面给出基于蒙哥马利约减的蒙哥马利模乘算法完整流程:

复杂度分析
从蒙哥马利约减算法过程看来,算法1,2,3步骤中模R实际上就是移位运算,这很容易实现,而第1步的-N的模逆可以提前预运算获得,第4步中最多有一个减法运算,蒙哥马利约简的复杂度确实很低。但是看蒙哥马利模乘的流程,在第1步中进行蒙哥马利表示时就计算了两次模乘N运算(a'≡a*R mod N和b'≡b*R mod N),貌似复杂度没有降低,但是实际上,第一步可以看成是蒙哥马利的预先计算,在硬件实现中,先把预先计算的算好,在后面运行就会快很多,尤其是在幂模运算中,如果幂很大(相当于连续进行大量的模乘运算),最开始的两步预运算和后面反复模乘相比可以直接忽略;另外,其实还以先预运算出R^2^ mod N,则求a*R mod N,可以直接调用MontgomeryReduction(a, R^2^, N)获得,同理可求b*R mod N,这样就降低了蒙哥马利模乘的复杂度。
2.3 蒙哥马利幂模
最后介绍蒙哥马利幂模运算,先看一下百度百科上对蒙哥马利幂模运算的介绍(其实该文章就是普通的幂模运算,并不涉及蒙哥马利算法)
针对快速模幂运算这一课题,西方现代数学家提出了大量的解决方案,通常都是先将幂模运算转化为乘模运算。
例如求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](ei*2^i),0<=ei<=1
则:
D=1
FOR i=n TO 0
D=D*D % N
IF ei=1
D=D*C % N
RETURN D
这样,模幂运算就转化成了一系列的模乘运算。
在开始算法前先将C转换为蒙哥马利形式,然后中间模乘进行蒙哥马利模乘,在算法最后在将蒙哥马利形式转回正常形式,即可实现基于蒙哥马利的幂模运算,由文章分析可知,模数越大该算法相比普通方式的幂模运算优势也越大!
3 实例
下面通过一个简单的实例介绍该算法的计算过程,首先a=18,b=29,N=59,那么根据R的选取原则,可以取R=64=2^6^(即k=6),R二进制表示为0b1000000,由于N是质数,所以N和R满足gcd(R, N)=1。所以首先可以得出如下预运算值
a'≡a*R mod N≡31 mod N,b'≡b*R mod N≡27 mod N,N'≡-N^-1^ mod R≡13 mod R,R^2^ mod N≡25 mod N
则MontgomeryMultiply算法有以下计算
1 X=a'*b'=837
2 调用MontgomeryReduction(837, R, N)
2.1 N'运算已算出
2.2 m≡X*N' mod R≡837*13 mod R≡10881 mod 64≡0b10101010000001 mod 0b1000000≡1 mod R,由于R选取的特殊性,可知模R运算很好计算
2.3 y=(X+m*N)/R=(837+1*59)/64=896/64=0b1110000000/0b1000000=14,可见X+m*N能被R整除,所以该步进行简单右移k=6位操作即可
2.4 y < N,无需任何操作
2.5 返回y=14
3 再次调用MontgomeryReduction(14, R, N),该步和步骤2完全相同,不再进行详述,最终返回50
4 50即为18*29 mod 59的最终结果
以下给出上述实例的python版源码
import math
class MontMul:
"""docstring for ClassName"""
def __init__(self, R, N):
self.N = N
self.R = R
self.logR = int(math.log(R, 2))
N_inv = MontMul.modinv(N, R)
self.N_inv_neg = R - N_inv
self.R2 = (R*R)%N
@staticmethod
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = MontMul.egcd(b % a, a)
return (g, x - (b // a) * y, y)
@staticmethod
def modinv(a, m):
g, x, y = MontMul.egcd(a, m)
if g != 1:
raise Exception('modular inverse does not exist')
else:
return x % m
def MontgomeryReduction(self, T):
N, R, logR, N_inv_neg = self.N, self.R, self.logR, self.N_inv_neg
m = ((T&int('1'*logR, 2)) * N_inv_neg)&int('1'*logR, 2) # m = (T%R * N_inv_neg)%R
t = (T+m*N) >> logR # t = int((T+m*N)/R)
if t >= N:
return t-N
else:
return t
def MontgomeryMultiply(self, a, b):
if a >= self.N or b >= self.N:
raise Exception('input integer must be smaller than the modulus N')
R2 = self.R2
aR = self.MontgomeryReduction(a*R2) # convert a to Montgomery form
bR = self.MontgomeryReduction(b*R2) # convert b to Montgomery form
T = aR*bR # standard multiplication
print("aR {} bR {}".format(aR, bR))
abR = self.MontgomeryReduction(T) # Montgomery reduction
print("abR ", abR)
return self.MontgomeryReduction(abR) # covnert abR to normal ab
if __name__ == '__main__':
N = 59
R = 64
g, x, y = MontMul.egcd(N,R)
if R<=N or g !=1:
raise Exception('N must be larger than R and gcd(N,R) == 1')
inst = MontMul(R, N)
a, b = 18, 29
mul = inst.MontgomeryMultiply(a, b)
if mul == (a*b)%N:
print ('({a}*{b})%{N} is {mul}'.format(a = a, b = b, N = N, mul = mul))
参考
本文参考了以下文章,一并表示感谢
https://blog.csdn.net/weixin_46395886/article/details/112988136
https://blog.csdn.net/zgzczzw/article/details/52712980