2021SC@SDUSC
从本篇开始我将开始分析libsecp256k1中的代码。本开源库可以实现:
私钥经标量乘法生成公钥;
私钥签名(ECDSA);
公钥验证签名;
从签名消息中恢复公钥;
共享私钥(ECDH)
在这一篇中,我将对有限域和椭圆曲线的定义代码和椭圆曲线的几何加法和标量乘法运算进行分析。由于本人目前对这个项目整体认识不足,所以这一篇会随着我对后续代码细节理解的加深随时更改,后续的几篇会详细分析secp256k1开源库实现上述五个功能的代码段以及所用到的各种算法。
这个开源库使用rust语言编写,本人目前正在自学rust。目前阅读代码重点在于明白其中使用的各种算法和数据结构,理解这个开源库的各种功能,因此代码的分析不会强调语法的细枝末节。
有限域Galois Field
定义有限域的秩p以及模p运算
trait curve {
fn p() ->BigInt ;
}
struct Field<C: Curve >(BigInt);
impl<C: Curve> Field<C>{
fn new( v: BigInt ) -> Self{
Self( v.mod_floor (C::p()))
}
}
椭圆曲线Elliptic Curves
定义椭圆曲线的三个参数:
use num_bigint::BigInt;
pub trait Curve {
fn p() -> BigInt ;
fn a() -> BigInt ;
fn b() -> BigInt ;
}
具体到secp256k1椭圆曲线的三个参数值:
struct P256K1Curve;
impl Curve for P256K1Curve {
fn p() -> BigInt {
BigInt::from_str_radix("FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFEFFFFFC2F" ,16).unwrap()
}
fn a() -> BigInt { BigInt::zero() }
fn b() -> BigInt { BigInt::from(7u32) }
}
定义椭圆曲线上的点,要么是一个无穷远点,要么就具有x值和y值:
pub enum PointValue {
Infinity ,
Value {
x: BigInt ,
y: BigInt ,
}
}
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()
})
);
|