Skip to content

Commit

Permalink
refactor(crypto): decode data to point on verification (#1339)
Browse files Browse the repository at this point in the history
  • Loading branch information
b00f authored Jun 12, 2024
1 parent 4beeda5 commit 268fd72
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 132 deletions.
32 changes: 23 additions & 9 deletions crypto/bls/bls.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,26 @@ func SignatureAggregate(sigs ...*Signature) *Signature {
return nil
}
g1 := bls12381.NewG1()
aggPointG1 := sigs[0].pointG1
aggPointG1, err := sigs[0].PointG1()
if err != nil {
return nil
}
for i := 1; i < len(sigs); i++ {
s, err := sigs[i].PointG1()
if err != nil {
return nil
}
g1.Add(
&aggPointG1,
&aggPointG1,
&sigs[i].pointG1)
&s)
}

data := g1.ToCompressed(&aggPointG1)

return &Signature{
pointG1: aggPointG1,
data: data,
pointG1: &aggPointG1,
}
}

Expand All @@ -31,18 +41,22 @@ func PublicKeyAggregate(pubs ...*PublicKey) *PublicKey {
return nil
}
g2 := bls12381.NewG2()
aggPointG2 := pubs[0].pointG2
aggPointG2, err := pubs[0].PointG2()
if err != nil {
return nil
}
for i := 1; i < len(pubs); i++ {
if g2.IsZero(&pubs[i].pointG2) {
return nil
}
pointG2, _ := pubs[i].PointG2()
g2.Add(
&aggPointG2,
&aggPointG2,
&pubs[i].pointG2)
&pointG2)
}

data := g2.ToCompressed(&aggPointG2)

return &PublicKey{
pointG2: aggPointG2,
data: data,
pointG2: &aggPointG2,
}
}
25 changes: 13 additions & 12 deletions crypto/bls/bls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestSigning(t *testing.T) {
sig1 := prv.Sign(msg)
assert.Equal(t, sig1.Bytes(), sig.Bytes())
assert.NoError(t, pub.Verify(msg, sig))
assert.Equal(t, prv.PublicKey(), pub)
assert.Equal(t, pub.ValidatorAddress(), addr)
}

Expand Down Expand Up @@ -84,18 +85,18 @@ func TestAggregateFailed(t *testing.T) {
assert.Error(t, pub2.Verify(msg1, agg1))
assert.Error(t, pub3.Verify(msg1, agg1))

assert.Nil(t, pubAgg1.Verify(msg1, agg1))
assert.NotNil(t, pubAgg1.Verify(msg2, agg1))
assert.NotNil(t, pubAgg1.Verify(msg1, agg2))
assert.NotNil(t, pubAgg2.Verify(msg1, agg1))
assert.Nil(t, pubAgg2.Verify(msg1, agg2))
assert.NotNil(t, pubAgg2.Verify(msg2, agg2))
assert.NotNil(t, pubAgg1.Verify(msg1, agg3))
assert.NotNil(t, pubAgg1.Verify(msg2, agg3))
assert.NotNil(t, pubAgg1.Verify(msg1, agg4))
assert.NotNil(t, pubAgg3.Verify(msg1, agg1))
assert.Nil(t, pubAgg1.Verify(msg1, agg5))
assert.Nil(t, pubAgg4.Verify(msg1, agg1))
assert.NoError(t, pubAgg1.Verify(msg1, agg1))
assert.Error(t, pubAgg1.Verify(msg2, agg1))
assert.Error(t, pubAgg1.Verify(msg1, agg2))
assert.Error(t, pubAgg2.Verify(msg1, agg1))
assert.NoError(t, pubAgg2.Verify(msg1, agg2))
assert.Error(t, pubAgg2.Verify(msg2, agg2))
assert.Error(t, pubAgg1.Verify(msg1, agg3))
assert.Error(t, pubAgg1.Verify(msg2, agg3))
assert.Error(t, pubAgg1.Verify(msg1, agg4))
assert.Error(t, pubAgg3.Verify(msg1, agg1))
assert.NoError(t, pubAgg1.Verify(msg1, agg5))
assert.NoError(t, pubAgg4.Verify(msg1, agg1))
}

