C++ Bizzare在RSA montgomery乘法的不同MWR2MM算法中得出相同的错误结果 背景

C++ Bizzare在RSA montgomery乘法的不同MWR2MM算法中得出相同的错误结果 背景,c++,algorithm,cryptography,rsa,montgomery-multiplication,C++,Algorithm,Cryptography,Rsa,Montgomery Multiplication,我正在尝试使用各种不同的蒙哥马利方法在硬件(xilinx ZYNQ FPGA)中实现RSA 2048。我使用Xilinx HLS(基本上是C++代码,被合成为硬件)来实现算法。p> < P>注意:为了这个帖子,把它当作标准C++实现,除了我可以有一些变量,这些变量像位向量一样宽4096位,使用 Fo[BIT] < /COD>或Fo.Real[7/0 ] 语法访问各个位。我还没有并行化它,所以它应该像标准C++代码一样运行。请不要害怕,停止阅读,因为我说了FPGA和HLS这个词。只需将此处理为C+

我正在尝试使用各种不同的蒙哥马利方法在硬件(xilinx ZYNQ FPGA)中实现RSA 2048。我使用Xilinx HLS(基本上是C++代码,被合成为硬件)来实现算法。p> < P>注意:为了这个帖子,把它当作标准C++实现,除了我可以有一些变量,这些变量像位向量一样宽4096位,使用 Fo[BIT] < /COD>或Fo.Real[7/0 ] 语法访问各个位。我还没有并行化它,所以它应该像标准C++代码一样运行。请不要害怕,停止阅读,因为我说了FPGA和HLS这个词。只需将此处理为C++代码。< /P> 我已经能够得到一个工作原型,它使用标准的平方和乘法进行模幂运算,使用标准的基数2 MM算法进行模乘运算,但是它在FPGA上占用了太多的空间,我需要使用更少的资源密集型算法

为了节省空间,我尝试实现Tenka koc可伸缩多字基数2蒙哥马利乘法(MWR2MM)。我已经为此挣扎了一个月,但毫无结果。然而,有一个有趣的问题产生于我的斗争,我无法理解

问题 我的问题是,在执行蒙哥马利乘法时,MWR2MM没有返回正确答案。然而,我开始认为这不是一个编码错误,相反,我只是误解了有关算法使用的一些关键问题。

MWR2MM算法有多种变体,实现方式也有很大不同,我已经尝试过实现其中的许多。我目前有4种不同的MWR2MM编码实现,都是基于对一些论文中提出的算法的修改让我认为我的实现实际上是正确的,是所有这些不同版本的算法都返回了相同的错误答案我不认为这是巧合,但我也不认为公布的算法是错误的……因此,我假设实际发生了更邪恶的事情,我的算法实现是正确的

