sm2_z256_loong64.S 代码分析 - emmansun/gmsm GitHub Wiki
代码来源
代码来自GmSSL
的一个未合并的PR:https://github.com/DengJianbo-loongson/GmSSL/blob/2497946ac6458ae1fb6931b66804dbc62cfffe44/src/sm2_z256_loong64.S
函数说明
__sm2_z256_modp_add
实现(t0, t1, t2, t3) = (t0, t1, t2, t3) + (a4, a5, a6, a7)
,每个代表64位整数,从低到高排列,也就是说t0
和a4
是最低64位数,也就是多字(Multi-Word)加法。
总体实现思路:
- 在RISC(如龙芯/LoongArch、MIPS)架构中,没有x86那样的进位标识寄存器,所以进位都要用
sltu
(无符号小于)等指令手动计算。容易出错! - 使用加
1
来实现mod P
,这里的1
为 $2^{256} - P$ 。 - 使用
or
合并进位(carry)。 - 使用
masknez
和maskeqz
来选择最终结果。
__sm2_z256_modp_dbl
实现(t0, t1, t2, t3) = (t0, t1, t2, t3)*2
,double运算。
总体实现思路:
- 使用移位操作:
srli.d
,srli.d
和算术运算alsl.d
实现double运算。alsl.d $t3, $t3, $t5, 1
表示t3 = t3<<1 + t5
- 其它和
__sm2_z256_modp_add
实现一致。
add.d $t5, $t1, $t8 //no carry
add.d $t5, $t5, $a1
改为
add.d $t5, $a1, $t8 //no carry
add.d $t5, $t5, $t1
改完后的 //no carry
注释才成立 (这里t8
的值为0x00000000ffffffff
,a1
为上一个字加法进位)。
add.d $a2, $a2, $a1
改为
or $a2, $a2, $a1
也可以,其实我们只关注a2
的值是0还是非0。
__sm2_z256_modp_sub
计算 (t0, t1, t2, t3) = (t0, t1, t2, t3) - (a4, a5, a6, a7)
。
总体实现思路:
- 和加法一样,在RISC(如龙芯/LoongArch、MIPS)架构中,没有x86那样的借位标识寄存器,所以借位都要用
sltu
(无符号小于)等指令手动计算。 - 如果最后一个字的减法有借位,则再减
1
来实现mod P
,这里的1
为 $2^{256} - P$ ;否则再减的就是0。 - 使用
maskeqz
来选择减数是0还是1
。
__sm2_z256_modp_neg_sub
计算(t0, t1, t2, t3) = (t0, t1, t2, t3) - (a4, a5, a6, a7)
。
和上面方法类似。
__sm2_z256_modp_haf
计算 (t0, t1, t2, t3) = (t0, t1, t2, t3) / 2
。
总体实现思路:
- 如果该数是奇数,则先减
1
,这里的1
为 $2^{256} - P$ ,相当于先加P;否则先减的就是0。 - 通过
andi $a2, $t0, 1
取最低位值。 - 使用
maskeqz
来选择减数是0还是1
。 - 通过
srli.d
和bstrins.d
进行除2操作。bstrins.d $t0, $t1, 63, 63
表示t0[63] = t1[0]
,t0
的其它位值保持不变。
__sm2_z256_modp_mont_mul
按字蒙哥马利模约减乘法(WW-MM),计算 (t0,t1,t2,t3) = (a4,a5,a6,a7) * (t0,t1,t2,t3)
。
实现思路:
- 可参考https://github.com/emmansun/gmsm/wiki/SM2-WWMM-(2)
$T_2=T_1 \ast P=t_0 \ast P= t_0 \ast (2^{256}-(2^{32} \ast 2^{192} + 0 \ast 2^{128} + (2^{32} - 1) \ast 2^{64} + 1))$
$T_2=(t_0-t_0>>32) \ast 2^{256}+(0 - t_0<<32) \ast 2^{192} + (0 - t_0>>32) \ast 2^{128} + (t_0 - t_0<<32) \ast 2^{64} - t_0$
$T_3=T + T_2=(t_4+t_0-t_0>>32) \ast 2^{256}+(t_3 - t_0<<32) \ast 2^{192} + (t_2 - t_0>>32) \ast 2^{128} + (t_1 + t_0 - t_0<<32) \ast 2^{64} $
注释:这里 $t_0<<32$ 是 $t_0 \ast 2^{32}$ 的低64位, $t_0>>32$ 是 $t_0 \ast 2^{32}$ 的高64位。 它这里先计算T的系数(WORD),再计算 $T_2$ 的系数。 - 几个no carry判断的准确性:
- https://github.com/DengJianbo-loongson/GmSSL/blob/2497946ac6458ae1fb6931b66804dbc62cfffe44/src/sm2_z256_loong64.S#L268
add.d $t5, $a1, $t5
,这里$a1
是可能的进位,这一点是不一定成立的。但是,如果把它和261行add.d $t5, $t5, $s2
交换一下顺序,则成立,因为两个64位字的乘法产生的高64位字,不可能是 $2^{64}-1$ 。所以如果不交换顺序,结果是否正确,需要进一步证明,并没那么直观。 - https://github.com/DengJianbo-loongson/GmSSL/blob/2497946ac6458ae1fb6931b66804dbc62cfffe44/src/sm2_z256_loong64.S#L273 这里是成立的。
- https://github.com/DengJianbo-loongson/GmSSL/blob/2497946ac6458ae1fb6931b66804dbc62cfffe44/src/sm2_z256_loong64.S#L268
- 约简还没完成,也就是上一步 $T_2 + T$ 还没算完,就开始了下一个字的乘法,代码可读性降低,为了性能?
- 最后,依然使用加
1
来实现mod P
,这里的1
为 $2^{256} - P$ 。
__sm2_z256_modp_mont_sqr
计算 (t0, t1, t2, t3) = (t0, t1, t2, t3)^2
。
总体实现思路:
- 先完整计算出平方结果,用8个64位字表示,算完后再进行蒙哥马利约简计算。
- 最后,依然使用加
1
来实现mod P
,这里的1
为 $2^{256} - P$ 。
总结
- 进位、借位要格外小心、无进位判断要严谨。
- 使用加(或者减)
1
来实现mod P
,这里的1
为 $2^{256} - P$ 。 - 使用
masknez
和(或)maskeqz
来选择最终结果。