func TestAggregateOnlyOneSignature(t *testing.T) {
Expand Down
17 changes: 11 additions & 6 deletions crypto/bls/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ func PrivateKeyFromString(text string) (*PrivateKey, error) {
// Decode the bech32m encoded private key.
hrp, typ, data, err := bech32m.DecodeToBase256WithTypeNoLimit(text)
if err != nil {
return nil, errors.Errorf(errors.ErrInvalidPrivateKey, err.Error())
return nil, err
}

// Check if hrp is valid
if hrp != crypto.PrivateKeyHRP {
return nil, errors.Errorf(errors.ErrInvalidPrivateKey,
"invalid hrp: %v", hrp)
return nil, crypto.InvalidHRPError(hrp)
}

if typ != crypto.SignatureTypeBLS {
Expand Down Expand Up @@ -136,18 +135,24 @@ func (prv *PrivateKey) SignNative(msg []byte) *Signature {
if err != nil {
panic(err)
}
s := g1.MulScalar(g1.New(), q, &prv.fr)
pointG1 := g1.MulScalar(g1.New(), q, &prv.fr)
data := g1.ToCompressed(pointG1)

return &Signature{pointG1: *s}
return &Signature{
data: data,
pointG1: pointG1,
}
}

func (prv *PrivateKey) PublicKeyNative() *PublicKey {
g2 := bls12381.NewG2()

pointG2 := g2.MulScalar(g2.New(), g2.One(), &prv.fr)
data := g2.ToCompressed(pointG2)

return &PublicKey{
pointG2: *pointG2,
data: data,
pointG2: pointG2,
}
}

Expand Down
4 changes: 1 addition & 3 deletions crypto/bls/private_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"testing"

"github.com/pactus-project/pactus/crypto/bls"
"github.com/pactus-project/pactus/util/errors"
"github.com/pactus-project/pactus/util/testsuite"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -56,7 +55,7 @@ func TestPrivateKeyToString(t *testing.T) {
false, nil,
},
{
"invalid hrp: xxx",
"invalid HRP: xxx",
"XXX1PDRWTLP5PX0FAHDX39GXZJP7FKZFALML0D5U9TT9KVQHDUC99CMGQMUUMJT",
false, nil,
},
Expand Down Expand Up @@ -101,7 +100,6 @@ func TestPrivateKeyToString(t *testing.T) {
assert.Equal(t, prv.Bytes(), test.result, "test %v: invalid bytes", no)
assert.Equal(t, prv.String(), strings.ToUpper(test.encoded), "test %v: invalid encoded", no)
} else {
assert.Equal(t, errors.Code(err), errors.ErrInvalidPrivateKey, "test %v: invalid error code", no)
assert.Contains(t, err.Error(), test.errMsg, "test %v: error not matched", no)
}
}
Expand Down
82 changes: 48 additions & 34 deletions crypto/bls/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bls

import (
"bytes"
"fmt"
"io"

cbor "github.com/fxamacker/cbor/v2"
Expand All @@ -18,7 +19,8 @@ var _ crypto.PublicKey = &PublicKey{}
const PublicKeySize = 96

type PublicKey struct {
pointG2 bls12381.PointG2
pointG2 *bls12381.PointG2 // Lazily initialized point on G2.
data []byte // Raw public key data.
}

// PublicKeyFromString decodes the string encoding of a BLS public key
Expand All @@ -27,12 +29,12 @@ func PublicKeyFromString(text string) (*PublicKey, error) {
// Decode the bech32m encoded public key.
hrp, typ, data, err := bech32m.DecodeToBase256WithTypeNoLimit(text)
if err != nil {
return nil, errors.Errorf(errors.ErrInvalidPublicKey, err.Error())
return nil, err
}

// Check if hrp is valid
if hrp != crypto.PublicKeyHRP {
return nil, errors.Errorf(errors.ErrInvalidPublicKey, "invalid hrp: %v", hrp)
return nil, crypto.InvalidHRPError(hrp)
}

if typ != crypto.SignatureTypeBLS {
Expand All @@ -48,24 +50,13 @@ func PublicKeyFromBytes(data []byte) (*PublicKey, error) {
return nil, errors.Errorf(errors.ErrInvalidPublicKey,
"public key should be %d bytes, but it is %v bytes", PublicKeySize, len(data))
}
g2 := bls12381.NewG2()

pointG2, err := g2.FromCompressed(data)
if err != nil {
return nil, errors.Errorf(errors.ErrInvalidPublicKey, err.Error())
}
if g2.IsZero(pointG2) {
return nil, errors.Errorf(errors.ErrInvalidPublicKey,
"public key is zero")
}

return &PublicKey{pointG2: *pointG2}, nil
return &PublicKey{data: data}, nil
}

// Bytes returns the raw byte representation of the public key.
func (pub *PublicKey) Bytes() []byte {
g2 := bls12381.NewG2()

return g2.ToCompressed(pub.point())
return pub.data
}

// String returns a human-readable string for the BLS public key.
Expand All @@ -78,10 +69,12 @@ func (pub *PublicKey) String() string {
return str
}

// MarshalCBOR encodes the public key into CBOR format.
func (pub *PublicKey) MarshalCBOR() ([]byte, error) {
return cbor.Marshal(pub.Bytes())
}

// UnmarshalCBOR decodes the public key from CBOR format.
func (pub *PublicKey) UnmarshalCBOR(bs []byte) error {
var data []byte
if err := cbor.Unmarshal(bs, &data); err != nil {
Expand All @@ -91,21 +84,20 @@ func (pub *PublicKey) UnmarshalCBOR(bs []byte) error {
return pub.Decode(bytes.NewReader(data))
}

// Encode writes the raw bytes of the public key to the provided writer.
func (pub *PublicKey) Encode(w io.Writer) error {
return encoding.WriteElements(w, pub.Bytes())
}

// Decode reads the raw bytes of the public key from the provided reader and initializes the public key.
func (pub *PublicKey) Decode(r io.Reader) error {
data := make([]byte, PublicKeySize)
err := encoding.ReadElements(r, data)
if err != nil {
return err
}

p, err := PublicKeyFromBytes(data)
if err != nil {
return err
}
p, _ := PublicKeyFromBytes(data)
*pub = *p

return nil
Expand All @@ -118,21 +110,26 @@ func (pub *PublicKey) Verify(msg []byte, sig crypto.Signature) error {
return errors.Error(errors.ErrInvalidSignature)
}
g1 := bls12381.NewG1()
g2 := bls12381.NewG2()

r := sig.(*Signature)
if g1.IsZero(&r.pointG1) {
return errors.Errorf(errors.ErrInvalidSignature,
"signature is zero")
pointG1, err := r.PointG1()
if err != nil {
return err
}
pointG2, err := pub.PointG2()
if err != nil {
return err
}
q, err := g1.HashToCurve(msg, dst)
if err != nil {
panic(err)
return err
}
g2one := bls12381.NewG2().New().Set(&bls12381.G2One)
g2one := g2.New().Set(&bls12381.G2One)

eng := bls12381.NewEngine()
eng.AddPair(q, pub.point())
eng.AddPairInv(&r.pointG1, g2one)
eng.AddPair(q, &pointG2)
eng.AddPairInv(&pointG1, g2one)

if !eng.Check() {
return crypto.ErrInvalidSignature
Expand All @@ -141,26 +138,28 @@ func (pub *PublicKey) Verify(msg []byte, sig crypto.Signature) error {
return nil
}

// EqualsTo checks if the current public key is equal to another public key.
func (pub *PublicKey) EqualsTo(right crypto.PublicKey) bool {
g2 := bls12381.NewG2()

return g2.Equal(pub.point(), right.(*PublicKey).point())
return bytes.Equal(pub.data, right.(*PublicKey).data)
}

// AccountAddress returns the account address derived from the public key.
func (pub *PublicKey) AccountAddress() crypto.Address {
data := hash.Hash160(hash.Hash256(pub.Bytes()))
addr := crypto.NewAddress(crypto.AddressTypeBLSAccount, data)

return addr
}

// ValidatorAddress returns the validator address derived from the public key.
func (pub *PublicKey) ValidatorAddress() crypto.Address {
data := hash.Hash160(hash.Hash256(pub.Bytes()))
addr := crypto.NewAddress(crypto.AddressTypeValidator, data)

return addr
}

// VerifyAddress checks if the provided address matches the derived address from the public key.
func (pub *PublicKey) VerifyAddress(addr crypto.Address) error {
if addr.IsValidatorAddress() {
if addr != pub.ValidatorAddress() {
Expand All @@ -181,7 +180,22 @@ func (pub *PublicKey) VerifyAddress(addr crypto.Address) error {
return nil
}

// clonePoint clones the pointG2 to make sure it remains intact.
func (pub *PublicKey) point() *bls12381.PointG2 {
return bls12381.NewG2().New().Set(&pub.pointG2)
// PointG2 returns the point on G2 for the public key.
func (pub *PublicKey) PointG2() (bls12381.PointG2, error) {
if pub.pointG2 != nil {
return *pub.pointG2, nil
}

g2 := bls12381.NewG2()
pointG2, err := g2.FromCompressed(pub.data)
if err != nil {
return bls12381.PointG2{}, err
}
if g2.IsZero(pointG2) {
return bls12381.PointG2{}, fmt.Errorf("public key is zero")
}

pub.pointG2 = pointG2

return *pointG2, nil
}
Loading

0 comments on commit 268fd72

Please sign in to comment.