secp256k1曲线Curve结构定义——域元素的运算与压缩存储

2021SC@SDUSC

secp256k1曲线Curve结构定义——域元素的运算与压缩存储


在libsecp256k1中对域元素进行了定义,其中 Field定义secp256k1的有限域 G F ( p ) GF(p) GF(p)元素,为了方便进行域下的模加法和模乘法运算,在libsecp256k1库中定义的Field元素是320位,并不是常见的256位。在进行相应的运算结束后会在 FieldStorage中实现紧凑的域元素存储,将Field中的元素压缩为256位,便于存储。

由于域元素都是长达256位的大整数,对大整数的存储采用数组来实现,在有的资料中也将这个过程称为序列化。在libsecp256k1中域元素(即椭圆曲线群元素点的横纵坐标)的序列化存储采用的都是大端序,即高位字节存入低地址,低位字节存入高地址

在libsecp256k1中对大整数的加法乘法运算进行了定义和实现。

Field域元素

Field元素是320位,也就是说使用了10个u32构成的数组表示一个域元素大整数,但实际上每个数组元素有效的只有26位,10*26=260也足以表示256位的域元素。在FIeld的每个数组元素中除了26位有效位外其余的部分都是0,下面的与或运算也使用到了这一点。域元素的高位存储在数组的高位,域元素的低位存储在数组的低位,因此对于一个大整数的实际值可以表示为: X = ∑ i = 0..9 n [ i ] ∗ 2 ( i ∗ 26 )   m o d   p X = \sum_{i=0..9} n[i]*2^{(i*26)}\,mod\,p X=i=0..9n[i]2(i26)modp,在代码中数据的存储和表示都采用16进制,用0x表示十六进制的前缀,所以上面的 p = 2 256 − 0 x 1000003 D 1 p = 2^{256} - 0x1000003D1 p=22560x1000003D1
下面是定义的Field域元素结构体:

pub struct Field {
    n: [u32; 10],
    magnitude: u32,
    normalized: bool,
}

每个Field元素320位,用10个u32数组元素表示。

