Skip to content

Commit

Permalink
pairing: Adds support for BLS12381 using CIRCL library (#49)
Browse files Browse the repository at this point in the history
* Adding support for BLS12381 using CIRCL.
* Register suite for bls12381.
* Renaming to circl_bls12381

---------

Co-authored-by: Yolan Romailler <[email protected]>
  • Loading branch information
armfazh and AnomalRoil authored Feb 21, 2024
1 parent 4417fb6 commit ab02d54
Show file tree
Hide file tree
Showing 13 changed files with 1,009 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down
48 changes: 48 additions & 0 deletions pairing/circl_bls12381/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package circl_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"
}
28 changes: 28 additions & 0 deletions pairing/circl_bls12381/adapter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package circl_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())
}
95 changes: 95 additions & 0 deletions pairing/circl_bls12381/g1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package circl_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 }
95 changes: 95 additions & 0 deletions pairing/circl_bls12381/g2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package circl_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 }
23 changes: 23 additions & 0 deletions pairing/circl_bls12381/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package circl_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() }
Loading

0 comments on commit ab02d54

Please sign in to comment.