Skip to content

Commit

Permalink
Applying changes after Thibault's review.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jun 26, 2023
1 parent 4824d24 commit 36749b6
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 23 deletions.
123 changes: 100 additions & 23 deletions sign/bls/bls.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
// Package bls provides BLS signatures instantiated with the BLS12-381 pairing curve.
// Package bls provides BLS signatures using the BLS12-381 pairing curve.
//
// This packages implements the IETF/CFRG draft for BLS signatures [1].
// Curently only the BASIC mode (one of the three modes specified
// in the draft) is supported. The pairing function is instantiated
// with the BLS12-381 curve.
//
// # Groups
//
// The BLS signature scheme can be instantiated with keys in one of the
// two groups: G1 or G2, which correspond to the input domain of a pairing
// function e(G1,G2) -> Gt.
// Thus, choosing keys in G1 implies that signature values are internally
// represented in G2; or viceversa. Use the types KeyG1SigG2 or KeyG2SigG1
// to express this preference.
//
// # Serialization
//
// The serialization of elements in G1 and G2 follows the recommendation
// given in [2], in order to be compatible with other implementations of
// BLS12-381 curve.
//
// # References
//
// [1] https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/
//
// [2] https://github.com/zkcrypto/bls12_381/blob/main/src/notes/serialization.rs
package bls

