libsecp256k1比特币密码算法开源库
Posted yyDrifter
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libsecp256k1比特币密码算法开源库相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
libsecp256k1代码分析综述
从本篇开始我将开始分析libsecp256k1中的代码。本开源库可以实现:
私钥经标量乘法生成公钥;
私钥签名(ECDSA);
公钥验证签名;
从签名消息中恢复公钥;
共享私钥(ECDH)
在这一篇中,我将对有限域和椭圆曲线的定义代码和椭圆曲线的几何加法和标量乘法运算进行分析。由于本人目前对这个项目整体认识不足,所以这一篇会随着我对后续代码细节理解的加深随时更改,后续的几篇会详细分析secp256k1开源库实现上述五个功能的代码段以及所用到的各种算法。
这个开源库使用rust语言编写,本人目前正在自学rust。目前阅读代码重点在于明白其中使用的各种算法和数据结构,理解这个开源库的各种功能,因此代码的分析不会强调语法的细枝末节。
有限域Galois Field
定义有限域的秩p以及模p运算
trait curve {
fn p() ->BigInt ;
//这里的p就是有限域的秩p
}
struct Field<C: Curve >(BigInt);
impl<C: Curve> Field<C>{
//将一个元素放入到有限域中,要进行mod p运算
fn new( v: BigInt ) -> Self{
Self( v.mod_floor (C::p()))
}
}
椭圆曲线Elliptic Curves
定义椭圆曲线的三个参数:
//有限域椭圆曲线方程的三个参数a、b、p
use num_bigint::BigInt;
pub trait Curve {
fn p() -> BigInt ;
fn a() -> BigInt ;
fn b() -> BigInt ;
}
具体到secp256k1椭圆曲线的三个参数值:
//定义secp256k1的三个参数a、b、p的值
struct P256K1Curve;
impl Curve for P256K1Curve {
fn p() -> BigInt {
BigInt::from_str_radix("FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFEFFFFFC2F" ,16).unwrap()
}//定义p取值为以上256位二进制数
fn a() -> BigInt { BigInt::zero() }//定义a为0
fn b() -> BigInt { BigInt::from(7u32) }//定义b为7,u32表示无符号int基本整数类型
}
定义椭圆曲线上的点,要么是一个无穷远点,要么就具有x值和y值:
pub enum PointValue {
Infinity , //无穷远点
Value {
x: BigInt ,
y: BigInt ,
} //定义横纵坐标x和y
}
pub struct Point<C:Curve> {
pub value: PointValue,
_marker: PhantomData<C>,
}
判断任意一个点是不是在椭圆曲线上:
impl<C:Curve> Point<C>{
pub fn is_valid(&self) -> bool {
match self.value {
PointValue::Infinity => true, //如果是一个无穷远点,那么在椭圆曲线上
PointValue::Value { ref x, ref y } => {
(y * y - (x * x * x + &C::a() * x + &C::b()))
.mod_floor(&C::p()) == BigInt::zero() },//如果不是无穷远点,就将该点的横纵坐标代入有限域下椭圆曲线方程检验
}
}
}
椭圆曲线几何加法
进行椭圆曲线P和Q的几何加法主要分几种情况讨论:
P或Q中有一个是无穷远点;
P和Q是同一个点,横纵坐标一致;
P和Q是相反的点,即在图像上关于直线y=p/2对称;
P和Q是除上面三种情况外的一般点
如果其中一个P或者Q是无穷远点,将不是无穷远点的点的横纵坐标返回
impl<C: Curve> Add for Point<C> {
fn add (self, other: Point<C>) -> Point <C> {
let (ox, oy) = match other.value {
PointValue::Infinity => return self ,
PointValue::Value { ref x, ref y } => (x.clone(), y.clone()),};
let (sx, sy) = match self.value {
PointValue::Infinity => return other ,
PointValue::Value { ref x, ref y } => (x.clone(), y.clone()),};
......
}
}
P和Q横坐标一致时:
如果P和Q是相反的点,那么两点的纵坐标之和等于p,模p的结果为0,可以直接返回结果为无穷远点;
如果P和Q是相同的点,那么两点的纵坐标之和模p的结果一定不为0,就对自身(P或Q)取二倍。
impl<C: Curve> Add for Point <C> {
fn add(self, other: Point<C>) -> Point <C> {
...
if sx == ox {
return if (sy + oy ).mod_floor(&C::p()) == BigInt::zero() {
Point::infinity ()
}else {
self.double ()
}
}
...
}
}
如果P和Q是一般点
先求斜率l,P点纵坐标减Q点纵坐标再乘以P点横坐标减Q点横坐标模p的乘法逆元,最后结果再对p取模。即( ( yP - yQ) ( xP - xQ )^ -1 ) mod p
再分别求出R的横纵坐标
xR = ( m^2 - xP - xQ ) mod p
yR = ( m (xP - xR) - yP ) mod p
impl<C: Curve > Add for Point<C> {
type Output = Point <C>;
fn add(self, other: Point <C>) -> Point<C> {
...
let l = ((&oy - &sy) * utils::inverse_mod(&ox -&sx, C::p())).mod_floor (&C::p());
let x3 = (&l * &l - &sx - &ox).mod_floor ( &C::p());
let y3 = (&l *(&sx - &×3) - &sy ) .mod_floor ( &C::p());
Self::from(PointValue::Value { x: x3, y: y3 })
}
}
椭圆曲线标量乘法
对椭圆曲线上一点计算标量乘法,如果该点乘的系数n为0,那么返回结果直接就为无穷远点;否则让该点不断与自己做几何加法运算,直到n次结束。
impl<C: Curve> Mul<BigInt> for Point <C> {
fn mul(self, n: BigInt) -> Point<C> {
if n == 0 {
Self::infinity ()
}else {
let mut ret = self.clone();
for _ in 1 ... n {
ret = ret + self.clone ();
}
ret
}
}
}
在secp256k1中被计算标量乘法的(也就是椭圆曲线群的生成元)基准点G的横纵坐标。
let g = Point::<P256K1Curve>::from(PointValue::Value {
x: BigInt::from_str_radix(
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16).unwrap(),
y: BigInt::from_str_radix(
"483ADA7726A3C4655DA4FBFCOE1108A8FD17B448A68554199C47D08FFB1004B8",16).unwrap()
});
基准点G进行标量乘法(系数n=3,即三次几何加法运算)得到新的点:
assert.eq!(
g.clone() *BigInt::from(3u32),
Point::<P256K1Curve>::from(PointValue::Value {
x: BigInt::from_str_radix(
"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",16).unwrap(),
y: BigInt::from_str_radix(
"388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672",16).unwrap()
})
);
以上是关于libsecp256k1比特币密码算法开源库的主要内容,如果未能解决你的问题,请参考以下文章