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 }