secp256k1算法详解二(大数操作关键理论及源码分析)

1 关键结构体

1.1 secp256k1_fe

secp256k1库域元素field element,其具体定义如下

复制代码
/** This field implementation represents the value as 10 uint32_t limbs in base
 *  2^26. */
typedef struct {
   /* A field element f represents the sum(i=0..9, f.n[i] << (i*26)) mod p,
    * where p is the field modulus, 2^256 - 2^32 - 977.
    *
    * The individual limbs f.n[i] can exceed 2^26; the field's magnitude roughly
    * corresponds to how much excess is allowed. The value
    * sum(i=0..9, f.n[i] << (i*26)) may exceed p, unless the field element is
    * normalized. */
    uint32_t n[10];
    /*
     * Magnitude m requires:
     *     n[i] <= 2 * m * (2^26 - 1) for i=0..8
     *     n[9] <= 2 * m * (2^22 - 1)
     *
     * Normalized requires:
     *     n[i] <= (2^26 - 1) for i=0..8
     *     sum(i=0..9, n[i] << (i*26)) < p
     *     (together these imply n[9] <= 2^22 - 1)
     */
    SECP256K1_FE_VERIFY_FIELDS
} secp256k1_fe;
复制代码

由定义可知,secp256k1_fe由10个uint32_t变量进行数据存储,9 个 26 位字(n[0]到n[8])和1 个 22 位字(n[9]),总位数为:9*26+22=256位,每个字大小为:n[i] ≤ 2^26 - 1(i=0..8),n[9] ≤ 2^22 - 1。在定义VERIFY时,m(magnitude)是一个缩放因子(scaling factor),用于控制每个字的最大允许值。具体来说:

对于前 9 个字(i=0..8):
n[i] ≤ 2 × m × (2^26 - 1)
这意味着每个字的最大值被放大了 2m 倍。
对于最后一个字(i=9):
n[9] ≤ 2 × m × (2^22 - 1)
同样被放大了 2m 倍。

在椭圆曲线运算(如模乘、点加)中,中间结果可能会超出单个字的范围。通过允许每个字暂时存储更大的值(通过m缩放),可以避免频繁的归一化(normalization)操作,从而提高效率。这里的归一化,是将每个字的值压缩到标准范围(如 n[i] ≤ 2^26 - 1)的过程。当m=1时:

前9个字的最大值为2 × (2^26 - 1) = 134,217,726(二进制下 27 位)。
最后一个字的最大值为2 × (2^22 - 1) = 8,388,606(二进制下 24 位)。

此外根据椭圆曲线方程,x坐标三次方运算可能导致中间结果快速增长,需要更大的临时存储空间,所以x能需要更大缩放希数,如下定义

复制代码
/** Maximum allowed magnitudes for group element coordinates
 *  in affine (x, y) and jacobian (x, y, z) representation. */
#define SECP256K1_GE_X_MAGNITUDE_MAX  4
#define SECP256K1_GE_Y_MAGNITUDE_MAX  3
#define SECP256K1_GEJ_X_MAGNITUDE_MAX 4
#define SECP256K1_GEJ_Y_MAGNITUDE_MAX 4
#define SECP256K1_GEJ_Z_MAGNITUDE_MAX 1
复制代码

1.2 secp256k1_fe_storage

typedef struct {
    uint32_t n[8];
} secp256k1_fe_storage;

正如其名称显示,该数据结构用8个uint32_t表示大数,最大支持256位大数。

1.3 secp256k1_ge & secp256k1_gej

群元素group element,椭圆曲线上点的仿射坐标(affine coordinates),x和y坐标都是secp256k1_fe类型大数,infinity变量指明点是否为无穷远点。

复制代码
/** A group element in affine coordinates on the secp256k1 curve,
 *  or occasionally on an isomorphic curve of the form y^2 = x^3 + 7*t^6.
 *  Note: For exhaustive test mode, secp256k1 is replaced by a small subgroup of a different curve.
 */
typedef struct {
    secp256k1_fe x;
    secp256k1_fe y;
    int infinity; /* whether this represents the point at infinity */
} secp256k1_ge;
复制代码

椭圆曲线上点的Jacobian射影坐标,射影坐标(x, y, z)对应的仿射坐标为(x/z^2, y/z^3),同样infinity变量指明点是否为无穷远点。

复制代码
/** A group element of the secp256k1 curve, in jacobian coordinates.
 *  Note: For exhastive test mode, secp256k1 is replaced by a small subgroup of a different curve.
 */
typedef struct {
    secp256k1_fe x; /* actual X: x/z^2 */
    secp256k1_fe y; /* actual Y: y/z^3 */
    secp256k1_fe z;
    int infinity; /* whether this represents the point at infinity */
} secp256k1_gej;
复制代码

1.4 secp256k1_ge_storage

以存储形式表示的group element,该形式坐标无法表示无穷远点。

typedef struct {
    secp256k1_fe_storage x;
    secp256k1_fe_storage y;
} secp256k1_ge_storage;

2 关键理论

2.1 Schoolbook 算法

Schoolbook 算法(竖式乘法算法)是最基础的多精度整数乘法方法,直接模拟人类手算乘法的过程。它易于理解和实现,适用于中小规模的数值计算。

一 核心原理

Schoolbook算法的核心思想是将多位数乘法拆解为多个单字乘法和累加操作,类似于小学竖式计算:
逐位相乘:用乘数的每一位分别去乘被乘数的每一位,得到部分积。
错位累加:将每个部分积按位对齐后累加,得到最终结果。

二 算法步骤(以十进制为例)

假设计算两个多位数A*B,其中:

A = an-1an-2...a1a0(共n位)

