libsecp256k1比特币密码算法开源库
Posted yyDrifter
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libsecp256k1比特币密码算法开源库相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
secp256k1曲线Curve结构定义
本篇将介绍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(
//开头的0x表示使用16进制表示
Field::new(
0x79BE667E, 0xF9DCBBAC, 0x55A06295, 0xCE870B07, 0x029BFCDB, 0x2DCE28D9, 0x59F2815B, 0x16F81798,
),//G点的横坐标
Field::new(
0x483ADA77, 0x26A3C465, 0x5DA4FBFC, 0x0E1108A8, 0xFD17B448, 0xA6855419, 0x9C47D08F, 0xFB10D4B8,
),//G点的纵坐标
);
CURVE_B定义了secp256k1方程参数b(b为常量=7)。
pub const CURVE_B: u32 = 7;
//u32即为32比特(可以看做int)
//在Rust语言中,声明变量默认是不可修改的
//如果是可修改的变量要在变量名前加关键字mut
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();//令y2为对y求平方
let mut x3 = self.x.sqr();
x3 *= &self.x;//x3为对x求立方
let mut c = Field::default();
c.set_int(CURVE_B);//c取secp256k1参数b取值,其中b=7
x3 += &c;//结果x3即为x^3+7,椭圆曲线方程右端
x3.normalize_weak();
y2.eq_var(&x3)//判断y2与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);//z值必定为1
}
4.比较Jacobian坐标下群元素的X坐标:
pub fn eq_x_var(&self, x: &Field) -> bool {
debug_assert!(!self.is_infinity());
//这里 debug_assert! 宏是只在未开启优化的编译包中才有效
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);//neg为相反数指令
}
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=4x1y12λ3=8y14x3=λ12−2λ2y3=λ1(λ2−x3)−λ3z3=2y1z1下面的代码将会实现上面的运算过程,由于代码实现和数学公式的表达有点不同,而且很多变量取名也会不一致,所以代码和数学公式的详细对应关系我在相应位置给出了注释,代码中 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);
//求得self.z为2yz,即a+a得到的点的z坐标,即self.z对应上面公式中的z3
let mut t1 = a.x.sqr();
t1.mul_int(3);//t1为3x^2,即t1对应上面公式中的λ1
let mut t2 = t1.sqr();//t2为λ1的平方
let mut t3 = a.y.sqr();
t3.mul_int(2);//t3为2倍的y^2
let mut t4 = t3.sqr();//此时t4为4倍的y^4
t4.mul_int(2);//t4为8倍的y^4,则t4对应上面公式中的λ3
t3 *= &a.x;//此时t3为2y^2乘以x
self.x = t3;//令self.x等于2y^2乘以x
self.x.mul_int(4);//此时self.x等于8y^2乘以x,即2倍的λ2
self.x = self.x.neg(4);//此时self.x等于8y^2乘以x的相反数,即-2倍的λ2
self.x += &t2;
//此时self.x等于t2加上-2倍的λ2,由t2为λ1的平方,则self.x对应上面公式中的x3
t2 = t2.neg(1);//对t2求相反数,即为负的λ1的平方
t3.mul_int(6);//此时t3为12倍y^2乘以x
t3 += &t2;//此时t3为12倍y^2乘以x,再减λ1的平方
self.y = t1 * t3;//此时self.y对应上面公式中的λ1(λ2 −x3)
t2 = t4.neg(2);//此时t2为−λ3
self.y += t2;
//此时self.y为λ1(λ2 −x3)加上t2,得到的结果即为上面公式中的y3
}
调用上面的函数,计算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
以上是关于libsecp256k1比特币密码算法开源库的主要内容,如果未能解决你的问题,请参考以下文章