【源码阅读】用Go语言实现环签名的签名和验证(一)

环签名是区块链中保护用户隐私的方法之一,今天起更新环签名的实现,顺带讲一下原理。源码是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
}

环签名结构体,变量名没有特别意思,后面再讲。

你可能感兴趣的:(区块链,数论密码学,数字签名)