B = bm-1bm-2...b1b0(共m位)

具体步骤:

1 初始化结果数组:创建一个长度为n+m的数组C,用于存储中间结果和最终乘积。
2 逐位相乘并累加

复制代码
1 for i in range(m):  # 遍历乘数的每一位
2     carry = 0
3     for j in range(n):  # 遍历被乘数的每一位
4         # 计算当前位的乘积和进位
5         product = a[j] × b[i] + C[i+j] + carry
6         C[i+j] = product % 10  # 当前位的结果
7         carry = product // 10  # 进位
8     C[i+n] = carry  # 处理最高位的进位
复制代码

3 结果处理

数组C即为最终乘积,可能包含前导零,需按需处理。

三 计算机实现细节(多字乘法)

在计算机中,多精度整数通常以字数组(Word Array)形式存储,每个字(Word)是一个固定位数的整数(如 32 位或 64 位)。此时 Schoolbook 算法的实现需注意:

1 单字乘法的处理

两个w位的字相乘可能产生2w位的结果,需用两个字表示。例如,32位 × 32位 → 64位结果,需拆分为高位和低位两部分。

2 进位管理

每次乘法产生的进位需传递到高位,累加中间结果时需处理多字进位。

复制代码
 1 A = [0xFF, 0xFF]
 2 B = [0xFF, 0xFF, 0xFF]
 3 C = [0, 0, 0, 0, 0]  # 结果数组
 4 w = 8
 5 m = 3
 6 n = 2
 7 for i in range(m):
 8     carry = 0
 9     
10     for j in range(n):
11         product = A[j] * B[i]
12         product_high = product >> w
13         product_low = product & ((1 << w) - 1)
14         
15         # 累加低位部分到当前位置
16         sum_low = product_low + C[i+j]
17         carry = sum_low >> w  # 进位传递到下一位
18         C[i+j] = sum_low & ((1 << w) - 1)
19         
20         # 累加高位部分及进位到下一个位置
21         C[i+j+1] += product_high+carry
22 
23 print("result\n0x", end="")
24 for i in range(m+n):
25     print("%02x"%C[m+n-1-i], end="")
26 print("")
复制代码

程序中第11~13行将乘积分成高低字节两部分,第16行将低字节部分和当前位置现有值相加,17行获取相加结果的进位,18行将相加结果的低字节部分保留在当前位置,21行将之前乘积的高字节部分和产生的进位一起累加到下一个位置,最后输出最终结果。

2.2 二次剩余及相关理论

一 欧拉函数

对于正整数n,欧拉函数φ(n)表示小于n且与n互质的数的个数。如果n是质数,则φ(n)=n-1。如果n可以分解成两个互质的整数之积,则其欧拉函数等于其互质因子欧拉函数之积,即φ(n)=φ(p1p2)=φ(p1)φ(p2)。

二 欧拉定理

费马小定理:若p是质数,且整数a满足gcd(a, p) = 1,则ap-1 ≡ 1 mod p。其经典证明如下:

1 构造互质的剩余希

考虑小于p的正整数集合S = { 1, 2, 3, ..., p-1 },由于p是质数,且gcd(a, p) = 1,则集合S中每个元素与p互质,且a与p也互质。

构造新集合T = { a*1 mod p, a*2 mod p, a*(p-1) mod p},即对S中每个元素乘以a后取模p。

2 证明T是S的一个排列

需证明T中的元素与S中的元素完全相同(仅顺序可能不同):

互质性:由于gcd(a, p) = 1且gcd(k, p) = 1(k ∈ S),根据互质性质,gcd(a*k, p) = 1,因此a*k mod p仍属于S(即T ⊆ S)。

唯一性:若a*k1 ≡ a*k2 mod p,则a*(k1 - k2) ≡ 0 mod p,由于gcd(a, p) = 1,可推出k1 ≡ k2 mod p,又因为k1,k2 < p,故k1 = k2,即T中元素互不相同。

因此,T是S的一个排列(元素完全一致,顺序不同)。

3 两边乘积的同余关系

由于T是S的排列,两集合中的所有元素的乘积模p相等:

(a*1)*(a*2)*...*(a*(p-1)) ≡ 1*2*...*(p-1) mod p

左边可化简为:

