假设您的最大整数大小是 64 位。在大多数语言中,字符串对于做数学运算没有多大用处,因此请忽略字符串类型。现在选择该大小一半的整数,即 32 位。这些数组可以解释为以 2 32为底的数字的数字。有了这些,你可以做很长的加法和乘法,就像你习惯用 10 进制和纸笔一样。在每个基本步骤中,您结合两个 32 位量,以产生 32 位结果和可能的一些进位。如果您在 64 位算术中执行基本操作,您将把这两个作为单个 64 位变量的一部分,然后您必须将其拆分为 32 位结果数字(通过位掩码或简单截断强制转换)和剩余的进位(通过位移)。
分工更难。但是如果除数是已知的,那么你可以通过常数进行除法改为使用乘法。考虑一个例子:除以 7。7 的倒数是 1/7=0.142857…。所以你可以乘以得到相同的结果。显然我们不想在这里做任何浮点数学运算。但是您也可以简单地乘以 14286,然后省略结果的最后六位数字。如果您的股息足够小,这将是完全正确的结果。多么小?好吧,您将 x/7 计算为 x*14286/100000,因此错误将为 x*(14286/100000 - 1/7)=x/350000,因此只要 x<350000,您就安全了。只要您的 RSA 设置中的模数是已知的,即只要密钥对保持不变,您就可以使用这种方法进行整数除法,也可以使用它来计算余数。记得使用基数 2 32但是,而不是以 10 为底,并检查反常数需要多少位数。
您可能需要考虑另一种选择,以便更轻松地进行模减少,即使 n 是可变的。除了将余数表示为数字 0 到 n-1,您还可以使用 2 1024 -n 到 2 1024 -1。因此,如果您的初始数字小于 2 1024 -n,则添加 n 以转换为这种新编码。这样做的好处是您可以在不执行任何除法的情况下执行归约步骤。在此设置中, 2 1024等效于 2 1024 -n,因此基本的模减少将从将某个数字拆分为其较低的 1024 位和较高的其余位开始。较高的其余部分将右移 1024 位(这只是数组索引的更改),然后乘以 21024 -n 最后加到下半部分。在确定结果不超过 1024 位之前,您必须这样做。这取决于 n 的频率,因此对于固定 n,您可以预先计算(对于较大的 n,我希望它是加法后的两个缩减步骤,但乘法后的三个步骤,但请仔细检查)而对于变量 n你必须在运行时检查。最后,您可以回到通常的表示:如果结果不小于 n,则减去 n。如果 n>2 512,所有这些都应该按照描述的那样工作。如果不是,即如果您的模数的最高位为零,那么您可能需要进行进一步的调整。还没有考虑到这一点,因为到目前为止我只将这种方法用于固定模量接近 2 的幂。
现在进行幂运算。我非常建议您为此使用二进制方法。在计算 x d时,您从 x, x 2 =x*x, x 4 =x 2 *x 2 , x 8 =…开始,即计算所有二次幂指数。您还维护一些中间结果,将其初始化为一个。在每一步中,如果在指数 d 中设置了相应的位,则将相应的幂乘以该中间结果。所以假设你有 d=11。然后你会计算 1*x 1 *x 2 *x 8因为 d=11=1+2+8=1011 2. 这样,如果您的指数有 512 位,您最多只需要大约 1024 次乘法。其中一半用于二次幂次方,另一半用于结合二次幂。所有这些中的每一次乘法都应立即进行模减少,以保持较低的内存需求。
请注意,以这种简单的形式,上述求幂过程的速度将取决于 d 中实际设置了多少位。因此,这可能会引发侧信道攻击,这可能使攻击者可以访问有关 d 的信息。但是,如果您担心旁道攻击,那么您真的应该让专家开发您的实现,因为我想可能还有更多我没有想到的那些。