github.com/consensys/gnark-crypto@v0.14.0/internal/generator/edwards/eddsa/template/eddsa.go.tmpl (about)

     1  import (
     2  	"crypto/subtle"
     3  	"errors"
     4  	"hash"
     5  	"io"
     6  	"math/big"
     7  
     8  	"github.com/consensys/gnark-crypto/signature"
     9  	"github.com/consensys/gnark-crypto/ecc/{{.Name}}/twistededwards"
    10  	"github.com/consensys/gnark-crypto/ecc/{{.Name}}/fr"
    11  	"golang.org/x/crypto/blake2b"
    12  )
    13  
    14  var errNotOnCurve = errors.New("point not on curve")
    15  var errHashNeeded = errors.New("hFunc cannot be nil. We need a hash for Fiat-Shamir")
    16  
    17  const (
    18  	sizeFr         = fr.Bytes
    19  	sizePublicKey  = sizeFr
    20  	sizeSignature  = 2 * sizeFr
    21  	sizePrivateKey = 2 * sizeFr + 32
    22  )
    23  
    24  // PublicKey eddsa signature object
    25  // cf https://en.wikipedia.org/wiki/EdDSA for notation
    26  type PublicKey struct {
    27  	A twistededwards.PointAffine
    28  }
    29  
    30  // PrivateKey private key of an eddsa instance
    31  type PrivateKey struct {
    32  	PublicKey  	PublicKey    // copy of the associated public key
    33  	scalar  	[sizeFr]byte // secret scalar, in big Endian
    34  	randSrc 	[32]byte // source
    35  }
    36  
    37  // Signature represents an eddsa signature
    38  // cf https://en.wikipedia.org/wiki/EdDSA for notation
    39  type Signature struct {
    40  	R twistededwards.PointAffine
    41  	S [sizeFr]byte
    42  }
    43  
    44  
    45  // GenerateKey generates a public and private key pair.
    46  func GenerateKey(r io.Reader) (*PrivateKey, error) {
    47  	c := twistededwards.GetEdwardsCurve()
    48  
    49  	var pub PublicKey
    50  	var priv PrivateKey
    51  
    52      {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}}
    53  	// The source of randomness and the secret scalar must come
    54  	// from 2 distinct sources. Since the scalar is the size of the
    55  	// field of definition (48 bytes), the scalar must come from a
    56  	// different digest so there is no overlap between the source of
    57  	// randomness and the scalar.
    58  
    59  	// used for random scalar (aka private key)
    60  	seed := make([]byte, 32)
    61  	_, err := r.Read(seed)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	h1 := blake2b.Sum512(seed[:])
    66  
    67  	// used for the source of randomness when hashing the message
    68  	h2 := blake2b.Sum512(h1[:])
    69  	for i := 0; i < 32; i++ {
    70  		priv.randSrc[i] = h2[i]
    71  	}
    72  	{{- else }}
    73  	// hash(h) = private_key || random_source, on 32 bytes each
    74  	seed := make([]byte, 32)
    75  	_, err := r.Read(seed)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	h := blake2b.Sum512(seed[:])
    80  	for i := 0; i < 32; i++ {
    81  		priv.randSrc[i] = h[i+32]
    82  	}
    83  	{{- end }}
    84  
    85  	// prune the key
    86  	// https://tools.ietf.org/html/rfc8032#section-5.1.5, key generation
    87  	{{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}}
    88  	h1[0] &= 0xF8
    89  	h1[sizeFr-1] &= 0x7F
    90  	h1[sizeFr-1] |= 0x40
    91  	{{- else }}
    92  	h[0] &= 0xF8
    93  	h[31] &= 0x7F
    94  	h[31] |= 0x40
    95  	{{- end }}
    96  
    97  	// reverse first bytes because setBytes interpret stream as big endian
    98  	// but in eddsa specs s is the first 32 bytes in little endian
    99  	{{- $h := "h"}}
   100  	{{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}}
   101  		{{- $h = "h1"}}
   102  	{{- end}}
   103  	for i, j := 0, sizeFr - 1; i < sizeFr; i, j = i+1, j-1 {
   104  		priv.scalar[i] = {{$h}}[j]
   105  	}
   106  
   107  	var bScalar big.Int
   108  	bScalar.SetBytes(priv.scalar[:])
   109  	pub.A.ScalarMultiplication(&c.Base, &bScalar)
   110  
   111  	priv.PublicKey = pub
   112  
   113  	return &priv, nil
   114  }
   115  
   116  
   117  // Equal compares 2 public keys
   118  func (pub *PublicKey) Equal(x signature.PublicKey) bool {
   119  	xx, ok := x.(*PublicKey)
   120  	if !ok {
   121  		return false
   122  	}
   123  	bpk := pub.Bytes()
   124  	bxx := xx.Bytes()
   125  	return subtle.ConstantTimeCompare(bpk, bxx) == 1
   126  }
   127  
   128  // Public returns the public key associated to the private key.
   129  func (privKey *PrivateKey) Public() signature.PublicKey {
   130  	var pub PublicKey
   131  	pub.A.Set(&privKey.PublicKey.A)
   132  	return &pub
   133  }
   134  
   135  // Sign sign a sequence of field elements
   136  // For arbitrary strings use fr.Hash first
   137  // Pure Eddsa version (see https://tools.ietf.org/html/rfc8032#page-8)
   138  func (privKey *PrivateKey) Sign(message []byte, hFunc hash.Hash) ([]byte, error) {
   139  
   140  	// hFunc cannot be nil.
   141  	// We need a hash function for the Fiat-Shamir.
   142  	if hFunc == nil {
   143  		return nil, errHashNeeded
   144  	}
   145  
   146  	curveParams := twistededwards.GetEdwardsCurve()
   147  
   148  	var res Signature
   149  
   150  	// blinding factor for the private key
   151  	// blindingFactorBigInt must be the same size as the private key,
   152  	// blindingFactorBigInt = h(randomness_source||message)[:sizeFr]
   153  	var blindingFactorBigInt big.Int
   154  
   155  // randSrc = privKey.randSrc || msg (-> message = MSB message .. LSB message)
   156  	randSrc := make([]byte, 32+len(message))
   157  	copy(randSrc, privKey.randSrc[:])
   158  	copy(randSrc[32:], message)
   159  
   160  	// randBytes = H(randSrc)
   161  	blindingFactorBytes := blake2b.Sum512(randSrc[:]) // TODO ensures that the hash used to build the key and the one used here is the same
   162  	blindingFactorBigInt.SetBytes(blindingFactorBytes[:sizeFr])
   163  
   164  	// compute R = randScalar*Base
   165  	res.R.ScalarMultiplication(&curveParams.Base, &blindingFactorBigInt)
   166  	if !res.R.IsOnCurve() {
   167  		return nil, errNotOnCurve
   168  	}
   169  
   170  	// compute H(R, A, M), all parameters in data are in Montgomery form
   171  	hFunc.Reset()
   172  
   173  	resRX := res.R.X.Bytes()
   174   	resRY := res.R.Y.Bytes()
   175   	resAX := privKey.PublicKey.A.X.Bytes()
   176   	resAY := privKey.PublicKey.A.Y.Bytes()
   177  	toWrite := [][]byte{resRX[:], resRY[:], resAX[:], resAY[:], message}
   178  	for _, bytes := range toWrite {
   179  		if _, err := hFunc.Write(bytes); err != nil {
   180  			return nil, err
   181  		}
   182  	}
   183  
   184  	var hramInt big.Int
   185  	hramBin := hFunc.Sum(nil)
   186  	hramInt.SetBytes(hramBin)
   187  
   188  	// Compute s = randScalarInt + H(R,A,M)*S
   189  	// going with big int to do ops mod curve order
   190  	var bscalar, bs big.Int
   191  	bscalar.SetBytes(privKey.scalar[:])
   192  	bs.Mul(&hramInt, &bscalar).
   193  		Add(&bs, &blindingFactorBigInt).
   194  		Mod(&bs, &curveParams.Order)
   195  	sb := bs.Bytes()
   196  	if len(sb) < sizeFr {
   197  		offset := make([]byte, sizeFr-len(sb))
   198  		sb = append(offset, sb...)
   199  	}
   200  	copy(res.S[:], sb[:])
   201  
   202  	return res.Bytes(), nil
   203  }
   204  
   205  // Verify verifies an eddsa signature
   206  func (pub *PublicKey) Verify(sigBin, message []byte, hFunc hash.Hash) (bool, error) {
   207  
   208  	// hFunc cannot be nil.
   209  	// We need a hash function for the Fiat-Shamir.
   210  	if hFunc == nil {
   211  		return false, errHashNeeded
   212  	}
   213  
   214  	curveParams := twistededwards.GetEdwardsCurve()
   215  
   216  	// verify that pubKey and R are on the curve
   217  	if !pub.A.IsOnCurve() {
   218  		return false, errNotOnCurve
   219  	}
   220  
   221  	// Deserialize the signature
   222  	var sig Signature
   223  	if _, err := sig.SetBytes(sigBin); err != nil {
   224  		return false, err
   225  	}
   226  
   227  	// compute H(R, A, M), all parameters in data are in Montgomery form
   228  
   229  	hFunc.Reset()
   230  
   231  	sigRX := sig.R.X.Bytes()
   232   	sigRY := sig.R.Y.Bytes()
   233   	sigAX := pub.A.X.Bytes()
   234   	sigAY := pub.A.Y.Bytes()
   235  
   236  	toWrite := [][]byte{sigRX[:], sigRY[:], sigAX[:], sigAY[:], message}
   237  	for _, bytes := range toWrite {
   238  		if _, err := hFunc.Write(bytes); err != nil {
   239  			return false, err
   240  		}
   241  	}
   242  
   243  	var hramInt big.Int
   244  	hramBin := hFunc.Sum(nil)
   245  	hramInt.SetBytes(hramBin)
   246  
   247  	// lhs = cofactor*S*Base
   248  	var lhs twistededwards.PointAffine
   249  	var bCofactor, bs big.Int
   250  	curveParams.Cofactor.BigInt(&bCofactor)
   251  	bs.SetBytes(sig.S[:])
   252  	lhs.ScalarMultiplication(&curveParams.Base, &bs).
   253  		ScalarMultiplication(&lhs, &bCofactor)
   254  
   255  	if !lhs.IsOnCurve() {
   256  		return false, errNotOnCurve
   257  	}
   258  
   259  	// rhs = cofactor*(R + H(R,A,M)*A)
   260  	var rhs twistededwards.PointAffine
   261  	rhs.ScalarMultiplication(&pub.A, &hramInt).
   262  		Add(&rhs, &sig.R).
   263  		ScalarMultiplication(&rhs, &bCofactor)
   264  	if !rhs.IsOnCurve() {
   265  		return false, errNotOnCurve
   266  	}
   267  
   268  	// verifies that cofactor*S*Base=cofactor*(R + H(R,A,M)*A)
   269  	if !lhs.X.Equal(&rhs.X) || !lhs.Y.Equal(&rhs.Y) {
   270  		return false, nil
   271  	}
   272  
   273  	return true, nil
   274  }