ap-1*(1*2*...*(p-1) ≡ (1*2*...*(p-1) mod p,令W = 1*2*...*(p-1),则上式变为:

ap-1*W ≡ W mod p

4 消去W得到结论

由于p是质数,W = (p-1)!与p互质,因此可以同余式两边同时除于W(或乘以W的逆元),即得ap-1 ≡ W mod p。

欧拉定理是费马小定理的推广,使用于更一般的情形。其定义:若两个正整数a和n互质(即gcd(a, n) = 1),则aφ(n) ≡ 1 mod n,φ(n)是欧拉函数。其证明过程类似。

三 二次剩余

设p是一个正整数(通常为质数),且整数a与p互质(即gcd(a, p) = 1),如果存在整数x使得:x2 ≡ a mod p,则称a是模p的二次剩余;否则,称a是模p的二次非剩余。简单来说,二次剩余是“模意义下能开平方的数”。例如:

模7时,12 ≡ 1,22 ≡ 4,32 ≡ 2,42 ≡ 2,52 ≡ 4,62 ≡ 1,因此1、2、4是模7的二次剩余,3、5、6是模7的二次非剩余。

二次剩余有如下关键性质

1 数量对称性

若p是奇质数,则模p的二次剩余和二次非剩余各有(p-1)/2个。

2 欧拉判别法

对于奇质数p和与p互质的整数a,a是模p的二次剩余的充要条件是:a(p-1)/2 ≡ 1 mod p,若a是二次非剩余,则:a(p-1)/2 ≡ -1 mod p。

3 勒让德符号

为简化二次剩余的表示,定义勒让德符号

其核心性质是:

后续的secp256k1_fe_sqrt函数实现用到了这里的二次剩余理论,在这里先提前说一下,因为a是模p的二次剩余的充要条件是a(p-1)/2 ≡ 1 mod p,则有a*a(p-1)/2 ≡ a mod p,即a(p+1)/2 ≡ a mod p,如果存在k使得p=4*k+3,那么(p+1)/4正好是整数,这时有(a(p+1)/4)2 ≡ a mod p,也就是说如果a是模p的二次剩余,则a(p+1)/4必是a的开方之一(模p操作下),这正是secp256k1_fe_sqrt函数的理论基础。

3 关键函数解析

3.1 secp256k1_fe_normalize

该函数实现大数的归一化操作,其实现如下

复制代码
 1 static void secp256k1_fe_normalize(secp256k1_fe *r) {
 2     uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
 3              t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
 4 
 5     /* Reduce t9 at the start so there will be at most a single carry from the first pass */
 6     uint32_t m;
 7     uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL;
 8 
 9     /* The first pass ensures the magnitude is 1, ... */
10     t0 += x * 0x3D1UL; t1 += (x << 6);
11     t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
12     t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
13     t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2;
14     t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3;
15     t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4;
16     t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5;
17     t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6;
18     t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7;
19     t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8;
20 
21     /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */
22 
23     /* At most a single final reduction is needed; check if the value is >= the field characteristic */
24     x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL)
25         & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
26 
27     /* Apply the final reduction (for constant-time behaviour, we do it always) */
28     t0 += x * 0x3D1UL; t1 += (x << 6);
29     t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
30     t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
31     t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL;
32     t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL;
33     t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL;
34     t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL;
35     t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL;
36     t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL;
37     t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL;
38 
39     /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */
40 
41     /* Mask off the possible multiple of 2^256 from the final reduction */
42     t9 &= 0x03FFFFFUL;
43 
44     r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
45     r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9;
46 }
复制代码

域的模数p = 2256 - 232 - 977,域中大数r被分为10个limbs,其标准表示为每个limb是26-bit数(除了最后一个是22-bit数),即:

如之前所描述,由于magnitude的存在,在运算过程中,会使某些limbs超过26-bit,导致进位错误。归一化目标:1 确保最终值 < p; 2 所有limbs小于226(除了t9,小于222)。

在函数中共进行了两次模约减操作,首轮模约减主要目的是去除高位t9的22-bit之后多余部分:

uint32_t x = t9 >> 22;  // 取出超过 256 位的部分
t9 &= 0x03FFFFFUL;      // 保留低 22 位

这里的x值为r/2256向下取整,由SECP256K1_GE_X_MAGNITUDE_MAX等宏定义可知x最大为4-bit数,因为2256 ≡ 232 + 977 mod p,所以超出2256部分即为 x*2256 ≡ x*(232 + 977) mod p ≡ x*(1 << 32) + x*977 mod p ≡ x*(1 << 6)*226 + x*977 mod p,由以上推理可知,x*2256在模p操作下,可分解为两部分,其中x*977(即x*0x3D1)可加到t0部分,而x*232可以将乘积的高于26-bit部分x<<6加到t1,由于x最大为4-bit数,所以x<<6最大为10-bit数,也就是x*2256高位在模p意义下,只能循环传导到t0和t1,对应代码第10行。之后11~19行,依次按标准模式计算t0~t8,本级保留低26-bits,进位加到下一级,最终所有进位传导到t9。运行进行到这里会有两种情况:1, t9 >> 22为1,即 t9有进位,说明大数归一化值还大于2256,需要进一步归约;2, t9 == 222 - 1(即最大值),t2~t8也都为0x3FFFFFF最大值,因为2256 - p = 0x1000003d1 = 0x40*226 + 0x3d1,所以如果t1 + 0x40 + ((t0 + 0x3D1UL) >> 26大于0x3FFFFFF,则必然产生向t2的进位,从而直接导致最终t9超过最大值,也即和超过2256,假设大数归一化值是A,可用不等式A+(2256 - p) ≥ 2256,即A ≥ p,当A等于p时,其加法可以通过下图清楚的看到各进位情况。简单的说就是这两种情况就是第一次归一化后的值大于2256或者大于或等于p,对应代码24~25行,这两种情况下就仍需进一步归一化操作(可以进一步验证在magnitude不大于4的情况下,第一次归一化操作后进位x最大为1)。

类似于第一次归一化,代码28~37行,将可能为1的进位x依次传导到t0~t9中,然后第42行将已经“补偿”到低位的最高位用且操作消除掉,最后44~45将结果赋值给远大数变量。了解了该函数的原理,函数secp256k1_fe_normalize_weak就更好理解了,它只是进行了一次归一化操作,也就是说最终结果t9可能还在23bit处包含1个最高进位。

3.2 secp256k1_fe_normalizes_to_zero

static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
    uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
             t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];

    /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
    uint32_t z0, z1;

    /* Reduce t9 at the start so there will be at most a single carry from the first pass */
    uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL;

    /* The first pass ensures the magnitude is 1, ... */
    t0 += x * 0x3D1UL; t1 += (x << 6);
    t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0  = t0; z1  = t0 ^ 0x3D0UL;
    t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL;
    t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2;
    t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3;
    t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4;
    t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5;
    t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6;
    t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7;
    t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8;
                                         z0 |= t9; z1 &= t9 ^ 0x3C00000UL;

    /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */

    return (z0 == 0) | (z1 == 0x3FFFFFFUL);
}
secp256k1_fe_normalize_to_zero

