From d85e318f7a6b586f34d8a8cef7811f797c5c2c9e Mon Sep 17 00:00:00 2001 From: armfazh Date: Wed, 12 Apr 2023 18:04:23 -0700 Subject: [PATCH 1/3] Adding support for BLS12381 using CIRCL. --- go.mod | 1 + go.sum | 2 + pairing/bls12381/g1.go | 95 +++++++ pairing/bls12381/g2.go | 95 +++++++ pairing/bls12381/group.go | 23 ++ pairing/bls12381/gt.go | 92 +++++++ pairing/bls12381/scalar.go | 117 +++++++++ pairing/bls12381/suite.go | 56 +++++ pairing/bls12381/suite_test.go | 440 +++++++++++++++++++++++++++++++++ 9 files changed, 921 insertions(+) create mode 100644 pairing/bls12381/g1.go create mode 100644 pairing/bls12381/g2.go create mode 100644 pairing/bls12381/group.go create mode 100644 pairing/bls12381/gt.go create mode 100644 pairing/bls12381/scalar.go create mode 100644 pairing/bls12381/suite.go create mode 100644 pairing/bls12381/suite_test.go diff --git a/go.mod b/go.mod index 68b66fd9b..dd5947e22 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/drand/kyber go 1.18 require ( + github.com/cloudflare/circl v1.3.2 github.com/drand/kyber-bls12381 v0.2.5 github.com/jonboulle/clockwork v0.3.0 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 523cbc9a5..7385aede9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cloudflare/circl v1.3.2 h1:VWp8dY3yH69fdM7lM6A1+NhhVoDu9vqK0jOgmkQHFWk= +github.com/cloudflare/circl v1.3.2/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/pairing/bls12381/g1.go b/pairing/bls12381/g1.go new file mode 100644 index 000000000..70508ca82 --- /dev/null +++ b/pairing/bls12381/g1.go @@ -0,0 +1,95 @@ +package bls12381 + +import ( + "crypto/cipher" + "io" + + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" +) + +var _ kyber.SubGroupElement = &G1Elt{} + +type G1Elt struct{ inner circl.G1 } + +func (p *G1Elt) MarshalBinary() (data []byte, err error) { return p.inner.BytesCompressed(), nil } + +func (p *G1Elt) UnmarshalBinary(data []byte) error { return p.inner.SetBytes(data) } + +func (p *G1Elt) String() string { return p.inner.String() } + +func (p *G1Elt) MarshalSize() int { return circl.G1SizeCompressed } + +func (p *G1Elt) MarshalTo(w io.Writer) (int, error) { + buf, err := p.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(buf) +} + +func (p *G1Elt) UnmarshalFrom(r io.Reader) (int, error) { + buf := make([]byte, p.MarshalSize()) + n, err := io.ReadFull(r, buf) + if err != nil { + return n, err + } + return n, p.UnmarshalBinary(buf) +} + +func (p *G1Elt) Equal(p2 kyber.Point) bool { x := p2.(*G1Elt); return p.inner.IsEqual(&x.inner) } + +func (p *G1Elt) Null() kyber.Point { p.inner.SetIdentity(); return p } + +func (p *G1Elt) Base() kyber.Point { p.inner = *circl.G1Generator(); return p } + +func (p *G1Elt) Pick(rand cipher.Stream) kyber.Point { + var buf [32]byte + rand.XORKeyStream(buf[:], buf[:]) + p.inner.Hash(buf[:], nil) + return p +} + +func (p *G1Elt) Set(p2 kyber.Point) kyber.Point { p.inner = p2.(*G1Elt).inner; return p } + +func (p *G1Elt) Clone() kyber.Point { return new(G1Elt).Set(p) } + +func (p *G1Elt) EmbedLen() int { + panic("bls12-381: unsupported operation") +} + +func (p *G1Elt) Embed(data []byte, r cipher.Stream) kyber.Point { + panic("bls12-381: unsupported operation") +} + +func (p *G1Elt) Data() ([]byte, error) { + panic("bls12-381: unsupported operation") +} + +func (p *G1Elt) Add(a, b kyber.Point) kyber.Point { + aa, bb := a.(*G1Elt), b.(*G1Elt) + p.inner.Add(&aa.inner, &bb.inner) + return p +} + +func (p *G1Elt) Sub(a, b kyber.Point) kyber.Point { return p.Add(a, new(G1Elt).Neg(b)) } + +func (p *G1Elt) Neg(a kyber.Point) kyber.Point { + p.Set(a) + p.inner.Neg() + return p +} + +func (p *G1Elt) Mul(s kyber.Scalar, q kyber.Point) kyber.Point { + if q == nil { + q = new(G1Elt).Base() + } + ss, qq := s.(*Scalar), q.(*G1Elt) + p.inner.ScalarMult(&ss.inner, &qq.inner) + return p +} + +func (p *G1Elt) IsInCorrectGroup() bool { return p.inner.IsOnG1() } + +func (p *G1Elt) Hash(msg []byte) kyber.Point { p.inner.Hash(msg, nil); return p } +func (p *G1Elt) Hash2(msg, dst []byte) kyber.Point { p.inner.Hash(msg, dst); return p } diff --git a/pairing/bls12381/g2.go b/pairing/bls12381/g2.go new file mode 100644 index 000000000..da134ea47 --- /dev/null +++ b/pairing/bls12381/g2.go @@ -0,0 +1,95 @@ +package bls12381 + +import ( + "crypto/cipher" + "io" + + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" +) + +var _ kyber.SubGroupElement = &G2Elt{} + +type G2Elt struct{ inner circl.G2 } + +func (p *G2Elt) MarshalBinary() (data []byte, err error) { return p.inner.BytesCompressed(), nil } + +func (p *G2Elt) UnmarshalBinary(data []byte) error { return p.inner.SetBytes(data) } + +func (p *G2Elt) String() string { return p.inner.String() } + +func (p *G2Elt) MarshalSize() int { return circl.G2SizeCompressed } + +func (p *G2Elt) MarshalTo(w io.Writer) (int, error) { + buf, err := p.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(buf) +} + +func (p *G2Elt) UnmarshalFrom(r io.Reader) (int, error) { + buf := make([]byte, p.MarshalSize()) + n, err := io.ReadFull(r, buf) + if err != nil { + return n, err + } + return n, p.UnmarshalBinary(buf) +} + +func (p *G2Elt) Equal(p2 kyber.Point) bool { x := p2.(*G2Elt); return p.inner.IsEqual(&x.inner) } + +func (p *G2Elt) Null() kyber.Point { p.inner.SetIdentity(); return p } + +func (p *G2Elt) Base() kyber.Point { p.inner = *circl.G2Generator(); return p } + +func (p *G2Elt) Pick(rand cipher.Stream) kyber.Point { + var buf [32]byte + rand.XORKeyStream(buf[:], buf[:]) + p.inner.Hash(buf[:], nil) + return p +} + +func (p *G2Elt) Set(p2 kyber.Point) kyber.Point { p.inner = p2.(*G2Elt).inner; return p } + +func (p *G2Elt) Clone() kyber.Point { return new(G2Elt).Set(p) } + +func (p *G2Elt) EmbedLen() int { + panic("bls12-381: unsupported operation") +} + +func (p *G2Elt) Embed(data []byte, r cipher.Stream) kyber.Point { + panic("bls12-381: unsupported operation") +} + +func (p *G2Elt) Data() ([]byte, error) { + panic("bls12-381: unsupported operation") +} + +func (p *G2Elt) Add(a, b kyber.Point) kyber.Point { + aa, bb := a.(*G2Elt), b.(*G2Elt) + p.inner.Add(&aa.inner, &bb.inner) + return p +} + +func (p *G2Elt) Sub(a, b kyber.Point) kyber.Point { return p.Add(a, new(G2Elt).Neg(b)) } + +func (p *G2Elt) Neg(a kyber.Point) kyber.Point { + p.Set(a) + p.inner.Neg() + return p +} + +func (p *G2Elt) Mul(s kyber.Scalar, q kyber.Point) kyber.Point { + if q == nil { + q = new(G2Elt).Base() + } + ss, qq := s.(*Scalar), q.(*G2Elt) + p.inner.ScalarMult(&ss.inner, &qq.inner) + return p +} + +func (p *G2Elt) IsInCorrectGroup() bool { return p.inner.IsOnG2() } + +func (p *G2Elt) Hash(msg []byte) kyber.Point { p.inner.Hash(msg, nil); return p } +func (p *G2Elt) Hash2(msg, dst []byte) kyber.Point { p.inner.Hash(msg, dst); return p } diff --git a/pairing/bls12381/group.go b/pairing/bls12381/group.go new file mode 100644 index 000000000..a5c4e7792 --- /dev/null +++ b/pairing/bls12381/group.go @@ -0,0 +1,23 @@ +package bls12381 + +import ( + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" +) + +var ( + G1 kyber.Group = &groupBls{name: "bls12-381.G1", newPoint: func() kyber.Point { return new(G1Elt).Null() }} + G2 kyber.Group = &groupBls{name: "bls12-381.G2", newPoint: func() kyber.Point { return new(G2Elt).Null() }} + GT kyber.Group = &groupBls{name: "bls12-381.GT", newPoint: func() kyber.Point { return new(GTElt).Null() }} +) + +type groupBls struct { + name string + newPoint func() kyber.Point +} + +func (g groupBls) String() string { return g.name } +func (g groupBls) ScalarLen() int { return circl.ScalarSize } +func (g groupBls) Scalar() kyber.Scalar { return new(Scalar).SetInt64(0) } +func (g groupBls) PointLen() int { return g.newPoint().MarshalSize() } +func (g groupBls) Point() kyber.Point { return g.newPoint() } diff --git a/pairing/bls12381/gt.go b/pairing/bls12381/gt.go new file mode 100644 index 000000000..3685c6d08 --- /dev/null +++ b/pairing/bls12381/gt.go @@ -0,0 +1,92 @@ +package bls12381 + +import ( + "crypto/cipher" + "io" + + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" +) + +var gtBase *circl.Gt + +func init() { + gtBase = circl.Pair(circl.G1Generator(), circl.G2Generator()) +} + +var _ kyber.Point = >Elt{} + +type GTElt struct{ inner circl.Gt } + +func (p *GTElt) MarshalBinary() (data []byte, err error) { return p.inner.MarshalBinary() } + +func (p *GTElt) UnmarshalBinary(data []byte) error { return p.inner.UnmarshalBinary(data) } + +func (p *GTElt) String() string { return p.inner.String() } + +func (p *GTElt) MarshalSize() int { return circl.GtSize } + +func (p *GTElt) MarshalTo(w io.Writer) (int, error) { + buf, err := p.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(buf) +} + +func (p *GTElt) UnmarshalFrom(r io.Reader) (int, error) { + buf := make([]byte, p.MarshalSize()) + n, err := io.ReadFull(r, buf) + if err != nil { + return n, err + } + return n, p.UnmarshalBinary(buf) +} + +func (p *GTElt) Equal(p2 kyber.Point) bool { x := p2.(*GTElt); return p.inner.IsEqual(&x.inner) } + +func (p *GTElt) Null() kyber.Point { p.inner.SetIdentity(); return p } + +func (p *GTElt) Base() kyber.Point { p.inner = *gtBase; return p } + +func (p *GTElt) Pick(rand cipher.Stream) kyber.Point { + panic("bls12-381: unsupported operation") +} + +func (p *GTElt) Set(p2 kyber.Point) kyber.Point { p.inner = p2.(*GTElt).inner; return p } + +func (p *GTElt) Clone() kyber.Point { return new(GTElt).Set(p) } + +func (p *GTElt) EmbedLen() int { + panic("bls12-381: unsupported operation") +} + +func (p *GTElt) Embed(data []byte, r cipher.Stream) kyber.Point { + panic("bls12-381: unsupported operation") +} + +func (p *GTElt) Data() ([]byte, error) { + panic("bls12-381: unsupported operation") +} + +func (p *GTElt) Add(a, b kyber.Point) kyber.Point { + aa, bb := a.(*GTElt), b.(*GTElt) + p.inner.Mul(&aa.inner, &bb.inner) + return p +} + +func (p *GTElt) Sub(a, b kyber.Point) kyber.Point { + return p.Add(a, new(GTElt).Neg(b)) +} + +func (p *GTElt) Neg(a kyber.Point) kyber.Point { + aa := a.(*GTElt) + p.inner.Inv(&aa.inner) + return p +} + +func (p *GTElt) Mul(s kyber.Scalar, q kyber.Point) kyber.Point { + qq, ss := q.(*GTElt), s.(*Scalar) + p.inner.Exp(&qq.inner, &ss.inner) + return p +} diff --git a/pairing/bls12381/scalar.go b/pairing/bls12381/scalar.go new file mode 100644 index 000000000..5fbd1dbff --- /dev/null +++ b/pairing/bls12381/scalar.go @@ -0,0 +1,117 @@ +package bls12381 + +import ( + "crypto/cipher" + "io" + + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" +) + +var _ kyber.Scalar = &Scalar{} + +type Scalar struct{ inner circl.Scalar } + +func (s *Scalar) MarshalBinary() (data []byte, err error) { return s.inner.MarshalBinary() } + +func (s *Scalar) UnmarshalBinary(data []byte) error { return s.inner.UnmarshalBinary(data) } + +func (s *Scalar) String() string { return s.inner.String() } + +func (s *Scalar) MarshalSize() int { return circl.ScalarSize } + +func (s *Scalar) MarshalTo(w io.Writer) (int, error) { + buf, err := s.inner.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(buf) +} + +func (s *Scalar) UnmarshalFrom(r io.Reader) (int, error) { + buf := make([]byte, s.MarshalSize()) + n, err := io.ReadFull(r, buf) + if err != nil { + return n, err + } + return n, s.inner.UnmarshalBinary(buf) +} + +func (s *Scalar) Equal(s2 kyber.Scalar) bool { + x := s2.(*Scalar) + return s.inner.IsEqual(&x.inner) == 1 +} + +func (s *Scalar) Set(a kyber.Scalar) kyber.Scalar { + aa := a.(*Scalar) + s.inner.Set(&aa.inner) + return s +} + +func (s *Scalar) Clone() kyber.Scalar { return new(Scalar).Set(s) } + +func (s *Scalar) SetInt64(v int64) kyber.Scalar { + if v >= 0 { + s.inner.SetUint64(uint64(v)) + } else { + s.inner.SetUint64(uint64(-v)) + s.inner.Neg() + } + + return s +} + +func (s *Scalar) Zero() kyber.Scalar { s.inner.SetUint64(0); return s } + +func (s *Scalar) Add(a, b kyber.Scalar) kyber.Scalar { + aa, bb := a.(*Scalar), b.(*Scalar) + s.inner.Add(&aa.inner, &bb.inner) + return s +} + +func (s *Scalar) Sub(a, b kyber.Scalar) kyber.Scalar { + aa, bb := a.(*Scalar), b.(*Scalar) + s.inner.Sub(&aa.inner, &bb.inner) + return s +} + +func (s *Scalar) Neg(a kyber.Scalar) kyber.Scalar { + s.Set(a) + s.inner.Neg() + return s +} + +func (s *Scalar) One() kyber.Scalar { s.inner.SetUint64(1); return s } + +func (s *Scalar) Mul(a, b kyber.Scalar) kyber.Scalar { + aa, bb := a.(*Scalar), b.(*Scalar) + s.inner.Mul(&aa.inner, &bb.inner) + return s +} + +func (s *Scalar) Div(a, b kyber.Scalar) kyber.Scalar { return s.Mul(new(Scalar).Inv(b), a) } + +func (s *Scalar) Inv(a kyber.Scalar) kyber.Scalar { + aa := a.(*Scalar) + s.inner.Inv(&aa.inner) + return s +} + +type zeroReader struct{} + +func (zeroReader) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = 0 + } + return len(p), nil +} + +func (s *Scalar) Pick(stream cipher.Stream) kyber.Scalar { + err := s.inner.Random(cipher.StreamReader{S: stream, R: zeroReader{}}) + if err != nil { + panic(err) + } + return s +} + +func (s *Scalar) SetBytes(data []byte) kyber.Scalar { s.inner.SetBytes(data); return s } diff --git a/pairing/bls12381/suite.go b/pairing/bls12381/suite.go new file mode 100644 index 000000000..4b931c357 --- /dev/null +++ b/pairing/bls12381/suite.go @@ -0,0 +1,56 @@ +package bls12381 + +import ( + "crypto/cipher" + "crypto/sha256" + "hash" + "io" + + circl "github.com/cloudflare/circl/ecc/bls12381" + "github.com/drand/kyber" + "github.com/drand/kyber/pairing" + "github.com/drand/kyber/util/random" + "github.com/drand/kyber/xof/blake2xb" +) + +var _ pairing.Suite = Suite{} + +type Suite struct{} + +func (s Suite) G1() kyber.Group { return G1 } +func (s Suite) G2() kyber.Group { return G2 } +func (s Suite) GT() kyber.Group { return GT } +func (s Suite) Pair(p1, p2 kyber.Point) kyber.Point { + aa, bb := p1.(*G1Elt), p2.(*G2Elt) + return >Elt{*circl.Pair(&aa.inner, &bb.inner)} +} +func (s Suite) ValidatePairing(p1, p2, p3, p4 kyber.Point) bool { + a, b := p1.(*G1Elt), p2.(*G2Elt) + c, d := p3.(*G1Elt), p4.(*G2Elt) + out := circl.ProdPairFrac( + []*circl.G1{&a.inner, &c.inner}, + []*circl.G2{&b.inner, &d.inner}, + []int{1, -1}, + ) + return out.IsIdentity() +} + +func (s Suite) Read(r io.Reader, objs ...interface{}) error { + panic("Suite.Read(): deprecated in drand") +} + +func (s Suite) Write(w io.Writer, objs ...interface{}) error { + panic("Suite.Write(): deprecated in drand") +} + +func (s Suite) Hash() hash.Hash { + return sha256.New() +} + +func (s Suite) XOF(seed []byte) kyber.XOF { + return blake2xb.New(seed) +} + +func (s Suite) RandomStream() cipher.Stream { + return random.New() +} diff --git a/pairing/bls12381/suite_test.go b/pairing/bls12381/suite_test.go new file mode 100644 index 000000000..2825daa9a --- /dev/null +++ b/pairing/bls12381/suite_test.go @@ -0,0 +1,440 @@ +package bls12381_test + +import ( + "bytes" + "crypto/cipher" + "sync" + "testing" + + "github.com/drand/kyber" + this "github.com/drand/kyber/pairing/bls12381" + "github.com/drand/kyber/sign/bls" + "github.com/drand/kyber/sign/tbls" + "github.com/drand/kyber/sign/test" + "github.com/drand/kyber/util/random" + "github.com/stretchr/testify/require" +) + +// Code extracted from kyber/utils/test +// TODO: expose API in forked drand/kyber +// Apply a generic set of validation tests to a cryptographic Group, +// using a given source of [pseudo-]randomness. +// +// Returns a log of the pseudorandom Points produced in the test, +// for comparison across alternative implementations +// that are supposed to be equivalent. +func testGroup(t *testing.T, g kyber.Group, rand cipher.Stream) []kyber.Point { + t.Logf("\nTesting group '%s': %d-byte Point, %d-byte Scalar\n", + g.String(), g.PointLen(), g.ScalarLen()) + + points := make([]kyber.Point, 0) + ptmp := g.Point() + stmp := g.Scalar() + pzero := g.Point().Null() + szero := g.Scalar().Zero() + sone := g.Scalar().One() + + // Do a simple Diffie-Hellman test + s1 := g.Scalar().Pick(rand) + s2 := g.Scalar().Pick(rand) + if s1.Equal(szero) { + t.Fatalf("first secret is scalar zero %v", s1) + } + if s2.Equal(szero) { + t.Fatalf("second secret is scalar zero %v", s2) + } + if s1.Equal(s2) { + t.Fatalf("not getting unique secrets: picked %s twice", s1) + } + + gen := g.Point().Base() + points = append(points, gen) + + // Sanity-check relationship between addition and multiplication + p1 := g.Point().Add(gen, gen) + p2 := g.Point().Mul(stmp.SetInt64(2), nil) + if !p1.Equal(p2) { + t.Fatalf("multiply by two doesn't work: %v == %v (+) %[2]v != %[2]v (x) 2 == %v", p1, gen, p2) + } + p1.Add(p1, p1) + p2.Mul(stmp.SetInt64(4), nil) + if !p1.Equal(p2) { + t.Fatalf("multiply by four doesn't work: %v (+) %[1]v != %v (x) 4 == %v", + g.Point().Add(gen, gen), gen, p2) + } + points = append(points, p1) + + // Find out if this curve has a prime order: + // if the curve does not offer a method IsPrimeOrder, + // then assume that it is. + type canCheckPrimeOrder interface { + IsPrimeOrder() bool + } + primeOrder := true + if gpo, ok := g.(canCheckPrimeOrder); ok { + primeOrder = gpo.IsPrimeOrder() + } + + // Verify additive and multiplicative identities of the generator. + // TODO: Check GT exp + /*fmt.Println("Inverse of base")*/ + //f := ptmp.Base().(*KyberGT).f + //newFp12(nil).inverse(f, f) + //fmt.Printf("\n-Inverse: %v\n", f) + //fmt.Println("Multiply by -1") + ptmp.Mul(stmp.SetInt64(-1), nil).Add(ptmp, gen) + /*fmt.Printf(" \n\nChecking equality additive identity\nptmp: %v \n\n zero %v\n", ptmp, pzero)*/ + if !ptmp.Equal(pzero) { + t.Fatalf("generator additive identity doesn't work: (scalar -1 %v) %v (x) -1 (+) %v = %v != %v the group point identity", + stmp.SetInt64(-1), ptmp.Mul(stmp.SetInt64(-1), nil), gen, ptmp.Mul(stmp.SetInt64(-1), nil).Add(ptmp, gen), pzero) + } + // secret.Inv works only in prime-order groups + if primeOrder { + ptmp.Mul(stmp.SetInt64(2), nil).Mul(stmp.Inv(stmp), ptmp) + if !ptmp.Equal(gen) { + t.Fatalf("generator multiplicative identity doesn't work:\n%v (x) %v = %v\n%[3]v (x) %v = %v", + ptmp.Base().String(), stmp.SetInt64(2).String(), + ptmp.Mul(stmp.SetInt64(2), nil).String(), + stmp.Inv(stmp).String(), + ptmp.Mul(stmp.SetInt64(2), nil).Mul(stmp.Inv(stmp), ptmp).String()) + } + } + + p1.Mul(s1, gen) + p2.Mul(s2, gen) + if p1.Equal(p2) { + t.Fatalf("encryption isn't producing unique points: %v (x) %v == %v (x) %[2]v == %[4]v", s1, gen, s2, p1) + } + points = append(points, p1) + + dh1 := g.Point().Mul(s2, p1) + dh2 := g.Point().Mul(s1, p2) + if !dh1.Equal(dh2) { + t.Fatalf("Diffie-Hellman didn't work: %v == %v (x) %v != %v (x) %v == %v", dh1, s2, p1, s1, p2, dh2) + } + points = append(points, dh1) + //t.Logf("shared secret = %v", dh1) + + // Test secret inverse to get from dh1 back to p1 + if primeOrder { + ptmp.Mul(g.Scalar().Inv(s2), dh1) + if !ptmp.Equal(p1) { + t.Fatalf("Scalar inverse didn't work: %v != (-)%v (x) %v == %v", p1, s2, dh1, ptmp) + } + } + + // Zero and One identity secrets + //println("dh1^0 = ",ptmp.Mul(dh1, szero).String()) + if !ptmp.Mul(szero, dh1).Equal(pzero) { + t.Fatalf("Encryption with secret=0 didn't work: %v (x) %v == %v != %v", szero, dh1, ptmp, pzero) + } + if !ptmp.Mul(sone, dh1).Equal(dh1) { + t.Fatalf("Encryption with secret=1 didn't work: %v (x) %v == %v != %[2]v", sone, dh1, ptmp) + } + + // Additive homomorphic identities + ptmp.Add(p1, p2) + stmp.Add(s1, s2) + pt2 := g.Point().Mul(stmp, gen) + if !pt2.Equal(ptmp) { + t.Fatalf("Additive homomorphism doesn't work: %v + %v == %v, %[3]v (x) %v == %v != %v == %v (+) %v", + s1, s2, stmp, gen, pt2, ptmp, p1, p2) + } + ptmp.Sub(p1, p2) + stmp.Sub(s1, s2) + pt2.Mul(stmp, gen) + if !pt2.Equal(ptmp) { + t.Fatalf("Additive homomorphism doesn't work: %v - %v == %v, %[3]v (x) %v == %v != %v == %v (-) %v", + s1, s2, stmp, gen, pt2, ptmp, p1, p2) + } + st2 := g.Scalar().Neg(s2) + st2.Add(s1, st2) + if !stmp.Equal(st2) { + t.Fatalf("Scalar.Neg doesn't work: -%v == %v, %[2]v + %v == %v != %v", + s2, g.Scalar().Neg(s2), s1, st2, stmp) + } + pt2.Neg(p2).Add(pt2, p1) + if !pt2.Equal(ptmp) { + t.Fatalf("Point.Neg doesn't work: (-)%v == %v, %[2]v (+) %v == %v != %v", + p2, g.Point().Neg(p2), p1, pt2, ptmp) + } + + // Multiplicative homomorphic identities + stmp.Mul(s1, s2) + if !ptmp.Mul(stmp, gen).Equal(dh1) { + t.Fatalf("Multiplicative homomorphism doesn't work: %v * %v == %v, %[2]v (x) %v == %v != %v", + s1, s2, stmp, gen, ptmp, dh1) + } + if primeOrder { + st2.Inv(s2) + st2.Mul(st2, stmp) + if !st2.Equal(s1) { + t.Fatalf("Scalar division doesn't work: %v^-1 * %v == %v * %[2]v == %[4]v != %v", + s2, stmp, g.Scalar().Inv(s2), st2, s1) + } + st2.Div(stmp, s2) + if !st2.Equal(s1) { + t.Fatalf("Scalar division doesn't work: %v / %v == %v != %v", + stmp, s2, st2, s1) + } + } + + pick := func(rand cipher.Stream) (p kyber.Point) { + defer func() { + /*if err := recover(); err != nil {*/ + //// TODO implement Pick for GT + //p = g.Point().Mul(g.Scalar().Pick(rand), nil) + //return + /*}*/ + }() + p = g.Point().Pick(rand) + return + } + + // Test randomly picked points + last := gen + for i := 0; i < 5; i++ { + // TODO fork kyber and make that an interface + rgen := pick(rand) + if rgen.Equal(last) { + t.Fatalf("Pick() not producing unique points: got %v twice", rgen) + } + last = rgen + + ptmp.Mul(stmp.SetInt64(-1), rgen).Add(ptmp, rgen) + if !ptmp.Equal(pzero) { + t.Fatalf("random generator fails additive identity: %v (x) %v == %v, %v (+) %[3]v == %[5]v != %v", + g.Scalar().SetInt64(-1), rgen, g.Point().Mul(g.Scalar().SetInt64(-1), rgen), + rgen, g.Point().Mul(g.Scalar().SetInt64(-1), rgen), pzero) + } + if primeOrder { + ptmp.Mul(stmp.SetInt64(2), rgen).Mul(stmp.Inv(stmp), ptmp) + if !ptmp.Equal(rgen) { + t.Fatalf("random generator fails multiplicative identity: %v (x) (2 (x) %v) == %v != %[2]v", + stmp, rgen, ptmp) + } + } + points = append(points, rgen) + } + + // Test encoding and decoding + buf := new(bytes.Buffer) + for i := 0; i < 5; i++ { + buf.Reset() + s := g.Scalar().Pick(rand) + if _, err := s.MarshalTo(buf); err != nil { + t.Fatalf("encoding of secret fails: " + err.Error()) + } + if _, err := stmp.UnmarshalFrom(buf); err != nil { + t.Fatalf("decoding of secret fails: " + err.Error()) + } + if !stmp.Equal(s) { + t.Fatalf("decoding produces different secret than encoded") + } + + buf.Reset() + p := pick(rand) + if _, err := p.MarshalTo(buf); err != nil { + t.Fatalf("encoding of point fails: " + err.Error()) + } + if _, err := ptmp.UnmarshalFrom(buf); err != nil { + t.Fatalf("decoding of point fails: " + err.Error()) + } + + if !ptmp.Equal(p) { + t.Fatalf("decoding produces different point than encoded") + } + } + + // Test that we can marshal/ unmarshal null point + pzero = g.Point().Null() + b, _ := pzero.MarshalBinary() + repzero := g.Point() + err := repzero.UnmarshalBinary(b) + if err != nil { + t.Fatalf("Could not unmarshall binary %v: %v", b, err) + } + + return points +} + +// GroupTest applies a generic set of validation tests to a cryptographic Group. +func GroupTest(t *testing.T, g kyber.Group) { + testGroup(t, g, random.New()) +} + +func TestKyberG1(t *testing.T) { + GroupTest(t, this.G1) +} + +func TestKyberG2(t *testing.T) { + GroupTest(t, this.G2) +} + +func TestKyberPairingG2(t *testing.T) { + s := this.Suite{} + a := s.G1().Scalar().Pick(s.RandomStream()) + b := s.G2().Scalar().Pick(s.RandomStream()) + aG := s.G1().Point().Mul(a, nil) + bH := s.G2().Point().Mul(b, nil) + ab := s.G1().Scalar().Mul(a, b) + abG := s.G1().Point().Mul(ab, nil) + // e(aG, bG) = e(G,H)^(ab) + p1 := s.Pair(aG, bH) + // e((ab)G,H) = e(G,H)^(ab) + p2 := s.Pair(abG, s.G2().Point().Base()) + require.True(t, p1.Equal(p2)) + require.True(t, s.ValidatePairing(aG, bH, abG.Clone(), s.G2().Point().Base())) + + pRandom := s.Pair(aG, s.G2().Point().Pick(s.RandomStream())) + require.False(t, p1.Equal(pRandom)) + pRandom = s.Pair(s.G1().Point().Pick(s.RandomStream()), bH) + require.False(t, p1.Equal(pRandom)) +} + +func TestRacePairings(t *testing.T) { + s := this.Suite{} + a := s.G1().Scalar().Pick(s.RandomStream()) + aG := s.G1().Point().Mul(a, nil) + B := s.G2().Point().Pick(s.RandomStream()) + aB := s.G2().Point().Mul(a, B.Clone()) + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + // e(p1,p2) =?= e(inv1^-1, inv2^-1) + s.ValidatePairing(aG, B, s.G1().Point(), aB) + wg.Done() + }() + } + wg.Wait() +} + +func BenchmarkPairingSeparate(bb *testing.B) { + s := this.Suite{} + a := s.G1().Scalar().Pick(s.RandomStream()) + b := s.G2().Scalar().Pick(s.RandomStream()) + aG := s.G1().Point().Mul(a, nil) + bH := s.G2().Point().Mul(b, nil) + ab := s.G1().Scalar().Mul(a, b) + abG := s.G1().Point().Mul(ab, nil) + bb.ResetTimer() + for i := 0; i < bb.N; i++ { + + // e(aG, bG) = e(G,H)^(ab) + p1 := s.Pair(aG, bH) + // e((ab)G,H) = e(G,H)^(ab) + p2 := s.Pair(abG, s.G2().Point().Base()) + if !p1.Equal(p2) { + panic("aie") + } + } +} + +func BenchmarkPairingInv(bb *testing.B) { + s := this.Suite{} + a := s.G1().Scalar().Pick(s.RandomStream()) + b := s.G2().Scalar().Pick(s.RandomStream()) + aG := s.G1().Point().Mul(a, nil) + bH := s.G2().Point().Mul(b, nil) + ab := s.G1().Scalar().Mul(a, b) + abG := s.G1().Point().Mul(ab, nil) + bb.ResetTimer() + for i := 0; i < bb.N; i++ { + if !s.ValidatePairing(aG, bH, abG.Clone(), s.G2().Point().Base()) { + panic("aie") + } + } +} + +func TestKyberBLSG2(t *testing.T) { + suite := this.Suite{} + scheme := bls.NewSchemeOnG2(suite) + test.SchemeTesting(t, scheme) +} + +func TestKyberBLSG1(t *testing.T) { + suite := this.Suite{} + scheme := bls.NewSchemeOnG1(suite) + test.SchemeTesting(t, scheme) +} + +func TestKyberThresholdG2(t *testing.T) { + suite := this.Suite{} + tscheme := tbls.NewThresholdSchemeOnG2(suite) + test.ThresholdTest(t, suite.G1(), tscheme) +} + +func TestKyberThresholdG1(t *testing.T) { + suite := this.Suite{} + tscheme := tbls.NewThresholdSchemeOnG1(suite) + test.ThresholdTest(t, suite.G2(), tscheme) +} + +func TestIsValidGroup(t *testing.T) { + suite := this.Suite{} + p1 := suite.G1().Point().Pick(random.New()) + p2 := suite.G1().Point().Pick(random.New()) + + require.True(t, p1.(kyber.SubGroupElement).IsInCorrectGroup()) + require.True(t, p2.(kyber.SubGroupElement).IsInCorrectGroup()) +} + +var suite = this.Suite{} + +func NewElement() kyber.Scalar { + return suite.G1().Scalar() +} +func NewG1() kyber.Point { + return suite.G1().Point().Base() +} +func NewG2() kyber.Point { + return suite.G2().Point().Base() +} +func Pair(a, b kyber.Point) kyber.Point { + return suite.Pair(a, b) +} +func TestBasicPairing(t *testing.T) { + + // we test a * b = c + d + a := NewElement().Pick(random.New()) + b := NewElement().Pick(random.New()) + c := NewElement().Pick(random.New()) + d := NewElement().Sub(NewElement().Mul(a, b), c) + + // check in the clear + ab := NewElement().Mul(a, b) + cd := NewElement().Add(c, d) + require.True(t, ab.Equal(cd)) + + // check in the exponent now with the following + // e(aG1,bG2) = e(cG1,G2) * e(G1,dG2) <=> + // e(G1,G2)^(a*b) = e(G1,G2)^c * e(G1,G2)^d + // e(G1,G2)^(a*b) = e(G1,G2)^(c + d) + aG := NewG1().Mul(a, nil) + bG := NewG2().Mul(b, nil) + left := Pair(aG, bG) + + cG := NewG1().Mul(c, nil) + right1 := Pair(cG, NewG2()) + dG := NewG2().Mul(d, nil) + right2 := Pair(NewG1(), dG) + right := suite.GT().Point().Add(right1, right2) + require.True(t, left.Equal(right)) + + // Test if addition works in GT + mright := right.Clone().Neg(right) + res := mright.Add(mright, right) + require.True(t, res.Equal(suite.GT().Point().Null())) + + // Test if Sub works in GT + expZero := right.Clone().Sub(right, right) + require.True(t, expZero.Equal(suite.GT().Point().Null())) + + // Test if scalar mul works in GT + // e(aG,G) == e(G,G)^a + left = Pair(aG, suite.G2().Point().Base()) + right = Pair(suite.G1().Point().Base(), suite.G2().Point().Base()) + right = right.Mul(a, right) + require.True(t, left.Equal(right)) +} From 8f8003d63e332d26a728e7165fdd1aefc2e0bc32 Mon Sep 17 00:00:00 2001 From: armfazh Date: Wed, 12 Apr 2023 18:16:30 -0700 Subject: [PATCH 2/3] Register suite for bls12381. --- pairing/bls12381/adapter.go | 48 ++++++++++++++++++++++++++++++++ pairing/bls12381/adapter_test.go | 28 +++++++++++++++++++ pairing/bls12381/suite.go | 3 ++ sign/tbls/tbls_test.go | 7 +++++ suites/all.go | 2 ++ 5 files changed, 88 insertions(+) create mode 100644 pairing/bls12381/adapter.go create mode 100644 pairing/bls12381/adapter_test.go diff --git a/pairing/bls12381/adapter.go b/pairing/bls12381/adapter.go new file mode 100644 index 000000000..359296dd5 --- /dev/null +++ b/pairing/bls12381/adapter.go @@ -0,0 +1,48 @@ +package bls12381 + +import ( + "github.com/drand/kyber" +) + +// SuiteBLS12381 is an adapter that implements the suites.Suite interface so that +// bls12381 can be used as a common suite to generate key pairs for instance but +// still preserves the properties of the pairing (e.g. the Pair function). +// +// It's important to note that the Point function will generate a point +// compatible with public keys only (group G2) where the signature must be +// used as a point from the group G1. +type SuiteBLS12381 struct { + Suite + kyber.Group +} + +// NewSuiteBLS12381 makes a new BN256 suite +func NewSuiteBLS12381() *SuiteBLS12381 { + return &SuiteBLS12381{} +} + +// Point generates a point from the G2 group that can only be used +// for public keys +func (s *SuiteBLS12381) Point() kyber.Point { + return s.G2().Point() +} + +// PointLen returns the length of a G2 point +func (s *SuiteBLS12381) PointLen() int { + return s.G2().PointLen() +} + +// Scalar generates a scalar +func (s *SuiteBLS12381) Scalar() kyber.Scalar { + return s.G1().Scalar() +} + +// ScalarLen returns the lenght of a scalar +func (s *SuiteBLS12381) ScalarLen() int { + return s.G1().ScalarLen() +} + +// String returns the name of the suite +func (s *SuiteBLS12381) String() string { + return "bls12381.adapter" +} diff --git a/pairing/bls12381/adapter_test.go b/pairing/bls12381/adapter_test.go new file mode 100644 index 000000000..0d607c0d4 --- /dev/null +++ b/pairing/bls12381/adapter_test.go @@ -0,0 +1,28 @@ +package bls12381 + +import ( + "testing" + + "github.com/drand/kyber/util/key" + "github.com/stretchr/testify/require" +) + +func TestAdapter_SuiteBLS12381(t *testing.T) { + suite := NewSuiteBLS12381() + + pair := key.NewKeyPair(suite) + pubkey, err := pair.Public.MarshalBinary() + require.Nil(t, err) + privkey, err := pair.Private.MarshalBinary() + require.Nil(t, err) + + pubhex := suite.Point() + err = pubhex.UnmarshalBinary(pubkey) + require.Nil(t, err) + + privhex := suite.Scalar() + err = privhex.UnmarshalBinary(privkey) + require.Nil(t, err) + + require.Equal(t, "bls12381.adapter", suite.String()) +} diff --git a/pairing/bls12381/suite.go b/pairing/bls12381/suite.go index 4b931c357..0e552f766 100644 --- a/pairing/bls12381/suite.go +++ b/pairing/bls12381/suite.go @@ -17,6 +17,9 @@ var _ pairing.Suite = Suite{} type Suite struct{} +func NewSuite() (s Suite) { return } + +func (s Suite) String() string { return "bls12381" } func (s Suite) G1() kyber.Group { return G1 } func (s Suite) G2() kyber.Group { return G2 } func (s Suite) GT() kyber.Group { return GT } diff --git a/sign/tbls/tbls_test.go b/sign/tbls/tbls_test.go index 88cc4a66b..fc6bc9abb 100644 --- a/sign/tbls/tbls_test.go +++ b/sign/tbls/tbls_test.go @@ -3,6 +3,7 @@ package tbls import ( "testing" + "github.com/drand/kyber/pairing/bls12381" "github.com/drand/kyber/pairing/bn256" "github.com/drand/kyber/sign/test" ) @@ -12,3 +13,9 @@ func TestBN256(t *testing.T) { scheme := NewThresholdSchemeOnG1(suite) test.ThresholdTest(t, suite.G2(), scheme) } + +func TestBLS12381(t *testing.T) { + suite := bls12381.NewSuite() + scheme := NewThresholdSchemeOnG1(suite) + test.ThresholdTest(t, suite.G2(), scheme) +} diff --git a/suites/all.go b/suites/all.go index f8a8cbe59..6c609d645 100644 --- a/suites/all.go +++ b/suites/all.go @@ -3,6 +3,7 @@ package suites import ( "github.com/drand/kyber/group/edwards25519" "github.com/drand/kyber/group/nist" + "github.com/drand/kyber/pairing/bls12381" "github.com/drand/kyber/pairing/bn256" ) @@ -15,6 +16,7 @@ func init() { register(bn256.NewSuiteG2()) register(bn256.NewSuiteGT()) register(bn256.NewSuiteBn256()) + register(bls12381.NewSuiteBLS12381()) // This is a constant time implementation that should be // used as much as possible register(edwards25519.NewBlakeSHA256Ed25519()) From f2633a8810ba4e2963ecdbf3077e7750fe021d02 Mon Sep 17 00:00:00 2001 From: Yolan Romailler Date: Wed, 21 Feb 2024 12:49:13 +0100 Subject: [PATCH 3/3] Renaming to circl_bls12381 --- pairing/{bls12381 => circl_bls12381}/adapter.go | 2 +- pairing/{bls12381 => circl_bls12381}/adapter_test.go | 2 +- pairing/{bls12381 => circl_bls12381}/g1.go | 2 +- pairing/{bls12381 => circl_bls12381}/g2.go | 2 +- pairing/{bls12381 => circl_bls12381}/group.go | 2 +- pairing/{bls12381 => circl_bls12381}/gt.go | 2 +- pairing/{bls12381 => circl_bls12381}/scalar.go | 2 +- pairing/{bls12381 => circl_bls12381}/suite.go | 2 +- pairing/{bls12381 => circl_bls12381}/suite_test.go | 4 ++-- sign/tbls/tbls_test.go | 4 ++-- suites/all.go | 4 ++-- 11 files changed, 14 insertions(+), 14 deletions(-) rename pairing/{bls12381 => circl_bls12381}/adapter.go (98%) rename pairing/{bls12381 => circl_bls12381}/adapter_test.go (96%) rename pairing/{bls12381 => circl_bls12381}/g1.go (99%) rename pairing/{bls12381 => circl_bls12381}/g2.go (99%) rename pairing/{bls12381 => circl_bls12381}/group.go (97%) rename pairing/{bls12381 => circl_bls12381}/gt.go (98%) rename pairing/{bls12381 => circl_bls12381}/scalar.go (99%) rename pairing/{bls12381 => circl_bls12381}/suite.go (98%) rename pairing/{bls12381 => circl_bls12381}/suite_test.go (99%) diff --git a/pairing/bls12381/adapter.go b/pairing/circl_bls12381/adapter.go similarity index 98% rename from pairing/bls12381/adapter.go rename to pairing/circl_bls12381/adapter.go index 359296dd5..aada3fcec 100644 --- a/pairing/bls12381/adapter.go +++ b/pairing/circl_bls12381/adapter.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "github.com/drand/kyber" diff --git a/pairing/bls12381/adapter_test.go b/pairing/circl_bls12381/adapter_test.go similarity index 96% rename from pairing/bls12381/adapter_test.go rename to pairing/circl_bls12381/adapter_test.go index 0d607c0d4..9d498fa02 100644 --- a/pairing/bls12381/adapter_test.go +++ b/pairing/circl_bls12381/adapter_test.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "testing" diff --git a/pairing/bls12381/g1.go b/pairing/circl_bls12381/g1.go similarity index 99% rename from pairing/bls12381/g1.go rename to pairing/circl_bls12381/g1.go index 70508ca82..0ea75bfe0 100644 --- a/pairing/bls12381/g1.go +++ b/pairing/circl_bls12381/g1.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "crypto/cipher" diff --git a/pairing/bls12381/g2.go b/pairing/circl_bls12381/g2.go similarity index 99% rename from pairing/bls12381/g2.go rename to pairing/circl_bls12381/g2.go index da134ea47..ea6a2f851 100644 --- a/pairing/bls12381/g2.go +++ b/pairing/circl_bls12381/g2.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "crypto/cipher" diff --git a/pairing/bls12381/group.go b/pairing/circl_bls12381/group.go similarity index 97% rename from pairing/bls12381/group.go rename to pairing/circl_bls12381/group.go index a5c4e7792..1291e30ef 100644 --- a/pairing/bls12381/group.go +++ b/pairing/circl_bls12381/group.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( circl "github.com/cloudflare/circl/ecc/bls12381" diff --git a/pairing/bls12381/gt.go b/pairing/circl_bls12381/gt.go similarity index 98% rename from pairing/bls12381/gt.go rename to pairing/circl_bls12381/gt.go index 3685c6d08..fe3ffde9d 100644 --- a/pairing/bls12381/gt.go +++ b/pairing/circl_bls12381/gt.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "crypto/cipher" diff --git a/pairing/bls12381/scalar.go b/pairing/circl_bls12381/scalar.go similarity index 99% rename from pairing/bls12381/scalar.go rename to pairing/circl_bls12381/scalar.go index 5fbd1dbff..8a564a19d 100644 --- a/pairing/bls12381/scalar.go +++ b/pairing/circl_bls12381/scalar.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "crypto/cipher" diff --git a/pairing/bls12381/suite.go b/pairing/circl_bls12381/suite.go similarity index 98% rename from pairing/bls12381/suite.go rename to pairing/circl_bls12381/suite.go index 0e552f766..88c02b2b1 100644 --- a/pairing/bls12381/suite.go +++ b/pairing/circl_bls12381/suite.go @@ -1,4 +1,4 @@ -package bls12381 +package circl_bls12381 import ( "crypto/cipher" diff --git a/pairing/bls12381/suite_test.go b/pairing/circl_bls12381/suite_test.go similarity index 99% rename from pairing/bls12381/suite_test.go rename to pairing/circl_bls12381/suite_test.go index 2825daa9a..99597be09 100644 --- a/pairing/bls12381/suite_test.go +++ b/pairing/circl_bls12381/suite_test.go @@ -1,4 +1,4 @@ -package bls12381_test +package circl_bls12381_test import ( "bytes" @@ -7,7 +7,7 @@ import ( "testing" "github.com/drand/kyber" - this "github.com/drand/kyber/pairing/bls12381" + this "github.com/drand/kyber/pairing/circl_bls12381" "github.com/drand/kyber/sign/bls" "github.com/drand/kyber/sign/tbls" "github.com/drand/kyber/sign/test" diff --git a/sign/tbls/tbls_test.go b/sign/tbls/tbls_test.go index fc6bc9abb..5a791da00 100644 --- a/sign/tbls/tbls_test.go +++ b/sign/tbls/tbls_test.go @@ -3,8 +3,8 @@ package tbls import ( "testing" - "github.com/drand/kyber/pairing/bls12381" "github.com/drand/kyber/pairing/bn256" + "github.com/drand/kyber/pairing/circl_bls12381" "github.com/drand/kyber/sign/test" ) @@ -15,7 +15,7 @@ func TestBN256(t *testing.T) { } func TestBLS12381(t *testing.T) { - suite := bls12381.NewSuite() + suite := circl_bls12381.NewSuite() scheme := NewThresholdSchemeOnG1(suite) test.ThresholdTest(t, suite.G2(), scheme) } diff --git a/suites/all.go b/suites/all.go index 6c609d645..abb06477b 100644 --- a/suites/all.go +++ b/suites/all.go @@ -3,8 +3,8 @@ package suites import ( "github.com/drand/kyber/group/edwards25519" "github.com/drand/kyber/group/nist" - "github.com/drand/kyber/pairing/bls12381" "github.com/drand/kyber/pairing/bn256" + "github.com/drand/kyber/pairing/circl_bls12381" ) func init() { @@ -16,7 +16,7 @@ func init() { register(bn256.NewSuiteG2()) register(bn256.NewSuiteGT()) register(bn256.NewSuiteBn256()) - register(bls12381.NewSuiteBLS12381()) + register(circl_bls12381.NewSuiteBLS12381()) // This is a constant time implementation that should be // used as much as possible register(edwards25519.NewBlakeSHA256Ed25519())