2021SC@SDUSC
本篇将介绍libsecp256k1中如何定义Affine坐标和Jacobian加重射影坐标,并实现其相应的函数。有关Affine坐标和Jacobian加重射影坐标的相关底层数学知识我在博客:椭圆曲线——从仿射坐标到雅可比坐标的转换中写的比较详细了,有需要可以去了解一下。
由于本开源库用Rust语言编写,开始前先说一下Rust提供的模块系统,自顶向下依次为: 包(package):一个用于构建、测试并共享crate的Cargo功能。 单元包(crate):一个用于生成library(库)或可执行文件的树形模块结构。 模块(module)、use:控制代码结构、作用域及路径的私有路径。 路径(path):一种用于命名条目的方法,这些条目包括struct、function和module等。
package与crate 一个package由一个或多个crate组成,一个package带有一个Cargo.toml描述构筑crate的信息。crate可以被用作生成二进制程序或库。 package可以包含:最多一个库crate(lib);package可以拥有任意多个二进制包;package内至少要有一个crate(库crate或二进制crate)。
Curve曲线结构
下面定义了曲线curve模块结构,它使用了如下的crate:
pub mod curve {
pub use crate::{
field::{Field, FieldStorage},
group::{Affine, AffineStorage, Jacobian, AFFINE_G, CURVE_B},
scalar::Scalar,
};
pub use crate::ecmult::{ECMultContext, ECMultGenContext};
}
其中, Field定义secp256k1的有限域
G
F
(
p
)
GF(p)
GF(p)元素。 FieldStorage实现紧凑的域元素存储。在libsecp256k1库中 定义了Field元素为320位,但Field可以被压缩成 FieldStorage, 也就是常见的256位,便于存储。
Affine,在仿射坐标中定义secp256k1曲线的一组元素。 AffineStorage实现仿射坐标群元紧凑存储。 Jacobian,在雅可比射影坐标中定义secp256k1曲线的一组元素。
AFFINE_G定义了secp256k1在仿射坐标中的生成元G点的横纵坐标,G点的横纵坐标我在libsecp256k1比特币密码算法开源库(五)中曾经给出过。可以看到每个逗号隔开8位16进制数,每个16进制数可用4位比特表示,也就是说每个逗号隔开32位比特,和一个int大小一样。
pub static AFFINE_G: Affine = Affine::new(
Field::new(
0x79BE667E, 0xF9DCBBAC, 0x55A06295, 0xCE870B07, 0x029BFCDB, 0x2DCE28D9, 0x59F2815B, 0x16F81798,
),
Field::new(
0x483ADA77, 0x26A3C465, 0x5DA4FBFC, 0x0E1108A8, 0xFD17B448, 0xA6855419, 0x9C47D08F, 0xFB10D4B8,
),
);
CURVE_B定义了secp256k1方程参数b(b为常量=7)。
pub const CURVE_B: u32 = 7;
Scalar为256位标量值。secp256k1中多处用到标量scalar,其中私钥和数字签名中使用的随机数k都是标量。
ECMultContext 加速aP + bG计算的上下文。 ECMultGenContext 加速aG计算的上下文。
由于内容比较多,本篇主要介绍Affine和Jacobian结构定义和相关实现。在代码中会有一些调用的函数实现不明确,下一篇会介绍相应的函数实现以及curve结构中剩余的部分。为了让数学公式结合代码更加清晰,我在代码片段中写了详尽的注释,如有需要可留意。
Affine 仿射坐标
定义仿射坐标下点的结构体,其中x和y值都是Field型变量,还有一个bool型变量infinty表示无穷远点。
pub struct Affine {
pub x: Field,
pub y: Field,
pub infinity: bool,
}
相关函数实现: 1.创建一个仿射坐标中的点。Self代表的是Affine类型的点,对于类型名字很长的时候或者频繁变更的时候,这样可以很省事。
pub const fn new(x: Field, y: Field) -> Self {
Self {
x,
y,
infinity: false,
}
}
2.对给定的仿射坐标的x和y值,找到一个在椭圆曲线群中的点:
pub fn set_xy(&mut self, x: &Field, y: &Field) {
self.infinity = false;
self.x = *x;
self.y = *y;
}
3.检查椭圆曲线群中的点是否满足椭圆曲线方程:
pub fn is_valid_var(&self) -> bool {
if self.is_infinity() {
return false;
}
let y2 = self.y.sqr();
let mut x3 = self.x.sqr();
x3 *= &self.x;
let mut c = Field::default();
c.set_int(CURVE_B);
x3 += &c;
x3.normalize_weak();
y2.eq_var(&x3)
}
4.对给定的Jacobian坐标下的点坐标,找到其在仿射坐标中的对应点:
pub fn set_gej(&mut self, a: &Jacobian) {
self.infinity = a.infinity;
let mut a = *a;
a.z = a.z.inv();
let z2 = a.z.sqr();
let z3 = a.z * z2;
a.x *= z2;
a.y *= z3;
a.z.set_int(1);
self.x = a.x;
self.y = a.y;
}
5.清除敏感数据。这里的清除敏感数据体现Rust这个编程语言在设计密码库的一大优势,即Rust对底层的控制能力很好,Rust可以提供对底层内存的直接控制,比如在使用完私钥等机密信息之后可以及时从内存空间完全释放掉。
pub fn clear(&mut self) {
self.infinity = false;
self.x.clear();
self.y.clear();
}
Jacobian加重射影坐标
定义Jacobian坐标下点的结构体,其中x、y和z值都是Field型变量,还有一个bool型变量infinty表示无穷远点(前面说过,在Jacobian坐标中该点即为z=0的点)。
pub struct Jacobian {
pub x: Field,
pub y: Field,
pub z: Field,
pub infinity: bool,
}
1.创建一个Jacobian坐标中的点。z=1的原因即为仿射坐标中的点
(
x
,
y
)
( x , y )
(x,y)对应Jacobian加重射影坐标中的点
(
x
,
y
,
1
)
( x , y , 1 )
(x,y,1)
pub const fn new(x: Field, y: Field) -> Self {
Self {
x,
y,
infinity: false,
z: Field::new(0, 0, 0, 0, 0, 0, 0, 1),
}
}
2.设置一个等于无穷远点的用Jacobian坐标表示的群元素:
pub fn set_infinity(&mut self) {
self.infinity = true;
self.x.clear();
self.y.clear();
self.z.clear();
}
3.对给定的仿射坐标点,找到对应的Jacobian坐标下的点:
pub fn set_ge(&mut self, a: &Affine) {
self.infinity = a.infinity;
self.x = a.x;
self.y = a.y;
self.z.set_int(1);
}
4.比较Jacobian坐标下群元素的X坐标:
pub fn eq_x_var(&self, x: &Field) -> bool {
debug_assert!(!self.is_infinity());
let mut r = self.z.sqr();
r *= x;
let mut r2 = self.x;
r2.normalize_weak();
r.eq_var(&r2)
}
5.求a的逆。由于a是一个点,在椭圆曲线构成的群中定义单位元为无穷远点,运算为几何加法运算,则a的逆元实际上就是点a关于坐标轴的对称点。
pub fn neg_in_place(&mut self, a: &Jacobian) {
self.infinity = a.infinity;
self.x = a.x;
self.y = a.y;
self.z = a.z;
self.y.normalize_weak();
self.y = self.y.neg(1);
}
6.检查群元素是否为无穷远处的点。
pub fn is_infinity(&self) -> bool {
self.infinity
}
7.检查群元素的y坐标是否为二次剩余。
pub fn has_quad_y_var(&self) -> bool {
if self.infinity {
return false;
}
let yz = self.y * self.z;
yz.is_quad_var()
}
8.在Jacobian坐标下求2a,即a+a(这里的a是Jacobian坐标下的一个点)。在上一篇中介绍,a=0时,Jacobian加重射影坐标运算过程如下:
λ
1
=
3
x
1
2
λ
2
=
4
x
1
y
1
2
λ
3
=
8
y
1
4
x
3
=
λ
1
2
?
2
λ
2
y
3
=
λ
1
(
λ
2
?
x
3
)
?
λ
3
z
3
=
2
y
1
z
1
λ_1 = 3x_1^2\\λ_2 = 4x_1y^2_1\\λ_3 = 8y^4_1\\x_3 = λ^2_1 ?2λ_2\\y_3 = λ_1(λ_2 ?x_3)?λ_3\\z_3 = 2y_1z_1
λ1?=3x12?λ2?=4x1?y12?λ3?=8y14?x3?=λ12??2λ2?y3?=λ1?(λ2??x3?)?λ3?z3?=2y1?z1?下面的代码将会实现上面的运算过程,由于代码实现和数学公式的表达有点不同,而且很多变量取名也会不一致,所以代码和数学公式的详细对应关系我在相应位置给出了注释,代码中 self.x, self.y, self.z的运算结果即为对应的
x
3
,
y
3
,
z
3
x_3,y_3,z_3
x3?,y3?,z3?。
pub fn double_var_in_place(&mut self, a: &Jacobian, rzr: Option<&mut Field>) {
self.infinity = a.infinity;
if self.infinity {
if let Some(rzr) = rzr {
rzr.set_int(1);
}
return;
}
if let Some(rzr) = rzr {
*rzr = a.y;
rzr.normalize_weak();
rzr.mul_int(2);
}
self.z = a.z * a.y;
self.z.mul_int(2);
let mut t1 = a.x.sqr();
t1.mul_int(3);
let mut t2 = t1.sqr();
let mut t3 = a.y.sqr();
t3.mul_int(2);
let mut t4 = t3.sqr();
t4.mul_int(2);
t3 *= &a.x;
self.x = t3;
self.x.mul_int(4);
self.x = self.x.neg(4);
self.x += &t2;
t2 = t2.neg(1);
t3.mul_int(6);
t3 += &t2;
self.y = t1 * t3;
t2 = t4.neg(2);
self.y += t2;
}
调用上面的函数,计算2a。
pub fn double_var(&self, rzr: Option<&mut Field>) -> Jacobian {
let mut ret = Jacobian::default();
ret.double_var_in_place(&self, rzr);
ret
}
9.在Jacobian坐标下求a+b(这里的a和b是Jacobian坐标下的一个点)。Jacobian加重射影坐标下的几何加法运算过程如下:
λ
1
=
x
1
z
2
2
λ
2
=
x
2
z
1
2
λ
3
=
λ
1
?
λ
2
λ
4
=
y
1
z
2
3
λ
5
=
y
2
z
1
3
λ
6
=
λ
4
?
λ
5
λ
7
=
λ
1
+
λ
2
λ
8
=
λ
4
+
λ
5
x
3
=
λ
6
2
?
λ
7
λ
3
2
λ
9
=
λ
7
λ
3
2
?
2
x
3
y
3
=
(
λ
9
λ
6
?
λ
8
λ
3
3
)
/
2
z
3
=
z
1
z
2
λ
3
λ_1=x_1z_2^2\\λ_2=x_2z_1^2\\λ_3 = λ_1 ?λ_2\\λ_4 = y_1z^3_2\\λ_5 = y_2z^3_1\\λ_6 = λ_4 ?λ_5\\λ_7 = λ_1 +λ_2\\ λ_8 = λ_4 +λ_5\\x_3 = λ^2_6 ?λ_7λ^2_3\\λ_9 = λ_7λ^2_3 ?2x_3\\y_3 = (λ_9λ_6 ?λ_8λ_3^3)/2\\z_3 = z_1z_2λ_3
λ1?=x1?z22?λ2?=x2?z12?λ3?=λ1??λ2?λ4?=y1?z23?λ5?=y2?z13?λ6?=λ4??λ5?λ7?=λ1?+λ2?λ8?=λ4?+λ5?x3?=λ62??λ7?λ32?λ9?=λ7?λ32??2x3?y3?=(λ9?λ6??λ8?λ33?)/2z3?=z1?z2?λ3?下面的代码将会实现上面的运算过程,代码和数学公式的对应关系我在相应位置给出了注释,代码中 self.x, self.y, self.z的运算结果即为对应的
x
3
,
y
3
,
z
3
x_3,y_3,z_3
x3?,y3?,z3?。
pub fn add_var_in_place(&mut self, a: &Jacobian, b: &Jacobian, rzr: Option<&mut Field>) {
if a.is_infinity() {
debug_assert!(rzr.is_none());
*self = *b;
return;
}
if b.is_infinity() {
if let Some(rzr) = rzr {
rzr.set_int(1);
}
*self = *a;
return;
}
self.infinity = false;
let z22 = b.z.sqr();
let z12 = a.z.sqr();
let u1 = a.x * z22;
let u2 = b.x * z12;
let mut s1 = a.y * z22;
s1 *= b.z;
let mut s2 = b.y * z12;
s2 *= a.z;
let mut h = u1.neg(1);
h += u2;
let mut i = s1.neg(1);
i += s2;
if h.normalizes_to_zero_var() {
if i.normalizes_to_zero_var() {
self.double_var_in_place(a, rzr);
} else {
if let Some(rzr) = rzr {
rzr.set_int(0);
}
self.infinity = true;
}
return;
}
let i2 = i.sqr();
let h2 = h.sqr();
let mut h3 = h * h2;
h *= b.z;
if let Some(rzr) = rzr {
*rzr = h;
}
self.z = a.z * h;
let t = u1 * h2;
self.x = t;
self.x.mul_int(2);
self.x += h3;
self.x = self.x.neg(3);
self.x += i2;
self.y = self.x.neg(5);
self.y += t;
self.y *= i;
h3 *= s1;
h3 = h3.neg(1);
self.y += h3;
}
调用上面的函数,计算a+b。
pub fn add_var(&self, b: &Jacobian, rzr: Option<&mut Field>) -> Jacobian {
let mut ret = Jacobian::default();
ret.add_var_in_place(self, b, rzr);
ret
}
10.清除敏感数据,防止敏感信息泄露。
pub fn clear(&mut self) {
self.infinity = false;
self.x.clear();
self.y.clear();
self.z.clear();
}
|