该函数在不完全归一化(normalize)整个字段元素的情况下,判断其值是否为零或等于模数,代码中z0用于检测是否所有位都为 0,z1用于检测是否所有位构成了模数p。类似secp256k1_fe_normalizes_to_zero_var函数是secp256k1_fe_normalizes_to_zero的变体(var)版,其功能与前者一致:判断一个字段元素是否为0或模数p(即归一化后是否为 0),但使用了更快路径和懒惰归一化以提高性能,该函数的关键代码如下:

复制代码
t0 = r->n[0];
t9 = r->n[9];
x = t9 >> 22;
t0 += x * 0x3D1UL;
z0 = t0 & 0x3FFFFFFUL;
z1 = z0 ^ 0x3D0UL;

if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) {
    return 0;
}
复制代码

若t9有进位(超过 22 位),说明有“≥2256”成分,我们提取出这部分(乘以 0x3D1 并加回 t0),这是归一化关键。然后立即对 t0 做一次判断:z0 == 0 表示可能是 0,z1 == 0x3FFFFFFUL 表示可能是 P(因为 t0 应该是 0x3D0),如果两个都不满足,直接返回 0,这是个非常快的判断分支。其他部分代码就和secp256k1_fe_normalizes_to_zero一致了。

3.3 secp256k1_fe_mul_inner

该函数是大数乘法的内部实现,libsecp256k1的作者Pieter Wuille的设计理念可以总结为:"所有公共API都要求输入是完全归一化的,但内部函数可以处理非归一化形式。中间值的大小必须受控,但可以比26位大(比如在该函数内部被限制在30bit),为的是避免频繁的carry"。以下是完整函数

  1 SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) {
  2     uint64_t c, d;
  3     uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8;
  4     uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7;
  5     const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL;
  6 
  7     VERIFY_BITS(a[0], 30);
  8     VERIFY_BITS(a[1], 30);
  9     VERIFY_BITS(a[2], 30);
 10     VERIFY_BITS(a[3], 30);
 11     VERIFY_BITS(a[4], 30);
 12     VERIFY_BITS(a[5], 30);
 13     VERIFY_BITS(a[6], 30);
 14     VERIFY_BITS(a[7], 30);
 15     VERIFY_BITS(a[8], 30);
 16     VERIFY_BITS(a[9], 26);
 17     VERIFY_BITS(b[0], 30);
 18     VERIFY_BITS(b[1], 30);
 19     VERIFY_BITS(b[2], 30);
 20     VERIFY_BITS(b[3], 30);
 21     VERIFY_BITS(b[4], 30);
 22     VERIFY_BITS(b[5], 30);
 23     VERIFY_BITS(b[6], 30);
 24     VERIFY_BITS(b[7], 30);
 25     VERIFY_BITS(b[8], 30);
 26     VERIFY_BITS(b[9], 26);
 27 
 28     /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n.
 29      *  for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
 30      *  for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9)
 31      *  Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0].
 32      */
 33 
 34     d  = (uint64_t)a[0] * b[9]
 35        + (uint64_t)a[1] * b[8]
 36        + (uint64_t)a[2] * b[7]
 37        + (uint64_t)a[3] * b[6]
 38        + (uint64_t)a[4] * b[5]
 39        + (uint64_t)a[5] * b[4]
 40        + (uint64_t)a[6] * b[3]
 41        + (uint64_t)a[7] * b[2]
 42        + (uint64_t)a[8] * b[1]
 43        + (uint64_t)a[9] * b[0];
 44     /* VERIFY_BITS(d, 64); */
 45     /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */
 46     t9 = d & M; d >>= 26;
 47     VERIFY_BITS(t9, 26);
 48     VERIFY_BITS(d, 38);
 49     /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */
 50 
 51     c  = (uint64_t)a[0] * b[0];
 52     VERIFY_BITS(c, 60);
 53     /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */
 54     d += (uint64_t)a[1] * b[9]
 55        + (uint64_t)a[2] * b[8]
 56        + (uint64_t)a[3] * b[7]
 57        + (uint64_t)a[4] * b[6]
 58        + (uint64_t)a[5] * b[5]
 59        + (uint64_t)a[6] * b[4]
 60        + (uint64_t)a[7] * b[3]
 61        + (uint64_t)a[8] * b[2]
 62        + (uint64_t)a[9] * b[1];
 63     VERIFY_BITS(d, 63);
 64     /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */
 65     u0 = d & M; d >>= 26; c += u0 * R0;
 66     VERIFY_BITS(u0, 26);
 67     VERIFY_BITS(d, 37);
 68     VERIFY_BITS(c, 61);
 69     /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */
 70     t0 = c & M; c >>= 26; c += u0 * R1;
 71     VERIFY_BITS(t0, 26);
 72     VERIFY_BITS(c, 37);
 73     /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */
 74     /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */
 75 
 76     c += (uint64_t)a[0] * b[1]
 77        + (uint64_t)a[1] * b[0];
 78     VERIFY_BITS(c, 62);
 79     /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */
 80     d += (uint64_t)a[2] * b[9]
 81        + (uint64_t)a[3] * b[8]
 82        + (uint64_t)a[4] * b[7]
 83        + (uint64_t)a[5] * b[6]
 84        + (uint64_t)a[6] * b[5]
 85        + (uint64_t)a[7] * b[4]
 86        + (uint64_t)a[8] * b[3]
 87        + (uint64_t)a[9] * b[2];
 88     VERIFY_BITS(d, 63);
 89     /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */
 90     u1 = d & M; d >>= 26; c += u1 * R0;
 91     VERIFY_BITS(u1, 26);
 92     VERIFY_BITS(d, 37);
 93     VERIFY_BITS(c, 63);
 94     /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */
 95     t1 = c & M; c >>= 26; c += u1 * R1;
 96     VERIFY_BITS(t1, 26);
 97     VERIFY_BITS(c, 38);
 98     /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */
 99     /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */
