椭圆曲线
y^2 = (x^3 + a * x + b) mod p
上述的曲线是在整数,一定bit数量(假设是160bit)内可以表达的,p是 160bits内可以表示的大整数。
为什么是这种曲线定义呢,我个人觉得有3个原因:
- 在这个曲线上加法是可定义的。
- 在这个曲线上的加法求解很简单。
- 在这个曲线上可以证明 对数运算是极复杂问题。
标量加法/乘法定义
在椭圆曲线上随意取两个点 A,B,连接A,B两个点的直线与曲线的新的交点称为C,C关于X对称的-C点表示为 A+B。
乘法可以用加法表示,但是算法复杂堵是 log(n)。
其实为什么选择上述的椭圆曲线实际上是由这个加法规则选出来的,总是要求解这个新的交点更加简单。
Trap door fuction(陷门函数)
R=k*P
的性质,已知R与P的值,无法推出k的值, 而知道k值于P值是很容易计算R值(log(n))。这是ECDSA签名算法的理论基础。
如何设计签名算法
如何利用这个门限函数来做一个签名算法呢?
一个顺理成章的想法是: 给验证人一个 R和P,那只有签名人才能提供K,那K是不是可以作为一个签名指纹呢?
ok,咱先生成一个公私钥对,Qa = dA * G
, Qa
就是公钥,dA
是一个随机数,就当是一个私钥吧,当然这个椭圆曲线的 a
, b
, p
, G
值都是要公开的。这个G
就是椭圆曲线上随机挑选的一个节点,我们叫原点,也是要公开的。
每次对一个数据做签名,都随机生成一个随机数 K
吧,P = K * G
。咱们把 P的x轴坐标R作为签名的一部分。另一部分签名数据叫 S
。
显然这个S
既要和 dA
相关,也要和被签名的信息z
相关。我们就假定:
S = Sig(dA, G, z, R, K)
, 那这个函数就是签名函数。
而验证函数应该只有 S,R
,而不会有K,dA
P = h(S, G, z, R)
, h
就是签名函数。
密码学专家想到了一个实现:
S = k^-1 (z + dA * R) mod p
k^-1
表示的是关于p的模逆元。( k^-1 * k mod p = 1)。
可以推倒初验证函数:
==> k = S^-1 (z + dA * R) mod p
==> k*G = S^-1 (z + dA * R) *G
==> P = S^-1*z*G + S^-1*dA*R*G
==> P = S^-1*z*G + S^-1*R*Qa
对于验证者来说,需要计算: S^-1*z*G + S^-1*R*Qa
的x坐标是否就是 R
。
实现
go的椭圆曲线 a
为固定的-3, 而b
可以自由的选择,而p224, p256曲线的其实指的是 曲线的bit位数是224和256。
算法实现着为了更加快速地执行曲线上的加法和乘法,对笛卡尔坐标做了投射成(x,y,z)的雅各布坐标,投射关系:
x'=x * (Z^-2)
y'=y * (Z^-3)
可以加速在雅各布坐标系上的运算,在需要最终结果的时候再投射回笛卡尔坐标系。
一个曲线的表示:
type CurveParams struct {
P *big.Int // the order of the underlying field
N *big.Int // the order of the base point
B *big.Int // the constant of the curve equation
Gx, Gy *big.Int // (x,y) of the base point
BitSize int // the size of the underlying field
Name string // the canonical name of the curve
}
公私钥对的表示:
// PublicKey represents an ECDSA public key.
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
// PrivateKey represents an ECDSA private key.
type PrivateKey struct {
PublicKey
D *big.Int
}
可以看出曲线,公钥,私钥拥有的信息越来越全。
公钥是 曲线信息+ dA*G的信息。
私钥是 公钥信息 + dA信息。
secp256k1
美国国安局发布的曲线其实是 secp256k1
, 对于其参数其实没有很好的解释,大家猜测是国安局找到了这是一条弱化的椭圆曲线,因此BTC在设计之初也没有采用这个曲线,而是用了Koblitz Curve
, b
是7,a
是0,btcsuit的package中,对KoblitzCurve
是golang的 curvparam
的一层封装(主要是为了加速),但是有更多的参数, 是因为a和b等参数都是预先设定的,增加更多的参数来加速计算。至于公私方法和golang基本一样。
ED25519
Ed25519 从某种意义上来说也属于椭圆曲线密码学,不同的是它采用扭曲爱德华兹曲线作为椭圆曲线,同时采用的签名机制也不同于 ECDSA 算法。EdDSA 的重要实现 ED25519 是 Daniel J. Bernstein 等人设计的 EdDSA 算法,采用的曲线参数完全公开,并说明了参数选取的意义,保证曲线中并未内置后门。
曲线:
y^2 = (x^3 + x^2 + {一个很大的随机数}x ) mod p
他生成公私钥的流程有一些区别,seed作为私钥,seed计算出的hash值去和G相乘得到公钥,为了防止多次计算,把这部分公钥扩展到私钥的后面作为一个完整的私钥。计算签名的时候,普通ecdsa会生成一个随机数K,而ec25519为了避免伪随机数被猜测到,因此是私钥和msg的hash值作为这个随机数。