impl Field {
    pub const fn new_raw(
        d9: u32,
        d8: u32,
        d7: u32,
        d6: u32,
        d5: u32,
        d4: u32,
        d3: u32,
        d2: u32,
        d1: u32,
        d0: u32,
    ) -> Self {
        Self {
            n: [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9],
            magnitude: 1,
            normalized: false,
        }
    }

由于数组中存放的就是u32类型的变量,可以实现加法,那么域元素加法实现就将对应数组index相加即可。但这里很容易就会想到一个问题,那就是进位。因为要进1,会导致本来26位的有效位这么一加可能有几个数组元素的有效位就成了27位,如果把这样的结果直接压缩存储的话肯定是不行的。在Field结构体中有个boolean型变量normalized,如果normalized为true,表示域元素的所有数组元素都是26位有效位,如果为false,就代表需要进行一定的规范化处理,让他变成正常的数组元素以便压缩。

impl<'a> AddAssign<&'a Field> for Field {
    fn add_assign(&mut self, other: &'a Field) {
        self.n[0] += other.n[0];
        self.n[1] += other.n[1];
        self.n[2] += other.n[2];
        self.n[3] += other.n[3];
        self.n[4] += other.n[4];
        self.n[5] += other.n[5];
        self.n[6] += other.n[6];
        self.n[7] += other.n[7];
        self.n[8] += other.n[8];
        self.n[9] += other.n[9];

        self.magnitude += other.magnitude;
        self.normalized = false;//normalized为false,表示需要规范化处理
        debug_assert!(self.verify());
    }
}

前面提到需要对数组元素进行规范化处理,让每个数组元素的有效位都是26位。规范化处理的代码很长,于是我把很显然的就像排比句的部分打个省略号,重点关注算法实现部分。

pub fn normalize(&mut self) {
        let mut t0 = self.n[0];
        let mut t1 = self.n[1];
        ......
        let mut t9 = self.n[9];

        let mut m: u32;
        let mut x = t9 >> 22;
        t9 &= 0x03fffff;

        t0 += x * 0x3d1;
        t1 += x << 6;
        t1 += t0 >> 26;
        t0 &= 0x3ffffff;
        t2 += t1 >> 26;
        t1 &= 0x3ffffff;
        t3 += t2 >> 26;
        t2 &= 0x3ffffff;
        m = t2;
        t4 += t3 >> 26;
        t3 &= 0x3ffffff;
        m &= t3;
        t5 += t4 >> 26;
        t4 &= 0x3ffffff;
        m &= t4;
        t6 += t5 >> 26;
        t5 &= 0x3ffffff;
        m &= t5;
        t7 += t6 >> 26;
        t6 &= 0x3ffffff;
        m &= t6;
        t8 += t7 >> 26;
        t7 &= 0x3ffffff;
        m &= t7;
        t9 += t8 >> 26;
        t8 &= 0x3ffffff;
        m &= t8;

        debug_assert!(t9 >> 23 == 0);

        x = (t9 >> 22)
            | (if t9 == 0x03fffff { 1 } else { 0 }
                & if m == 0x3ffffff { 1 } else { 0 }
                & (if (t1 + 0x40 + ((t0 + 0x3d1) >> 26)) > 0x3ffffff {
                    1
                } else {
                    0
                }));

        t0 += x * 0x3d1;
        t1 += x << 6;
        t1 += t0 >> 26;
        t0 &= 0x3ffffff;
        t2 += t1 >> 26;
        t1 &= 0x3ffffff;
        t3 += t2 >> 26;
        t2 &= 0x3ffffff;
        ......
        t9 += t8 >> 26;
        t8 &= 0x3ffffff;

        debug_assert!(t9 >> 22 == x);

        t9 &= 0x03fffff;

        self.n = [t0, t1, t2, t3, t4, t5, t6, t7, t8, t9];
        self.magnitude = 1;
        self.normalized = true;
        debug_assert!(self.verify());
    }

域元素乘法实现

impl<'a> MulAssign<&'a Field> for Field {
    fn mul_assign(&mut self, other: &'a Field) {
        let mut ret = Field::default();
        ret.mul_in_place(self, other);
        *self = ret;
    }
}

FieldStorage域元素紧凑存储

在libsecp256k1中对每个大数以数组的方式存储,每个数组元素为u32即32位比特,在FieldStorage中,每个Field元素为256位比特,用8个u32就可以表示。

impl FieldStorage {
    pub const fn new(
        d7: u32,
        d6: u32,
        d5: u32,
        d4: u32,
        d3: u32,
        d2: u32,
        d1: u32,
        d0: u32,
    ) -> Self {
        Self([d0, d1, d2, d3, d4, d5, d6, d7])
    }

下面的代码实现了将320位的域元素压缩为256位存储的过程,首先介绍一下rust语言的语法:
|表示位或,相同位只要有一个是1则返回1,否则返回0;
左移 <<操作数中的所有位向左移动指定位数,右边的位补0;
右移 >>操作数中的所有位向右移动指定位数,左边的位补0。
有了上面的语法想要看懂压缩过程就很容易了只需把对应数组元素0-1比特串做对应运算即可。可以看到赋值号=右侧有10个数组元素,到了赋值号左侧就只有8个,实现了将320位的域元素压缩为256位的存储过程。

想要更好地理解这段代码,应该明白压缩的目的是什么,前面提到在Field中每个数组元素是u32,但只有26位是有效位,其余部分都是无效位,这就使得Field域元素表示成了320位,现在想要只用8个u32的数组元素存储这256位,也就是说不能再有无效位了,也就是说需要把无效位给压缩没,举下面代码一个例子,压缩后的0号数组元素其实就是把压缩前的0号元素的26位和压缩前的1号元素的6位给拼在一起;压缩后的1号数组元素其实就是先将压缩前的1号元素右移6位剩下的20位有效位再和压缩前的2号元素的12位给拼在一起……这也就是下面的8行实现压缩过程的代码所表示的意思。

impl Into<FieldStorage> for Field {
    fn into(self) -> FieldStorage {
        debug_assert!(self.normalized);
        let mut r = FieldStorage::default();

        r.0[0] = self.n[0] | self.n[1] << 26;
        r.0[1] = self.n[1] >> 6 | self.n[2] << 20;
        r.0[2] = self.n[2] >> 12 | self.n[3] << 14;
        r.0[3] = self.n[3] >> 18 | self.n[4] << 8;
        r.0[4] = self.n[4] >> 24 | self.n[5] << 2 | self.n[6] << 28;
        r.0[5] = self.n[6] >> 4 | self.n[7] << 22;
        r.0[6] = self.n[7] >> 10 | self.n[8] << 16;
        r.0[7] = self.n[8] >> 16 | self.n[9] << 10;

        r
    }
}

既然可以将Field元素压缩存储,那么必然需要将压缩的结果恢复出来,恢复的过程如下代码所示,还是一样的0-1比特串运算,其中&符号表示位与,即相同位都是1则返回1,否则返回0,这里都是和3FFFFFF位与,即保留后26位。

解压缩就是压缩的逆过程,有了上面压缩过程的解释,解压缩也就非常好理解,只需每个数组元素保留26位,其余6位还是无效位即可。

impl From<FieldStorage> for Field {
    fn from(a: FieldStorage) -> Field {
        let mut r = Field::default();

        r.n[0] = a.0[0] & 0x3FFFFFF;
        r.n[1] = a.0[0] >> 26 | ((a.0[1] << 6) & 0x3FFFFFF);
        r.n[2] = a.0[1] >> 20 | ((a.0[2] << 12) & 0x3FFFFFF);
        r.n[3] = a.0[2] >> 14 | ((a.0[3] << 18) & 0x3FFFFFF);
        r.n[4] = a.0[3] >> 8 | ((a.0[4] << 24) & 0x3FFFFFF);
        r.n[5] = (a.0[4] >> 2) & 0x3FFFFFF;
        r.n[6] = a.0[4] >> 28 | ((a.0[5] << 4) & 0x3FFFFFF);
        r.n[7] = a.0[5] >> 22 | ((a.0[6] << 10) & 0x3FFFFFF);
        r.n[8] = a.0[6] >> 16 | ((a.0[7] << 16) & 0x3FFFFFF);
        r.n[9] = a.0[7] >> 10;

        r.magnitude = 1;
        r.normalized = true;

        r
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值