100 
101     c += (uint64_t)a[0] * b[2]
102        + (uint64_t)a[1] * b[1]
103        + (uint64_t)a[2] * b[0];
104     VERIFY_BITS(c, 62);
105     /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */
106     d += (uint64_t)a[3] * b[9]
107        + (uint64_t)a[4] * b[8]
108        + (uint64_t)a[5] * b[7]
109        + (uint64_t)a[6] * b[6]
110        + (uint64_t)a[7] * b[5]
111        + (uint64_t)a[8] * b[4]
112        + (uint64_t)a[9] * b[3];
113     VERIFY_BITS(d, 63);
114     /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */
115     u2 = d & M; d >>= 26; c += u2 * R0;
116     VERIFY_BITS(u2, 26);
117     VERIFY_BITS(d, 37);
118     VERIFY_BITS(c, 63);
119     /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */
120     t2 = c & M; c >>= 26; c += u2 * R1;
121     VERIFY_BITS(t2, 26);
122     VERIFY_BITS(c, 38);
123     /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */
124     /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */
125 
126     c += (uint64_t)a[0] * b[3]
127        + (uint64_t)a[1] * b[2]
128        + (uint64_t)a[2] * b[1]
129        + (uint64_t)a[3] * b[0];
130     VERIFY_BITS(c, 63);
131     /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */
132     d += (uint64_t)a[4] * b[9]
133        + (uint64_t)a[5] * b[8]
134        + (uint64_t)a[6] * b[7]
135        + (uint64_t)a[7] * b[6]
136        + (uint64_t)a[8] * b[5]
137        + (uint64_t)a[9] * b[4];
138     VERIFY_BITS(d, 63);
139     /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */
140     u3 = d & M; d >>= 26; c += u3 * R0;
141     VERIFY_BITS(u3, 26);
142     VERIFY_BITS(d, 37);
143     /* VERIFY_BITS(c, 64); */
144     /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */
145     t3 = c & M; c >>= 26; c += u3 * R1;
146     VERIFY_BITS(t3, 26);
147     VERIFY_BITS(c, 39);
148     /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */
149     /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */
150 
151     c += (uint64_t)a[0] * b[4]
152        + (uint64_t)a[1] * b[3]
153        + (uint64_t)a[2] * b[2]
154        + (uint64_t)a[3] * b[1]
155        + (uint64_t)a[4] * b[0];
156     VERIFY_BITS(c, 63);
157     /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */
158     d += (uint64_t)a[5] * b[9]
159        + (uint64_t)a[6] * b[8]
160        + (uint64_t)a[7] * b[7]
161        + (uint64_t)a[8] * b[6]
162        + (uint64_t)a[9] * b[5];
163     VERIFY_BITS(d, 62);
164     /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */
165     u4 = d & M; d >>= 26; c += u4 * R0;
166     VERIFY_BITS(u4, 26);
167     VERIFY_BITS(d, 36);
168     /* VERIFY_BITS(c, 64); */
169     /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */
170     t4 = c & M; c >>= 26; c += u4 * R1;
171     VERIFY_BITS(t4, 26);
172     VERIFY_BITS(c, 39);
173     /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */
174     /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */
175 
176     c += (uint64_t)a[0] * b[5]
177        + (uint64_t)a[1] * b[4]
178        + (uint64_t)a[2] * b[3]
179        + (uint64_t)a[3] * b[2]
180        + (uint64_t)a[4] * b[1]
181        + (uint64_t)a[5] * b[0];
182     VERIFY_BITS(c, 63);
183     /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */
184     d += (uint64_t)a[6] * b[9]
185        + (uint64_t)a[7] * b[8]
186        + (uint64_t)a[8] * b[7]
187        + (uint64_t)a[9] * b[6];
188     VERIFY_BITS(d, 62);
189     /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */
190     u5 = d & M; d >>= 26; c += u5 * R0;
191     VERIFY_BITS(u5, 26);
192     VERIFY_BITS(d, 36);
193     /* VERIFY_BITS(c, 64); */
194     /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */
195     t5 = c & M; c >>= 26; c += u5 * R1;
196     VERIFY_BITS(t5, 26);
197     VERIFY_BITS(c, 39);
198     /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */
199     /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */
200 
201     c += (uint64_t)a[0] * b[6]
202        + (uint64_t)a[1] * b[5]
203        + (uint64_t)a[2] * b[4]
204        + (uint64_t)a[3] * b[3]
205        + (uint64_t)a[4] * b[2]
206        + (uint64_t)a[5] * b[1]
207        + (uint64_t)a[6] * b[0];
208     VERIFY_BITS(c, 63);
209     /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */
210     d += (uint64_t)a[7] * b[9]
211        + (uint64_t)a[8] * b[8]
212        + (uint64_t)a[9] * b[7];
213     VERIFY_BITS(d, 61);
214     /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */
215     u6 = d & M; d >>= 26; c += u6 * R0;
216     VERIFY_BITS(u6, 26);
217     VERIFY_BITS(d, 35);
218     /* VERIFY_BITS(c, 64); */
219     /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */
220     t6 = c & M; c >>= 26; c += u6 * R1;
221     VERIFY_BITS(t6, 26);
222     VERIFY_BITS(c, 39);
223     /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */
224     /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */
225 
226     c += (uint64_t)a[0] * b[7]
227        + (uint64_t)a[1] * b[6]
228        + (uint64_t)a[2] * b[5]
229        + (uint64_t)a[3] * b[4]
230        + (uint64_t)a[4] * b[3]
231        + (uint64_t)a[5] * b[2]
232        + (uint64_t)a[6] * b[1]
233        + (uint64_t)a[7] * b[0];
234     /* VERIFY_BITS(c, 64); */
235     VERIFY_CHECK(c <= 0x8000007C00000007ULL);
236     /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */
237     d += (uint64_t)a[8] * b[9]
238        + (uint64_t)a[9] * b[8];
239     VERIFY_BITS(d, 58);
240     /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */
241     u7 = d & M; d >>= 26; c += u7 * R0;
242     VERIFY_BITS(u7, 26);
243     VERIFY_BITS(d, 32);
244     /* VERIFY_BITS(c, 64); */
245     VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL);
246     /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */
247     t7 = c & M; c >>= 26; c += u7 * R1;
248     VERIFY_BITS(t7, 26);
249     VERIFY_BITS(c, 38);
250     /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */
251     /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */
252 
253     c += (uint64_t)a[0] * b[8]
254        + (uint64_t)a[1] * b[7]
255        + (uint64_t)a[2] * b[6]
256        + (uint64_t)a[3] * b[5]
257        + (uint64_t)a[4] * b[4]
258        + (uint64_t)a[5] * b[3]
259        + (uint64_t)a[6] * b[2]
260        + (uint64_t)a[7] * b[1]
261        + (uint64_t)a[8] * b[0];
262     /* VERIFY_BITS(c, 64); */
263     VERIFY_CHECK(c <= 0x9000007B80000008ULL);
264     /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
265     d += (uint64_t)a[9] * b[9];
266     VERIFY_BITS(d, 57);
267     /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
268     u8 = d & M; d >>= 26; c += u8 * R0;
269     VERIFY_BITS(u8, 26);
270     VERIFY_BITS(d, 31);
271     /* VERIFY_BITS(c, 64); */
272     VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL);
273     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
274 
275     r[3] = t3;
276     VERIFY_BITS(r[3], 26);
277     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
278     r[4] = t4;
279     VERIFY_BITS(r[4], 26);
280     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
281     r[5] = t5;
282     VERIFY_BITS(r[5], 26);
283     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
284     r[6] = t6;
285     VERIFY_BITS(r[6], 26);
286     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
287     r[7] = t7;
288     VERIFY_BITS(r[7], 26);
289     /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
290 
291     r[8] = c & M; c >>= 26; c += u8 * R1;
292     VERIFY_BITS(r[8], 26);
293     VERIFY_BITS(c, 39);
294     /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
295     /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
296     c   += d * R0 + t9;
297     VERIFY_BITS(c, 45);
298     /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
299     r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4);
300     VERIFY_BITS(r[9], 22);
301     VERIFY_BITS(c, 46);
302     /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
303     /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
304     /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
305 
306     d    = c * (R0 >> 4) + t0;
307     VERIFY_BITS(d, 56);
308     /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
309     r[0] = d & M; d >>= 26;
310     VERIFY_BITS(r[0], 26);
311     VERIFY_BITS(d, 30);
312     /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
313     d   += c * (R1 >> 4) + t1;
314     VERIFY_BITS(d, 53);
315     VERIFY_CHECK(d <= 0x10000003FFFFBFULL);
316     /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
317     /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
318     r[1] = d & M; d >>= 26;
319     VERIFY_BITS(r[1], 26);
320     VERIFY_BITS(d, 27);
321     VERIFY_CHECK(d <= 0x4000000ULL);
322     /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
323     d   += t2;
324     VERIFY_BITS(d, 27);
325     /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
326     r[2] = d;
327     VERIFY_BITS(r[2], 27);
328     /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
329 }
secp256k1_fe_mul_inner

