384位NIST素域椭圆曲线的公开资料中对于快速约减算法是这么描述的:
摘自:Mathematical routines for the NIST prime elliptic curves (April 05, 2010)
p384 = 2 ^ 384 − 2 ^ 128 − 2 ^ 96 + 2 ^ 32 − 1 p384 = ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 ffffffff Routine 3.2.11 mp_mod_384 (r, a): Set r = a (mod p384 ) 1: {Note: the ai are 32–bit quantities.} 2: t ← ( a11 || a10 || a9 || a8 || a7 || a6 || a5 || a4 || a3 || a2 || a1 || a0 ) 3: s1 ← ( 0 || 0 || 0 || 0 || 0 || a23 || a22 || a21 || 0 || 0 || 0 || 0 ) 4: s2 ← ( a23 || a22 || a21 || a20 || a19 || a18 || a17 || a16 || a15 || a14 || a13 || a12 ) 5: s3 ← ( a20 || a19 || a18 || a17 || a16 || a15 || a14 || a13 || a12 || a23 || a22 || a21 ) 6: s4 ← ( a19 || a18 || a17 || a16 || a15 || a14 || a13 || a12 || a20 || 0 || a23 || 0 ) 7: s5 ← ( 0 || 0 || 0 || 0 || a23 || a22 || a21 || a20 || 0 || 0 || 0 || 0 ) 8: s6 ← ( 0 || 0 || 0 || 0 || 0 || 0 || a23 || a22 || a21 || 0 || 0 || a20 ) 9: d1 ← ( a22 || a21 || a20 || a19 || a18 || a17 || a16 || a15 || a14 || a13 || a12 || a23 ) 10: d2 ← ( 0 || 0 || 0 || 0 || 0 || 0 || 0 || a23 || a22 || a21 || a20 || 0 ) 11: d3 ← ( 0 || 0 || 0 || 0 || 0 || 0 || 0 || a23 || a23 || 0 || 0 || 0 ) 12: d1 ← p384 − d1 13: r ← t + 2s1 + s2 + s3 + s4 + s5 + s6 + d1 − d2 − d3 14: Reduce r mod p384 by subtraction of up to four multiples of p384.
为了用x64汇编语言实现这个算法,需要对算法的描述形式加以转变,使之更适用于编程。
第1阶段转换:水平翻转数值矩阵使其变为先低位后高位的表达形式
|a00|a01|a02|a03|a04|a05|a06|a07|a08|a09|a10|a11| ->t | 0 | 0 | 0 | 0 |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | ->s1 | 0 |a23| 0 |a20|a12|a13|a14|a15|a16|a17|a18|a19| ->s2 |a21|a22|a23|a12|a13|a14|a15|a16|a17|a18|a19|a20| ->s3 |a12|a13|a14|a15|a16|a17|a18|a19|a20|a21|a22|a23| ->s4 | 0 | 0 | 0 | 0 |a20|a21|a22|a23| 0 | 0 | 0 | 0 | ->s5 |a20| 0 | 0 |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | 0 | ->s6 ------------------------------------------------- |a23|a12|a13|a14|a15|a16|a17|a18|a19|a20|a21|a22| ->d1 | 0 |a20|a21|a22|a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 | ->d2 | 0 | 0 | 0 |a23|a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 | ->d3
第2阶段转换:按照元素连续性分组
分组1:(a00 ~ a19) |a00|a01|a02|a03|a04|a05|a06|a07|a08|a09|a10|a11| | 0 | 0 | 0 | 0 |a12|a13|a14|a15|a16|a17|a18|a19| | 0 | 0 | 0 |a12|a13|a14|a15|a16|a17|a18|a19| 0 | |a12|a13|a14|a15|a16|a17|a18|a19| 0 | 0 | 0 | 0 | ------------------------------------------------- | 0 |a12|a13|a14|a15|a16|a17|a18|a19| 0 | 0 | 0 | 分组2:(a20 ~ a23) | 0 | 0 | 0 | 0 |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | | 0 |a23| 0 |a20| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |a20| | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |a20|a21|a22|a23| | 0 | 0 | 0 | 0 |a20|a21|a22|a23| 0 | 0 | 0 | 0 | |a20| 0 | 0 |a21|a22|a23| 0 | 0 | 0 | 0 | 0 | 0 | ------------------------------------------------- |a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |a20|a21|a22| | 0 |a20|a21|a22|a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 0 | 0 |a23|a23| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
第3阶段转换:在保证每组运算非负前提下按64位补齐调整
分组1:(完成) |a00|a01|a02|a03|a04|a05|a06|a07|a08|a09|a10|a11| |a12|a13|a14|a15|a16|a17|a18|a19|a20|a21|a22|a23| | 0 | 0 | 0 | 0 |a12|a13|a14|a15|a16|a17|a18|a19| | 0 | 0 |a23|a12|a13|a14|a15|a16|a17|a18|a19|a20| ------------------------------------------------- |a23|a12|a13|a14|a15|a16|a17|a18|a19|a20| 0 | 0 | 分组2:(完成) | p384 | ------------------------------------------------- | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |a21|a22| 分组3:(等待继续优化) | 0 | 0 | 0 | 0 |a21|a22|a23| 0 | | 0 | 0 | 0 | 0 |a21|a22|a23| 0 | | 0 |a23| 0 |a20| 0 | 0 | 0 | 0 | |a21|a22| 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |a20|a21|a22|a23| |a20| 0 | 0 |a21|a22|a23| 0 | 0 | --------------------------------- | 0 |a20|a21|a22|a23| 0 | 0 | 0 | | 0 | 0 | 0 |a23|a23| 0 | 0 | 0 |
第4阶段转换:优化第3阶段的分组3,得到分组4和分组5
分组4: | 0 | 0 | 0 | 0 |a20|a21|a22|a23| 分组5: |a21|a22| 0 |a20|a21|a22|a23<<1 | | 0 | 0 | 0 | 0 |a21|a22| 0 | 0 | |a20|a23|a20|a21|a22|a23| 0 | 0 | --------------------------------- | 0 |a20|a21|a22|a23<<1 | 0 | 0 | | 0 | 0 |a20|a23| 0 | 0 | 0 | 0 |
运算顺序是先计算低位和短位长分组,计算顺序如下:
|a21|a22| 0 |a20|a21|a22|a23<<1 |(=) | 0 |a20|a21|a22|a23<<1 | 0 | 0 |(-) | 0 | 0 |a20|a23| 0 | 0 | 0 | 0 |(-) | 0 | 0 | 0 | 0 |a21|a22| 0 | 0 |(+) |a20|a23|a20|a21|a22|a23| 0 | 0 |(+) | 0 | 0 | 0 | 0 |a20|a21|a22|a23|a20|a21|a22|a23|(+) |a12|a13|a14|a15|a16|a17|a18|a19| 0 | 0 | 0 | 0 |(+) | 0 | 0 | 0 | 0 |a12|a13|a14|a15|a16|a17|a18|a19|(+) |a00|a01|a02|a03|a04|a05|a06|a07|a08|a09|a10|a11|(+) | 0 | 0 |a23|a12|a13|a14|a15|a16|a17|a18|a19|a20|(+) |a23|a12|a13|a14|a15|a16|a17|a18|a19|a20| 0 | 0 |(-) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |a21|a22|(-)
运算顺序表每行末端带括号的运算符含义分别是:(=)赋值操作,即初始值;(-)本行数值符号为负,做减法;(+)表示本行符号为正,做加法。
整个流程中,唯一可能出现负数的就是最后一行做减法时,之前的累加值必然非负,可以根据各个元素出现位置证明。令a21 = a22 = (2 ^ 32 - 1),其余元素皆为0,则累加结果得到最小值为-(2 ^ 384 - 2 ^ 320),且此数值绝对值小于p384,根据有限域运算规则,用p384减去即可得到最终结果。若最终值大于p384,则需要减去最多不超过4个p384。
寄存器规划
数据输入指针:通用寄存器rsi 数据输出指针:通用寄存器rdi 通用寄存器备份与恢复: |---------------|---------------| | xmm14 | xmm15 | |-------|-------|-------|-------| | r12 | r13 | r14 | r15 | |-------|-------|-------|-------| 数据输入缓存: |---------------|---------------|---------------|---------------|---------------|---------------| | xmm0 | xmm1 | xmm2 | xmm3 | xmm4 | xmm5 | |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| |a00|a01|a02|a03|a04|a05|a06|a07|a08|a09|a10|a11|a12|a13|a14|a15|a16|a17|a18|a19|a20|a21|a22|a23| |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| 运算过程数据:7个64位通用寄存器,从地到高排列 |-------|-------|-------|-------|-------|-------|-------| | r10 | r11 | r12 | r13 | r14 | r15 | rdx | |-------|-------|-------|-------|-------|-------|-------| 临时数据:5个通用寄存器 |-------|-------|-------|-------|-------| | rax | rsi | r8 | r9 | rcx | |-------|-------|-------|-------|-------| 其它寄存器:用于数据交换和输出到内存前的缓冲 |-------|-------|-------| | xmm6 | xmm7 | xmm8 | |-------|-------|-------|
简单性能测试:
1、自测程序test1.c,通过调用开源P运算库GMP的函数得到参考数据:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <gmp.h> int main(int argc, char *argv[]) { mpz_t a, b, c, p, r; //int i = 0; //初始化内存 mpz_init2(a, 384); mpz_init2(b, 384); mpz_init2(c, 768); mpz_init2(p, 384); mpz_init2(r, 384); //初始化数据 mpz_init_set_str(a, "EEEEEEEEEEEEEEEECCCCCCCCCCCCCCCCCD8964545891EBC5F8F0D7E9F7D5C30A3E7EB0B141E265DCC459138BCE6E7F2D", 16); mpz_init_set_str(b, "CD8964545891EBC5F8F0D7E9F7D5C30A3E7EB0B141E265DCC459138BCE6E7F2DEEEEEEEEEEEEEEEECCCCCCCCCCCCCCCC", 16); mpz_init_set_str(p, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff", 16); //乘法运算 mpz_mul(c, a, b);//c = a *b //求模运算 mpz_mod(r, c, p);//r = c mod p //结果输出 mpz_out_str(stdout, 16, c); fprintf(stdout, "\r\n"); mpz_out_str(stdout, 16, r); fprintf(stdout, "\r\n"); //释放内存 mpz_clear(a); mpz_clear(b); mpz_clear(c); mpz_clear(p); exit(EXIT_SUCCESS); }
编译并运行此程序,得到数据样本(过长的输出已折行):
gcc -Wall -O2 test1.c -lgmp -o test1 ./test1 bfd590d741994274668a339bec91ebef2acb6a25db704d7c7fe4faabc9b8246b 687462a249cfed730f66a066ce6f3c5d3b1a0c40603abeb865aaafee147c0410 c086d7bc4c82ac9e655392a75eef149809285ef9273c26162fb8bd29c14133dc d0b3b0b8418585333af83fc31fdfa4f201e40c4c1b96b3a81e49401c199d271147e95cc5baf0d05e858d088f22f6feed
编写自测程序test2,引用汇编程序文件mp_mod_384.s和mul384.s,测试计算结果以及性能
#include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> #include "mul384.h" #include "mp_mod_384.h" int main(int argc, char *argv[]) { uint64_t a[6], b[6], c[12], r[6], i; //初始化数据 a[5] = 0xEEEEEEEEEEEEEEEE; a[4] = 0xCCCCCCCCCCCCCCCC; a[3] = 0xCD8964545891EBC5; a[2] = 0xF8F0D7E9F7D5C30A; a[1] = 0x3E7EB0B141E265DC; a[0] = 0xC459138BCE6E7F2D; b[5] = 0xCD8964545891EBC5; b[4] = 0xF8F0D7E9F7D5C30A; b[3] = 0x3E7EB0B141E265DC; b[2] = 0xC459138BCE6E7F2D; b[1] = 0xEEEEEEEEEEEEEEEE; b[0] = 0xCCCCCCCCCCCCCCCC; //乘法运算 mul384(c, a, b); //进行1亿次求模运算 for(i = 0; i < 100000000; i++) mp_mod_384(r, c); //mp_mod_384(r, c); //乘法结果结果输出 for(i = 0; i < 12; i++) printf("%016lx", c[11 - i]); printf("\r\n"); //求模结果输出 for(i = 0; i < 6; i++) printf("%016lx", r[5 - i]); printf("\r\n"); exit(EXIT_SUCCESS); }
编译自测程序test2.c并计时运行:
gcc -Wall -O2 tes2.c mul384.s mp_mod_384.s -o test2 time ./test2 bfd590d741994274668a339bec91ebef2acb6a25db704d7c7fe4faabc9b8246b 687462a249cfed730f66a066ce6f3c5d3b1a0c40603abeb865aaafee147c0410 c086d7bc4c82ac9e655392a75eef149809285ef9273c26162fb8bd29c14133dc d0b3b0b8418585333af83fc31fdfa4f201e40c4c1b96b3a81e49401c199d271147e95cc5baf0d05e858d088f22f6feed real 0m4.395s user 0m4.391s sys 0m0.002s
求模结果正确。
完整汇编程序代码见后续文章:384位NIST素域椭圆曲线快速约减算法x64编程实现研究(下)