This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
代码来源
代码来自GmSSL
的一个未合并的PR:2497946ac6/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
实现一致。
2497946ac6/src/sm2_z256_loong64.S (L94)
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
为上一个字加法进位)。
2497946ac6/src/sm2_z256_loong64.S (L104)
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判断的准确性:
2497946ac6/src/sm2_z256_loong64.S (L268)
add.d $t5, $a1, $t5
,这里$a1
是可能的进位,这一点是不一定成立的。但是,如果把它和261行add.d $t5, $t5, $s2
交换一下顺序,则成立,因为两个64位字的乘法产生的高64位字,不可能是2^{64}-1
。所以如果不交换顺序,结果是否正确,需要进一步证明,并没那么直观。2497946ac6/src/sm2_z256_loong64.S (L273)
这里是成立的。
- 约简还没完成,也就是上一步
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
来选择最终结果。