函数中第5行对M,R0,R1变量进行了初始化,M取值为了进行异或操作,使得每个limb保留26bit,R0和R1取值和secp256k1_fe_normalize中分析类似,只不过是将大数超过2260部分归一化到低位limb,即 2260 ≡ 0x1000003d10 mod p ≡ (1 << 36) + 0x3d10 mod p ≡ 0x400*226 + 0x3d10 mod p,归一化最低limb部分对应参数R0=0x3d10,次低limb部分对应参数R1=0x400。接下来7~26行是对输入参数的合法性检查,本节开头已经对此做出说明。之后第28~31行的注释说明了数组形式大数的具体含义,以及代码注释中px的含义,此外[x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0],等式表示大数中超过2260部分x,可以通过之前提到的归一化操作,借助R0和R1归一化到最低两个位置,即x*2260 ≡ x*0x400*226 + x*0x3d10 mod p ≡ x*R1 + x*R0 mod p。之后34~43行先求了p9位置的累加和d,46行将累加和d的低26bit值t9留在了p9位置,而后对累加和进行右移位操作,保留高位作为进位放在了p10位置。51行求得了p0位置的值,第54~62行求p10位置累加和,并将该累加和加到了之前的进位d上,第65行,u0是累加和的低26bit值对应位置是p10,继续进行右移位操作保留高位值d(对应位置p11),该行最后一条语句提前为u0向低位归一化做准备(69行注释说明了大数乘积当前各位置对应的实际取值,因为u0还没有完成归一化,所以最低p0位置应为新t加上u0*R0之前的值),70行完成了高位u0向最低两位的归一化操作,此时c为向p1位置的进位,t0为p0位置的值(注释73是归一化之前的对应值,74行是u0完成归一化后各位置对应值,此时已经消去u0)。