例1 例如,以tenca koc论文中提出的最初提议的MWR2MM为例,我们称之为MWR2MM_CSA,因为该算法的加法运算在硬件实现时都使用进位保存加法器(CSA)

  • S是部分和
  • M是模量
  • Y是被乘数
  • X是乘法器,X_i(下标)是单个位(例如X=(X_n,…,X_1,X_0)
  • 上标是单词向量(例如M=(0,M^{e-1},…,M^1,M^0)
  • (A,B)是两个位向量的串联
  • m是操作数宽度
  • w是所选单词的宽度
  • e是完成向量所需的w位字数(例如e=ceil((m+1)/w))

我对该算法的实现使用以下参数:

  • MWR2MM\u m=2048(操作数大小,从上方开始为m)
  • MWR2MM_w=8(字号,从上往下看)
  • MWR2MM_e=ceil((e+1)/w)=257(每个操作数的字数+1,从上面开始的e)
  • ap\u uint
    是如何在HLS中声明位向量的
我的代码:

void mwr2mm_csa( ap_uint<MWR2MM_m> X,
                 ap_uint<MWR2MM_w> Y[MWR2MM_e+1],
                 ap_uint<MWR2MM_w> M[MWR2MM_e+1],
                 ap_uint<MWR2MM_m> *out)
{
    // Declare and zero partial sum S
    ap_uint<MWR2MM_w> S[MWR2MM_e] = 0;
    for (int i=0; i<MWR2MM_e; i++)
        S[i] = 0;

    // Two Carry bits
    ap_uint<1> Ca=0, Cb=0;

    for (int i=0; i<MWR2MM_m; i++)
    {
        (Ca,S[0]) = X[i]*Y[0] + S[0]; // this is how HLS concatenates vectors, just like in the paper!
        if (S[0][0] == 1) // if the 0th bit of the 0th word is 1
        {
            (Cb,S[0]) = S[0] + M[0];
            for (int j=1; j<=MWR2MM_e; j++)
            {   
                (Ca, S[j]) = Ca + X[i]*Y[j] + S[j];
                (Cb, S[j]) = Cb + M[j] + S[j];
                S[j-1] = ( S[j][0], S[j-1].range(MWR2MM_w-1,1) );
            }
        }
        else
        {
            for (int j=1; j<=MWR2MM_e; j++)
            {
                (Ca, S[j]) = Ca + X[i]*Y[j] + S[j];
                S[j-1] = ( S[j][0], S[j-1].range(MWR2MM_w-1,1) );
            }
        }
    }

    // copy the result to the output pointer
    for (int i=0; i<MWR2MM_e-1; i++)
        out->range(MWR2MM_w*i+(MWR2MM_w-1), MWR2MM_w*i) = S[i].to_uchar();
}
但是,我的算法返回

MWR2MM_csa(X,Y,M) = 0x16C27CBC37C109B048B0F8B860C3501DB2E90F07D9BF9F6A63839453AC6603776C8CBD7AE8974544C52F078AD035AF1AC58CBBD5DB5801CDF3CF876C43F29FC1719ADF46804928D8BB621FCD48988160602C47812299603181FD97AEC74B7BE563EA0B0CB9EC9B2559191D8EE6AE8092FF9E50ADC1B874BC40C9256D785A4920DC1C1A5DF2B8492B181D16841EEA5377524BDF9BCC8A6DC3919DD4FDF6BBD7BB9D8FC35D06D7A4135363A2AA7FA6AE43B335A2704B007E405731A0D5D352EF7C51AD58241D201E07FA86AA395BB8F5AB3C9B966D5DB966777B45FE47B1838B97AFED23907D7AF61CF809D0B934FC3899998BFEF5B11516CA76C62D999CED8840
例2 好的,那么可能这个实现是错误的。让我们尝试另一个修改版本,MWR2MM_CPA算法(以硬件中使用的进位推进加法器命名):

以及我对MWR2M_CSA的实施:

void mwr2mm_cpa(rsaSize_t X, rsaSize_t Yin, rsaSize_t Min, rsaSize_t* out)
{
// extend operands to 2 extra words longer
ap_uint<MWR2MM_m+2*MWR2MM_w> Y = Yin; 
ap_uint<MWR2MM_m+2*MWR2MM_w> M = Min;
ap_uint<MWR2MM_m+2*MWR2MM_w> S = 0;

ap_uint<2> C = 0;
bit_t qi = 0;

// unlike the previous example, we store the concatenations in a temporary variable
ap_uint<10> temp_concat=0; 

for (int i=0; i<MWR2MM_m; i++)
{
    qi = (X[i]*Y[0]) xor S[0];

    // C gets top two bits of temp_concat, j'th word of S gets bottom 8 bits of temp_concat
    temp_concat = X[i]*Y.range(MWR2MM_w-1,0) + qi*M.range(MWR2MM_w-1,0) + S.range(MWR2MM_w-1,0);
    C = temp_concat.range(9,8);
    S.range(MWR2MM_w-1,0) = temp_concat.range(7,0);

    for (int j=1; j<=MWR2MM_e; j++)
    {
        temp_concat = C + X[i]*Y.range(MWR2MM_w*j+(MWR2MM_w-1), MWR2MM_w*j) + qi*M.range(MWR2MM_w*j+(MWR2MM_w-1), MWR2MM_w*j) + S.range(MWR2MM_w*j+(MWR2MM_w-1), MWR2MM_w*j);
        C = temp_concat.range(9,8);
        S.range(MWR2MM_w*j+(MWR2MM_w-1), MWR2MM_w*j) = temp_concat.range(7,0);

        S.range(MWR2MM_w*(j-1)+(MWR2MM_w-1), MWR2MM_w*(j-1)) = (S.bit(MWR2MM_w*j), S.range( MWR2MM_w*(j-1)+(MWR2MM_w-1), MWR2MM_w*(j-1)+1));
    }
    S.range(S.length()-1, S.length()-MWR2MM_w) = 0;
    C=0;
}

*out = S;
为简洁起见,我将不使用另外两种也返回相同错误结果的算法。我应该注意,当与4位操作数大小和2位字大小一起使用时,这两种算法都能正确工作。但是,任何其他操作数大小/字大小组合都是不正确的,但对于所有四个不同的位,它们的结果都是相同的-级别实现

我一辈子都搞不懂为什么这四种算法都返回相同的错误结果。我在第一个示例中的代码与tenca koc论文中提出的算法一字不差

我认为MWR2MM算法应该返回与标准基数-2mm算法相同的结果(在蒙哥马利域中)是错误的吗?它们具有相同的基数,因此无论字长如何,结果都应该是相同的。我是否应该不能相互交换这些算法

很抱歉,这篇文章太长了,但我想非常准确和连贯地解释问题所在。我不是在寻求调试代码的帮助,而是想弄清楚我是否误解了蒙哥马利乘法算法的一个基本功能。我也很好奇为什么要使用不同的实现g相同的错误的结果


谢谢!

问题是您的算法实际上返回:

0x116c27cbc37...
  ^
它大于M。如果你从中减去M,你会得到预期的答案:

这两种算法都返回0到2*M的值,因此如果答案大于或等于M,则需要最后的减法阶段

换句话说,如果你用随机选择的X和Y测试你的算法,你会发现有一半的时间它给出了正确的答案

本文第2节:

因此,只需一次条件减法即可使S[n]达到所需的范围0≤ S[n]
你确定你的e值是正确的吗?如果257个字的宽度是4,那么总共会有257*4=1028位,这就更少了
MWR2MM_cpa(X,Y,M) = 0x16C27CBC37C109B048B0F8B860C3501DB2E90F07D9BF9F6A63839453AC6603776C8CBD7AE8974544C52F078AD035AF1AC58CBBD5DB5801CDF3CF876C43F29FC1719ADF46804928D8BB621FCD48988160602C47812299603181FD97AEC74B7BE563EA0B0CB9EC9B2559191D8EE6AE8092FF9E50ADC1B874BC40C9256D785A4920DC1C1A5DF2B8492B181D16841EEA5377524BDF9BCC8A6DC3919DD4FDF6BBD7BB9D8FC35D06D7A4135363A2AA7FA6AE43B335A2704B007E405731A0D5D352EF7C51AD58241D201E07FA86AA395BB8F5AB3C9B966D5DB966777B45FE47B1838B97AFED23907D7AF61CF809D0B934FC3899998BFEF5B11516CA76C62D999CED8840
0x116c27cbc37...
  ^