import (
Expand All @@ -12,6 +38,19 @@ import (
"golang.org/x/crypto/hkdf"
)

var (
ErrInvalid = errors.New("bls: invalid BLS instance")
ErrInvalidKey = errors.New("bls: invalid key")
ErrKeyGen = errors.New("bls: too many unsuccessful key generation tries")
ErrShortIKM = errors.New("bls: IKM material shorter than 32 bytes")
ErrAggregate = errors.New("bls: error while aggregating signatures")
)

const (
dstG1 = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_"
dstG2 = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"
)

type Signature = []byte

type (
Expand Down Expand Up @@ -44,6 +83,8 @@ type PublicKey[K KeyGroup] struct{ key K }

func (k *PrivateKey[K]) Public() crypto.PublicKey { return k.PublicKey() }

// PublicKey computes the corresponding public key. The key is cached
// for further invocations to this function.
func (k *PrivateKey[K]) PublicKey() *PublicKey[K] {
if k.pub == nil {
k.pub = new(PublicKey[K])
Expand All @@ -64,14 +105,30 @@ func (k *PrivateKey[K]) PublicKey() *PublicKey[K] {

func (k *PrivateKey[K]) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey[K])
if !ok {
return false
}

switch (interface{})(k).(type) {
case *PrivateKey[G1], *PrivateKey[G2]:
return k.key.IsEqual(&xx.key) == 1
default:
panic(ErrInvalid)
}
}

// Validate explicitly determines if a private key is valid.
func (k *PrivateKey[K]) Validate() bool {
switch (interface{})(k).(type) {
case *PrivateKey[G1], *PrivateKey[G2]:
return ok && k.key.IsEqual(&xx.key) == 1
return k.key.IsZero() == 0
default:
panic(ErrInvalid)
}
}

// MarshalBinary returns a slice with the representation of
// the underlying PrivateKey scalar (in big-endian order).
func (k *PrivateKey[K]) MarshalBinary() ([]byte, error) {
switch (interface{})(k).(type) {
case *PrivateKey[G1], *PrivateKey[G2]:
Expand All @@ -84,12 +141,20 @@ func (k *PrivateKey[K]) MarshalBinary() ([]byte, error) {
func (k *PrivateKey[K]) UnmarshalBinary(data []byte) error {
switch (interface{})(k).(type) {
case *PrivateKey[G1], *PrivateKey[G2]:
return k.key.UnmarshalBinary(data)
if err := k.key.UnmarshalBinary(data); err != nil {
return err
}
if !k.Validate() {
return ErrInvalidKey
}
k.pub = nil
return nil
default:
panic(ErrInvalid)
}
}

// Validate explicitly determines if a public key is valid.
func (k *PublicKey[K]) Validate() bool {
switch (interface{})(k).(type) {
case *PublicKey[G1]:
Expand All @@ -105,20 +170,26 @@ func (k *PublicKey[K]) Validate() bool {

func (k *PublicKey[K]) Equal(x crypto.PublicKey) bool {
xx, ok := x.(*PublicKey[K])
if !ok {
return false
}

switch (interface{})(k).(type) {
case *PublicKey[G1]:
xxx := (interface{})(xx.key).(G1)
kk := (interface{})(k.key).(G1)
return ok && kk.g.IsEqual(&xxx.g)
return kk.g.IsEqual(&xxx.g)
case *PublicKey[G2]:
xxx := (interface{})(xx.key).(G2)
kk := (interface{})(k.key).(G2)
return ok && kk.g.IsEqual(&xxx.g)
return kk.g.IsEqual(&xxx.g)
default:
panic(ErrInvalid)
}
}

// MarshalBinary returns a slice with the compressed
// representation of the underlying element in G1 or G2.
func (k *PublicKey[K]) MarshalBinary() ([]byte, error) {
switch (interface{})(k).(type) {
case *PublicKey[G1]:
Expand All @@ -145,7 +216,13 @@ func (k *PublicKey[K]) UnmarshalBinary(data []byte) error {
}
}

// KeyGen derives a private key for the specified group (G1 or G2).
// The length of ikm material should be at least 32 bytes length.
// The salt value should be either empty or a uniformly random
// bytes whose length equals the output length of SHA-256.
func KeyGen[K KeyGroup](ikm, salt, keyInfo []byte) (*PrivateKey[K], error) {
// Implements recommended method at:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05#name-keygen
if len(ikm) < 32 {
return nil, ErrShortIKM
}
Expand Down Expand Up @@ -179,7 +256,13 @@ func KeyGen[K KeyGroup](ikm, salt, keyInfo []byte) (*PrivateKey[K], error) {
return nil, ErrKeyGen
}

// Sign computes a signature of a message using a key (defined in
// G1 or G1).
func Sign[K KeyGroup](k *PrivateKey[K], msg []byte) Signature {
if !k.Validate() {
panic(ErrInvalidKey)
}

switch (interface{})(k).(type) {
case *PrivateKey[G1]:
var Q GG.G2
Expand All @@ -196,6 +279,8 @@ func Sign[K KeyGroup](k *PrivateKey[K], msg []byte) Signature {
}
}

// Verify returns true if the signature of a message is valid for the
// corresponding public key.
func Verify[K KeyGroup](pub *PublicKey[K], msg []byte, sig Signature) bool {
var (
a, b interface {
Expand Down Expand Up @@ -236,6 +321,9 @@ func Verify[K KeyGroup](pub *PublicKey[K], msg []byte, sig Signature) bool {
return res.IsIdentity()
}

// Aggregate produces a unified signature given a list of signatures.
// To specify the group of keys pass either G1{} or G2{} as the first
// parameter.
func Aggregate[K KeyGroup](k K, sigs []Signature) (Signature, error) {
if len(sigs) == 0 {
return nil, ErrAggregate
Expand Down Expand Up @@ -269,6 +357,9 @@ func Aggregate[K KeyGroup](k K, sigs []Signature) (Signature, error) {
}
}

// VerifyAggregate returns true if the aggregated signature is valid for
// the list of messages and public keys provided. The slices must have
// equal size and have at least one element.
func VerifyAggregate[K KeyGroup](pubs []*PublicKey[K], msgs [][]byte, aggSig Signature) bool {
if len(pubs) != len(msgs) || len(pubs) == 0 || len(msgs) == 0 {
return false
Expand All @@ -279,6 +370,10 @@ func VerifyAggregate[K KeyGroup](pubs []*PublicKey[K], msgs [][]byte, aggSig Sig
listG2 := make([]*GG.G2, n+1)
listExp := make([]int, n+1)

listG1[n] = GG.G1Generator()
listG2[n] = GG.G2Generator()
listExp[n] = -1

switch (interface{})(pubs).(type) {
case []*PublicKey[G1]:
for i := range msgs {
Expand All @@ -290,13 +385,10 @@ func VerifyAggregate[K KeyGroup](pubs []*PublicKey[K], msgs [][]byte, aggSig Sig
listExp[i] = 1
}

listG2[n] = new(GG.G2)
err := listG2[n].SetBytes(aggSig)
if err != nil {
return false
}
listG1[n] = GG.G1Generator()
listExp[n] = -1

case []*PublicKey[G2]:
for i := range msgs {
Expand All @@ -308,13 +400,10 @@ func VerifyAggregate[K KeyGroup](pubs []*PublicKey[K], msgs [][]byte, aggSig Sig
listExp[i] = 1
}

listG1[n] = new(GG.G1)
err := listG1[n].SetBytes(aggSig)
if err != nil {
return false
}
listG2[n] = GG.G2Generator()
listExp[n] = -1

default:
panic(ErrInvalid)
Expand All @@ -323,15 +412,3 @@ func VerifyAggregate[K KeyGroup](pubs []*PublicKey[K], msgs [][]byte, aggSig Sig
C := GG.ProdPairFrac(listG1, listG2, listExp)
return C.IsIdentity()
}

var (
ErrInvalid = errors.New("bls: invalid BLS instance")
ErrKeyGen = errors.New("bls: too many unsuccessful key generation tries")
ErrShortIKM = errors.New("bls: IKM material shorter than 32 bytes")
ErrAggregate = errors.New("bls: error while aggregating signatures")
)

const (
dstG1 = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_"
dstG2 = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"
)
31 changes: 31 additions & 0 deletions sign/bls/bls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bls_test
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"encoding"
"fmt"
"testing"
Expand Down Expand Up @@ -80,6 +81,9 @@ func testMarshal[K bls.KeyGroup](
if !bytes.Equal(got, want) {
test.ReportError(t, got, want)
}

err = right.UnmarshalBinary(nil)
test.CheckIsErr(t, err, "should fail: empty input")
}

func testErrors[K bls.KeyGroup](t *testing.T) {
Expand All @@ -93,6 +97,33 @@ func testErrors[K bls.KeyGroup](t *testing.T) {
test.CheckNoErr(t, err, "failed to keygen")
pub := priv.PublicKey()
test.CheckOk(bls.Verify(pub, nil, nil) == false, "should fail: bad signature", t)

// Bad public key
msg := []byte("hello")
sig := bls.Sign[K](priv, msg)
pub = new(bls.PublicKey[K])
test.CheckOk(pub.Validate() == false, "should fail: bad public key", t)
test.CheckOk(bls.Verify(pub, msg, sig) == false, "should fail: bad signature", t)

// Bad private key
priv = new(bls.PrivateKey[K])
test.CheckOk(priv.Validate() == false, "should fail: bad private key", t)
err = test.CheckPanic(func() { bls.Sign(priv, msg) })
test.CheckNoErr(t, err, "sign should panic")

// Wrong comparisons
test.CheckOk(priv.Equal(new(rsa.PrivateKey)) == false, "should fail: bad private key types", t)
test.CheckOk(pub.Equal(new(rsa.PublicKey)) == false, "should fail: bad public key types", t)

// Aggregate nil
_, err = bls.Aggregate[K](*new(K), nil)
test.CheckIsErr(t, err, "should fail: empty signatures")

// VerifyAggregate nil
test.CheckOk(bls.VerifyAggregate([]*bls.PublicKey[K]{}, nil, nil) == false, "should fail: empty keys", t)

// VerifyAggregate empty signature
test.CheckOk(bls.VerifyAggregate([]*bls.PublicKey[K]{pub}, [][]byte{msg}, nil) == false, "should fail: empty signature", t)
}

func testAggregation[K bls.KeyGroup](t *testing.T) {
Expand Down

0 comments on commit 36749b6

Please sign in to comment.