接下来,76~77行继续求p1位置对应的大数乘法累加和,并将其加到之前的进位t上,80~87求p11位置对应的大数乘法累加和,并将其加到上一次的进位值d上,第90行保留d低26bit作为p11位置上实际值,并右移保留高位作为向p12位置的进位,同样改行最后一条语句为位置p11向次低位p1做归一化做准备,95行完成归一化操作并移除u1,并获取p1位置值t1。再之后,第101~124行获取p2位置值t2,第126~149行获取p3位置值t3,第151~174行获取p4位置值t4,第176~199行获取p5位置值t5,第201~224行获取p6位置值t6,第226~251行获取p7位置值t7,第253~295行获取p8位置t8,在此期间又将t3~t7存储到了最终结果r[3]~r[7]中(应该是程序做过极限测试,后续的归一化操作已经不会影响到该部分最终的取值)。

第296行将来自p8位置的进位和p18位置d对应归一化低位值以及最一开始获得的t9进行相加获取p9位置的值c,之后,第299行只保留低22bit值赋值给r[9](最终归一化结果最高位置只保留22bit),右移22bit保留高位c,最后将p18位置d对应归一化高位值和c进行相加(在此之前都是按照260位大数进行归一化操作,而进行到这一步,c已经相当于超出大数256位之后对应的值),接下来,将按照256位大数进行归一化操作,第306行首先c到最低位部分归一化操作,将对应值和t0相加,第309行将和d低26bit保存到r[0],右移保留高位d,第313行将对应次低位部分归一化值和t1以及上一步的d进行累加,并更新为d,第318~326行按相同方式依次更新t[1]和t[2],进行到这里所有归一化进位已经处理完毕算法结束。而大数平方函数secp256k1_fe_sqr_inner的处理过程和该函数基本一致,不再进行详细分析。

3.4 secp256k1_fe_sqrt

之前的二次剩余理论部分已经得出a如果是模p的二次剩余,则a(p+1)/4必是a的开平方,先直接给出函数实现

  1 /*
  2 * (p+1)/4 = 
  3 * 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
  4 * 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
  5 * 111111111111110111111111111111111111100001100
  6 * 可以将该二进制按连续1进行分块,分三块,连续1的个数分别是2,22,223
  7 */
  8 static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
  9     /** Given that p is congruent to 3 mod 4, we can compute the square root of
 10      *  a mod p as the (p+1)/4'th power of a.
 11      *
 12      *  As (p+1)/4 is an even number, it will have the same result for a and for
 13      *  (-a). Only one of these two numbers actually has a square root however,
 14      *  so we test at the end by squaring and comparing to the input.
 15      *  Also because (p+1)/4 is an even number, the computed square root is
 16      *  itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)).
 17      */
 18     secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
 19     int j;
 20 
 21     /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
 22      *  { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
 23      *  1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
 24      */
 25     secp256k1_fe_sqr(&x2, a);
 26     secp256k1_fe_mul(&x2, &x2, a);        // a^3 0b11
 27 
 28     secp256k1_fe_sqr(&x3, &x2);            // a^6 0b110
 29     secp256k1_fe_mul(&x3, &x3, a);        // x3 a^7 0b111
 30 
 31     x6 = x3;
 32     for (j=0; j<3; j++) {               // 0b111000
 33         secp256k1_fe_sqr(&x6, &x6);
 34     }
 35     secp256k1_fe_mul(&x6, &x6, &x3);    // x6 a^63 0b111111
 36 
 37     x9 = x6;
 38     for (j=0; j<3; j++) {               // 0b111111000
 39         secp256k1_fe_sqr(&x9, &x9);
 40     }
 41     secp256k1_fe_mul(&x9, &x9, &x3);    // x9 a^511 0b111111111
 42 
 43     x11 = x9;
 44     for (j=0; j<2; j++) {               // 0b11111111100
 45         secp256k1_fe_sqr(&x11, &x11);
 46     }
 47     secp256k1_fe_mul(&x11, &x11, &x2);    // x11 a^2047 0b11111111111
 48 
 49     x22 = x11;
 50     for (j=0; j<11; j++) {              // 0b1111111111100000000000
 51         secp256k1_fe_sqr(&x22, &x22);
 52     }
 53     secp256k1_fe_mul(&x22, &x22, &x11); // x22 0b1111111111111111111111
 54 
 55     x44 = x22;
 56     for (j=0; j<22; j++) {              // 0b11111111111111111111110000000000000000000000
 57         secp256k1_fe_sqr(&x44, &x44);
 58     }
 59     secp256k1_fe_mul(&x44, &x44, &x22); // 0b11111111111111111111111111111111111111111111
 60 
 61     x88 = x44;
 62     for (j=0; j<44; j++) {
 63         secp256k1_fe_sqr(&x88, &x88);
 64     }
 65     secp256k1_fe_mul(&x88, &x88, &x44); // 0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
 66 
 67     x176 = x88;
 68     for (j=0; j<88; j++) {
 69         secp256k1_fe_sqr(&x176, &x176);
 70     }
 71     secp256k1_fe_mul(&x176, &x176, &x88);
 72 
 73     x220 = x176;
 74     for (j=0; j<44; j++) {
 75         secp256k1_fe_sqr(&x220, &x220);
 76     }
 77     secp256k1_fe_mul(&x220, &x220, &x44);
 78 
 79     x223 = x220;
 80     for (j=0; j<3; j++) {
 81         secp256k1_fe_sqr(&x223, &x223);
 82     }
 83     secp256k1_fe_mul(&x223, &x223, &x3);
 84 
 85     /* The final result is then assembled using a sliding window over the blocks. */
 86     t1 = x223;
 87     for (j=0; j<23; j++) {                    // 左移23bit,给01111111111111111111111留出位置
 88         secp256k1_fe_sqr(&t1, &t1);
 89     }
 90     secp256k1_fe_mul(&t1, &t1, &x22);
 91     for (j=0; j<6; j++) {                    // 000011 length is 6
 92         secp256k1_fe_sqr(&t1, &t1);
 93     }
 94     secp256k1_fe_mul(&t1, &t1, &x2);
 95     secp256k1_fe_sqr(&t1, &t1);                // for the last two 0
 96     secp256k1_fe_sqr(r, &t1);
 97 
 98     /* Check that a square root was actually calculated */
 99     secp256k1_fe_sqr(&t1, r);
100     return secp256k1_fe_equal(&t1, a);
101 }
secp256k1_fe_sqrt

