MbedTLS中的Montgomery算法实现解析(三)

MbedTLS中的Montgomery算法实现解析(三)

在前面的两篇博客中,我们详细讨论了Montgomery算法的数学原理以及MbedTLS中是如何快速求得-N-1(mod n)。在这一篇博客中,我们将探讨MbedTLS中的蒙哥马利约减(Montgomery reduction)和蒙哥马利模乘(Montgomery multiplication)是如何实现的。

在阅读本博客之前,建议先掌握MbedTLS的大数表示方式

蒙哥马利约减Montgomery reduction

在我们的数学推导中,蒙哥马利约减是蒙哥马利模乘的基础,也就是说我们首先有:

X ≡ a × R-1 (mod n)

然后才有:

X = a × R × b × R × R-1 (mod n)

但是MbedTLS将这两者结合了起来,在MbedTLS中定义的蒙哥马利模乘式为:

X = A × B × R-1 (mod n)

既包含了模乘(Montgomery multiplication),又包含了约减(Montgomery reduction),这样的好处就是约减可以写成模乘的一种特殊情况,即当B=1时:

X = A × B × R-1 (mod n) = A × R-1 (mod n)

因此在MbenTLS中,约减就是模乘的一种特殊情况。

下面我们来看代码。

// Montgomery reduction: A = A * R^-1 mod N
static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint mm, const mbedtls_mpi *T )
{
    mbedtls_mpi_uint z = 1;
    mbedtls_mpi U;

    U.n = U.s = (int) z;
    U.p = &z;

    return( mpi_montmul( A, &U, N, mm, T ) );
}

可以看到这里初始化了一个值为1的大数变量U,随后调用了蒙哥马利模乘函数mpi_montmul

蒙哥马利模乘Montgomery multiplication

下面我们来看看这个蒙哥马利模乘函数mpi_montmul

// Montgomery multiplication: A = A * B * R^-1 mod N
static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm,const mbedtls_mpi *T )
{
    size_t i, n, m;
    mbedtls_mpi_uint u0, u1, *d;

    if( T->n < N->n + 1 || T->p == NULL )
        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );

    memset( T->p, 0, T->n * ciL );

    d = T->p;
    n = N->n;
    m = ( B->n < n ) ? B->n : n;

    for( i = 0; i < n; i++ )
    {
        /*
         * T = (T + u0*B + u1*N) / 2^biL
         */
        u0 = A->p[i];
        u1 = ( d[0] + u0 * B->p[0] ) * mm;

        mpi_mul_hlp( m, B->p, d, u0 );
        mpi_mul_hlp( n, N->p, d, u1 );

        *d++ = u0; d[n + 1] = 0;
    }

    memcpy( A->p, d, ( n + 1 ) * ciL );

    if( mbedtls_mpi_cmp_abs( A, N ) >= 0 )
        mpi_sub_hlp( n, N->p, A->p );
    else
        /* prevent timing attacks */
        mpi_sub_hlp( n, A->p, T->p );

    return( 0 );
}

函数有五个形参:
mbedtls_mpi*A :第一个乘数
const mbedtls_mpi*B:第二个乘数
const mbedtls_mpi*N:模除的大数N
mbedtls_mpi_uint mm:预计算结果-N-1(mod n)
const mbedtls_mpi*T:用来保存中间计算结果的T

函数没有提到R的取值,在后面的实现中函数采用232×n作为R。而在每一轮的运算中,我们针对的是232,之所以这样实现是因为MbedTLS中的大整数采用的字长是32位,这样在涉及到模除运算的时候,我们只需要关注大整数的最后一个字即可。

我们一步一步来看代码,首先是:

 if( T->n < N->n + 1 || T->p == NULL )
        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );

首先T是用来保存两个大数相乘的中间变量,Tlimbs数必须足够大,当不够大或者为空的时候要返回错误。

// 将T的内容全部置为0
memset( T->p, 0, T->n * ciL );

d = T->p; //d指向T->p
n = N->n; //n记录N->n
m = ( B->n < n ) ? B->n : n; //m记录N->n和B->n中较小的一个

接下来是Montgomery multiplication的核心,一个循环:

