RSA硬件实现:radix-2 montgomery乘法问题

时间:2017-01-10 22:48:37

标签: rsa montgomery-multiplication

我正在硬件中实现RSA 1024(xilinx ZYNQ FPGA),我无法弄清楚一些奇怪的问题。最值得注意的是,我发现我的实现只适用于某些基数/指数/模数组合,但没有找到任何理由为什么会这样。

注意:我正在使用Xilinx HLS(主要是合成到硬件中的C代码)实现算法。为了这篇文章,将其视为标准C实现,除了我可以有多达4096位宽的变量。我还没有并行化它,所以它应该像标准C代码一样。

问题

我的问题是我能够得到某些模幂运算测试问题的正确答案,但前提是基数,指数和模数的值可以用比实际1024位少得多的位写入操作数宽度(即它们为零填充)。

当我使用SSH-keygen生成的实际1024位值时,我不再获得正确的结果。

例如,如果我的输入参数是

uint1024_t base     = 1570
uint1024_t exponent = 1019
uint1024_t modulus  = 3337

我正确得到 1570 ^ 1029 mod(3337)= 688

的结果

然而,当我实际使用占据输入的所有(或几乎所有)1024位的值时......

uint1024_t base     = 0x00be5416af9696937b7234421f7256f78dba8001c80a5fdecdb4ed761f2b7f955946ec920399f23ce9627f66286239d3f20e7a46df185946c6c8482e227b9ce172dd518202381706ed0f91b53c5436f233dec27e8cb46c4478f0398d2c254021a7c21596b30f77e9886e2fd2a081cadd3faf83c86bfdd6e9daad12559f8d2747
uint1024_t exponent = 0x6f1e6ab386677cdc86a18f24f42073b328847724fbbd293eee9cdec29ac4dfe953a4256d7e6b9abee426db3b4ddc367a9fcf68ff168a7000d3a7fa8b9d9064ef4f271865045925660fab620fad0aeb58f946e33bdff6968f4c29ac62bd08cf53cb8be2116f2c339465a64fd02517f2bafca72c9f3ca5bbf96b24c1345eb936d1
uint1024_t modulus  = 0xb4d92132b03210f62e52129ae31ef25e03c2dd734a7235efd36bad80c28885f3a9ee1ab626c30072bb3fd9906bf89a259ffd9d5fd75f87a30d75178b9579b257b5dca13ca7546866ad9f2db0072d59335fb128b7295412dd5c43df2c4f2d2f9c1d59d2bb444e6dac1d9cef27190a97aae7030c5c004c5aea3cf99afe89b86d6d

我错误地得到了一个大数字,而不是29(0x1D)的正确答案

我已经检查了两百万次算法,并尝试了不同的初始值和循环边界,但似乎没有任何效果。

我的实施

我使用标准的square和multiply方法进行模幂运算,我选择使用Tenca-Koc radix-2算法进行蒙哥马利乘法,详见下面的伪代码...

/* Tenca-Koc radix2 montgomery multiplication */
Z = 0
for i = 0 to n-1
    Z = Z + X[i]*Y
    if Z is odd then Z = Z + M
    Z = Z/2  // left shift in radix2
if (S >= M) then S = S - M

我的蒙哥马利乘法实现如下:

void montMult(uint1024_t X, uint1024_t Y, uint1024_t M, uint1024_t* outData)
{
    ap_uint<2*NUM_BITS> S = 0; 

    for (int i=0; i<NUM_BITS; i++)
    {
        // add product of X.get_bit(i) and Y to partial sum
        S += X[i]*Y; 

        // if S is even, add modulus to partial sum
        if (S.test(0))  
            S += M;     

        // rightshift 1 bit (divide by 2)
        S = S >> 1;
    }

    // bring back to under 1024 bits by subtracting modulus
    if (S >= M)
        S -= M;

    // write output data
    *outData = S.range(NUM_BITS-1,0); 

}

和我的顶级模幂运算如下,其中(切换表示法!)...

// k: number of bits
// r = 2^k (radix)
// M: base
// e: exponent
// n: modulus
// Mbar: (precomputed residue) M*r mod(n)
// xbar: (precomputed initial residue) 1*r mod(n)

void ModExp(uint1024_t M, uint1024_t e, uint1024_t n, 
            uint1024_t Mbar, uint1024_t xbar, uint1024_t* out)
{
    for (int i=NUM_BITS-1; i>=0; i--)
    {
        // square
        montMult(xbar,xbar,n,&xbar);

        // multiply   
        if (e.test(i)) // if (e.bit(i) == 1)
            montMult(Mbar,xbar,n,&xbar);
    }
        // undo montgomery residue transformation
        montMult(xbar,1,n,out);
}

我不能为我的生活弄清楚为什么这适用于除了实际的1024位值之外的所有内容。任何帮助将不胜感激

2 个答案:

答案 0 :(得分:1)

我已经取代了我的答案,因为我错了。您的原始代码完全正确。我使用我自己的BigInteger库测试了它,其中包括蒙哥马利算术,一切都像魅力一样。这是我的代码:

}

答案 1 :(得分:1)

更新:在我将设计移植到Java以检查调试器中的中间值之后,我终于能够解决问题了。设计在Java中运行完美,没有对代码结构进行任何修改,这让我知道出了什么问题。

使用BigInteger java包获取正确的中间值后,问题就出现了。 HLS任意精度库具有固定的位宽(显然,因为它合成到硬件),而软件BigInteger库是灵活的位宽。事实证明,加法运算符将两个参数视为有符号值,如果它们是不同的位宽,尽管事实上我将它们声明为无符号。因此,当MSB中有一个中间值并且我试图将其添加到更大的值时,它将MSB视为符号位并尝试对其进行签名扩展。

Java BigInt库没有发生这种情况,它迅速指出了我的问题。

如果有人对使用Tenca-Koc radix2算法进行模幂运算的Java实现感兴趣,可以在这里找到代码:https://github.com/bigbrett/MontModExp-radix2