如2~6行注释中做出的解释,(p+1)/4二进制共有3段连续的二进制1,算法正是围绕着该特点进行实现的。

第25行求x2=a2,26行求得x2=a3=a0b11,第28~29行求得x3=a7=a0b111,第31~35行求得x6=a63=a0b111111,之后类似,直到第83行求得x223=a0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff,至此(p+1)/4最高位部分连续223个二级制1部分的平方已经计算完毕,之后第86行~90行,首先通过平方操作实现“左移23位操作”,将接下来的22个连续二级制1部分内容乘到结果中(这里面包含一个0位)。然后第91行~94行,将最后2个连续1乘到结果内,之后95~96行实现最后“左移2位操作”。最后99~100行结合二次剩余理论,通过开方的平方是否和原a相等,判断a是否真的“可开方“。

3.5 secp256k1_fe_inv

 该函数的实现和secp256k1_fe_sqrt类似,不再进行详细分析,只给出完整的函数实现。

 1 /*
 2 * 在有限域中求数a的模逆,由欧拉公式可知a^fai(p) = 1 mod p,即a^(p-1) = 1 mod p
 3 * 所以a^(p-2)即为a的逆元,0b11111111111111111111111111111111111111111111111111111111111111111
 4 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
 5 1111111111111111111111111111111111111111111111111111111111011111111111111111111110000101101
 6 */
 7 static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
 8     secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
 9     int j;
10 
11     /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in
12      *  { 1, 2, 1, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
13      *  [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
14      */
15     secp256k1_fe_sqr(&x2, a);
16     secp256k1_fe_mul(&x2, &x2, a);
17 
18     secp256k1_fe_sqr(&x3, &x2);
19     secp256k1_fe_mul(&x3, &x3, a);
20 
21     x6 = x3;
22     for (j=0; j<3; j++) {
23         secp256k1_fe_sqr(&x6, &x6);
24     }
25     secp256k1_fe_mul(&x6, &x6, &x3);
26 
27     x9 = x6;
28     for (j=0; j<3; j++) {
29         secp256k1_fe_sqr(&x9, &x9);
30     }
31     secp256k1_fe_mul(&x9, &x9, &x3);
32 
33     x11 = x9;
34     for (j=0; j<2; j++) {
35         secp256k1_fe_sqr(&x11, &x11);
36     }
37     secp256k1_fe_mul(&x11, &x11, &x2);
38 
39     x22 = x11;
40     for (j=0; j<11; j++) {
41         secp256k1_fe_sqr(&x22, &x22);
42     }
43     secp256k1_fe_mul(&x22, &x22, &x11);
44 
45     x44 = x22;
46     for (j=0; j<22; j++) {
47         secp256k1_fe_sqr(&x44, &x44);
48     }
49     secp256k1_fe_mul(&x44, &x44, &x22);
50 
51     x88 = x44;
52     for (j=0; j<44; j++) {
53         secp256k1_fe_sqr(&x88, &x88);
54     }
55     secp256k1_fe_mul(&x88, &x88, &x44);
56 
57     x176 = x88;
58     for (j=0; j<88; j++) {
59         secp256k1_fe_sqr(&x176, &x176);
60     }
61     secp256k1_fe_mul(&x176, &x176, &x88);
62 
63     x220 = x176;
64     for (j=0; j<44; j++) {
65         secp256k1_fe_sqr(&x220, &x220);
66     }
67     secp256k1_fe_mul(&x220, &x220, &x44);
68 
69     x223 = x220;
70     for (j=0; j<3; j++) {
71         secp256k1_fe_sqr(&x223, &x223);
72     }
73     secp256k1_fe_mul(&x223, &x223, &x3);
74 
75     /* The final result is then assembled using a sliding window over the blocks. */
76     t1 = x223;
77     for (j=0; j<23; j++) {
78         secp256k1_fe_sqr(&t1, &t1);
79     }
80     secp256k1_fe_mul(&t1, &t1, &x22);
81     for (j=0; j<5; j++) {
82         secp256k1_fe_sqr(&t1, &t1);
83     }
84     secp256k1_fe_mul(&t1, &t1, a);
85     for (j=0; j<3; j++) {
86         secp256k1_fe_sqr(&t1, &t1);
87     }
88     secp256k1_fe_mul(&t1, &t1, &x2);
89     for (j=0; j<2; j++) {
90         secp256k1_fe_sqr(&t1, &t1);
91     }
92     secp256k1_fe_mul(r, a, &t1);
93 }
secp256k1_fe_inv

 

原创作者: zhaoweiwei 转载于: https://www.cnblogs.com/zhaoweiwei/p/18945891/secp256k1_2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值