for( i = 0; i < n; i++ )
    {
        /*
         * T = (T + u0*B + u1*N) / 2^biL
         */
        u0 = A->p[i];
        u1 = ( d[0] + u0 * B->p[0] ) * mm;

        mpi_mul_hlp( m, B->p, d, u0 );
        mpi_mul_hlp( n, N->p, d, u1 );

        *d++ = u0; d[n + 1] = 0;
    }

首先,有一点我们需要明确的是,函数接收的A和B其实是:

A = a × R (mod n)
B = b × R (mod n)

这也就是说明:

0 < A , B < n - 1

这意味着A和B只是低n×Bil上的内容是有意义的,在高于n×Bil位上的内容全部是0.

在这里插入图片描述
因此我们在处理的时候只需要考虑A和B的低n×Bil上的内容即可。

在这个循环中,u0=A->p[i],经过循环后u0会遍历A->p中的内容。

u1计算的内容是我们在博客一中提到的:s ≡ - a0 × n-1 (mod R)

d[0] + u0 × B->p[0] = A->p[i] × B->p[0] + T->p[0]

相当于每一轮计算的低32位再加上之前计算结果中的低32位。为什么要只计算低32位呢?这就是蒙哥马利算法很巧妙的地方,因为R的取值是232,这说明一个数在模232之后只用看他的低32位内容即可,也就是说我们只用关注第一个字即可(MbedTLS中低位放在前面的字中)。

再乘上mm,我们在博客二中提到了mm ≡ -n-1 (mod R)

于是我们现在就得到了u1 ≡ - T0 × n-1 (mod R)

读者可能会注意到,为什么u1是两个字相乘的结果,应该占2个字的存储空间,为什么我们只用了一个字来存放?这是因为高位字的内容模R等于0,我们只需要关注低位字就行了。

mpi_mul_hlp( m, B->p, d, u0 );
mpi_mul_hlp( n, N->p, d, u1 );

这里计算了:

d + = u0 × B + u1 × N

在这里我们讨论一下d现在有多少个字长?
首先u0u1都是一个字长,而B的长度小于等于n个字,N的长度等于n个字。那么d的长度应该不超过n+1个字。

图解如下:

在这里插入图片描述

到这里我们就只差最后一步了,这个时候的d[0]的内容已经全是0(原因可以见博客一,这里就不解释了),下面我们需要进行右移操作,将整个d向右移动一个字,在这里MbedTLS采用了一种非常聪明的办法,并不是右移数字,而是左移指针

*d++ = u0;

在这里插入图片描述
最后d[n+1]=0,是为了防止内存中出现其它的数据干扰计算结果。
因为我们通过图解可以看到d的数据不可能超过d[n],d[n+1]用来保存的是
d[n]的内容,在下一个循环开始之前,我们将这个内容清空,方便保存d[n]。

每左移一次指针,就相当于除以232,一共左移了n次指针,一共就相当于除以了232×n,就相当于除以R

在这个循环结束后,我们就得到了一个长度为n+1个字的d,此时的
d=d[n]

此时的d[0]d[n-1]也记录了A->pn个字的内容。
在这里插入图片描述

在这个循环结束之后,我们就已经计算到了:
X = A × B × R-1 (mod n)

下面我们将这(n+1)个字的值copy给A:

 memcpy( A->p, d, ( n + 1 ) * ciL );

就相当于完成了:

A = A × B × R-1 (mod n)

最后我们需要检查一下A是否大于N,如果大于N的话就直接 A=A-N就行,在数学上有严格的证明可以证明 A < 2×N

if( mbedtls_mpi_cmp_abs( A, N ) >= 0 )
        mpi_sub_hlp( n, N->p, A->p );
    else
        /* prevent timing attacks */
        mpi_sub_hlp( n, A->p, T->p );

最后将T->p中的低n个字(也就是A->p)的内容清除是为了防止时序攻击,与算法的实现原理关系不大。

写在最后

从刚开始研究蒙哥马利到写完这篇博客,已经断断续续地过了一个星期的时间,在这个时间里我慢慢了解到了蒙哥马利算法的精巧,同时在实现的过程中也有很多小技巧需要去琢磨。在师兄们的无私帮助之下,我总算写完了这三篇博客,博客内也可能有一些错误,欢迎大家的批评指正。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值