环签名是区块链中保护用户隐私的方法之一,今天起更新环签名的实现,顺带讲一下原理。源码是MIT的开源代码,用go语言编写,由于我也是go语言小白,所以一边读一边学语法。
import (
"bytes"
"crypto/elliptic"
"crypto/sha256"
"fmt"
"io"
"math/big"
"sync"
)
一些包,bytes是二进制处理数组(对于哈希函数就很有效了),crypto是密码学库,这里我们用的是椭圆曲线密码学,又用到了SHA256哈希函数。math/big是大整数包,因为密码通常都是很长的,超出了整数范围。sync是并发,具体的后面解释。
首先我们来看看源码库elliptic.curve的内容
type Curve interface {
// Params returns the parameters for the curve.
Params() *CurveParams
// IsOnCurve reports whether the given (x,y) lies on the curve.
IsOnCurve(x, y *big.Int) bool
// Add returns the sum of (x1,y1) and (x2,y2)
Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int)
// Double returns 2*(x,y)
Double(x1, y1 *big.Int) (x, y *big.Int)
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
// ScalarBaseMult returns k*G, where G is the base point of the group
// and k is an integer in big-endian form.
ScalarBaseMult(k []byte) (x, y *big.Int)
}
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
}
注释很详细,解释一下,先看曲线参数,P是椭圆曲线的阶,N是基点的阶,它一定是P的一个质因子,GX,GY是基点坐标,Bitsize是基点阶的字节大小。
再看曲线函数,Add是曲线上点的加法,几何上为连线求另一点,公式可以用韦达定理推导,需要注意,我们用椭圆曲线代入到离散对数时,点的加法就是数的乘法,椭圆曲线密码的安全性高就是因为没有多项式复杂度方法求倍数。
Double函数是自加,几何上为切线求另一交点。
ScalarMult是点的数乘,注意就是离散对数里面的幂。
ScalarBaseMult是基点的数乘,供方便用。
下面看密钥结构体:
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
type PrivateKey struct {
PublicKey
D *big.Int
}
定义公钥和私钥,公钥里的X,Y就是公钥坐标,公钥的产生公式为 y=gx y = g x ,其中x是私钥,也就是PrivateKey结构体中的D。
type PublicKeyRing struct {
Ring []PublicKey
}
定义环,环就是公钥集合。
func NewPublicKeyRing(cap uint) *PublicKeyRing {
return &PublicKeyRing{make([]PublicKey, 0, cap)}
}
生成指定长度的环,0表示初始元素个数为0,预留cap个元素的空间。make是开数组的函数。
func (r *PublicKeyRing) Add(pub PublicKey) {
r.Ring = append(r.Ring, pub)
}
func (r *PublicKeyRing) Len() int {
return len(r.Ring)
}
func (r *PublicKeyRing) Bytes() (b []byte) {
for _, pub := range r.Ring {
b = append(b, pub.X.Bytes()...)
b = append(b, pub.Y.Bytes()...)
}
return
}
var one = new(big.Int).SetInt64(1)
一些常用操作,append是追加元素,range是遍历关键词。
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(rand, b)
if err != nil {
return
}
k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}
随机数产生,下半段是避免出现0的处理,因为椭圆曲线的子群是循环群,所以出现零点是可能的。
func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) {
k, err := randFieldElement(c, rand)
if err != nil {
return
}
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return
}
产生私钥、公钥。注意,是先有私钥,后有公钥,私钥仅仅是基点的那个幂指数,其他都是公钥。 err是go语言的异常处理关键字。
type RingSign struct {
X, Y *big.Int
C, T []*big.Int
}
环签名结构体,变量名没有特别意思,后面再讲。