-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bls #105
Bls #105
Changes from 13 commits
8fee622
e47c7f8
082e071
a5dd8ea
78a09e5
535777d
b5ccf24
a785d23
ea62aa9
2854483
97af6ee
9be8b74
221b5d3
200bf49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,88 @@ | ||||||||
package bls12381 | ||||||||
|
||||||||
import ( | ||||||||
"encoding/base64" | ||||||||
"fmt" | ||||||||
"os" | ||||||||
|
||||||||
blst "github.com/supranational/blst/bindings/go" | ||||||||
) | ||||||||
|
||||||||
func aggregatePublicKey(pks []*PubKey) *blst.P1Affine { | ||||||||
pubkeys := make([]*blst.P1Affine, len(pks)) | ||||||||
for i, pk := range pks { | ||||||||
pubkeys[i] = new(blst.P1Affine).Deserialize(pk.Key) | ||||||||
if pubkeys[i] == nil { | ||||||||
panic("Failed to deserialize public key") | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
aggregator := new(blst.P1Aggregate) | ||||||||
b := aggregator.Aggregate(pubkeys, false) | ||||||||
if !b { | ||||||||
panic("Failed to aggregate public keys") | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
apk := aggregator.ToAffine() | ||||||||
|
||||||||
return apk | ||||||||
} | ||||||||
|
||||||||
// AggregateSignature combines a set of verified signatures into a single bls signature | ||||||||
func AggregateSignature(sigs [][]byte) []byte { | ||||||||
sigmas := make([]*blst.P2Affine, len(sigs)) | ||||||||
for i, sig := range sigs { | ||||||||
sigmas[i] = new(blst.P2Affine).Uncompress(sig) | ||||||||
if sigmas[i] == nil { | ||||||||
panic("Failed to deserialize signature") | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
aggregator := new(blst.P2Aggregate) | ||||||||
b := aggregator.Aggregate(sigmas, false) | ||||||||
if !b { | ||||||||
panic("Failed to aggregate signatures") | ||||||||
} | ||||||||
aggSigBytes := aggregator.ToAffine().Compress() | ||||||||
return aggSigBytes | ||||||||
} | ||||||||
|
||||||||
// VerifyMultiSignature assumes public key is already validated | ||||||||
func VerifyMultiSignature(msg []byte, sig []byte, pks []*PubKey) bool { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. usually go There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, thanks, will change. I was just following what they did for secp256k1 and ed25519. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep I've already seen sdk dev loves to panic all around the place. Without any comments on those like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've made changes according to your comments and recommitted. Could you please have a look? Thanks |
||||||||
return VerifyAggregateSignature([][]byte{msg}, sig, [][]*PubKey{pks}) | ||||||||
} | ||||||||
|
||||||||
func Unique(msgs [][]byte) bool { | ||||||||
if len(msgs) <= 1 { | ||||||||
return true | ||||||||
} | ||||||||
msgMap := make(map[string]bool, len(msgs)) | ||||||||
for _, msg := range msgs { | ||||||||
s := base64.StdEncoding.EncodeToString(msg) | ||||||||
if _, ok := msgMap[s]; ok { | ||||||||
return false | ||||||||
} | ||||||||
msgMap[s] = true | ||||||||
} | ||||||||
return true | ||||||||
} | ||||||||
|
||||||||
func VerifyAggregateSignature(msgs [][]byte, sig []byte, pkss [][]*PubKey) bool { | ||||||||
// messages must be pairwise distinct | ||||||||
if !Unique(msgs) { | ||||||||
fmt.Fprintf(os.Stdout, "messages must be pairwise distinct") | ||||||||
return false | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
|
||||||||
apks := make([]*blst.P1Affine, len(pkss)) | ||||||||
for i, pks := range pkss { | ||||||||
apks[i] = aggregatePublicKey(pks) | ||||||||
} | ||||||||
|
||||||||
sigma := new(blst.P2Affine).Uncompress(sig) | ||||||||
if sigma == nil { | ||||||||
panic("Failed to deserialize signature") | ||||||||
} | ||||||||
|
||||||||
dst := []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") | ||||||||
return sigma.AggregateVerify(true, apks, false, msgs, dst) | ||||||||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,118 @@ | ||||
package bls12381_test | ||||
|
||||
import ( | ||||
"fmt" | ||||
"testing" | ||||
|
||||
"github.com/stretchr/testify/assert" | ||||
"github.com/stretchr/testify/require" | ||||
|
||||
bls "github.com/cosmos/cosmos-sdk/crypto/keys/bls12381" | ||||
) | ||||
|
||||
func TestBlsMultiSig(t *testing.T) { | ||||
total := 5 | ||||
pks := make([]*bls.PubKey, total) | ||||
sigs := make([][]byte, total) | ||||
msg := []byte("hello world") | ||||
for i := 0; i < total; i++ { | ||||
sk := bls.GenPrivKey() | ||||
pk, ok := sk.PubKey().(*bls.PubKey) | ||||
require.True(t, ok) | ||||
|
||||
sig, err := sk.Sign(msg) | ||||
require.Nil(t, err) | ||||
|
||||
pks[i] = pk | ||||
sigs[i] = sig | ||||
} | ||||
|
||||
aggSig := bls.AggregateSignature(sigs) | ||||
assert.True(t, bls.VerifyMultiSignature(msg, aggSig, pks)) | ||||
|
||||
} | ||||
|
||||
func TestBlsAggSig(t *testing.T) { | ||||
total := 5 | ||||
//sks := make([]*bls.PrivKey, total) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove if not needed
Suggested change
|
||||
pks := make([][]*bls.PubKey, total) | ||||
sigs := make([][]byte, total) | ||||
msgs := make([][]byte, total) | ||||
for i := 0; i < total; i++ { | ||||
msgs[i] = []byte(fmt.Sprintf("message %d", i)) | ||||
sk := bls.GenPrivKey() | ||||
pk, ok := sk.PubKey().(*bls.PubKey) | ||||
require.True(t, ok) | ||||
|
||||
sig, err := sk.Sign(msgs[i]) | ||||
require.Nil(t, err) | ||||
|
||||
pks[i] = []*bls.PubKey{pk} | ||||
sigs[i] = sig | ||||
} | ||||
|
||||
aggSig := bls.AggregateSignature(sigs) | ||||
assert.True(t, bls.VerifyAggregateSignature(msgs, aggSig, pks)) | ||||
|
||||
} | ||||
|
||||
func benchmarkBlsVerifyMulti(total int, b *testing.B) { | ||||
pks := make([]*bls.PubKey, total) | ||||
sigs := make([][]byte, total) | ||||
msg := []byte("hello world") | ||||
for i := 0; i < total; i++ { | ||||
sk := bls.GenPrivKey() | ||||
pk, ok := sk.PubKey().(*bls.PubKey) | ||||
require.True(b, ok) | ||||
|
||||
sig, err := sk.Sign(msg) | ||||
require.Nil(b, err) | ||||
|
||||
pks[i] = pk | ||||
sigs[i] = sig | ||||
} | ||||
|
||||
aggSig := bls.AggregateSignature(sigs) | ||||
|
||||
b.ResetTimer() | ||||
for i := 0; i < b.N; i++ { | ||||
bls.VerifyMultiSignature(msg, aggSig, pks) | ||||
} | ||||
} | ||||
|
||||
func BenchmarkBlsVerifyMulti8(b *testing.B) { benchmarkBlsVerifyMulti(8, b) } | ||||
func BenchmarkBlsVerifyMulti16(b *testing.B) { benchmarkBlsVerifyMulti(16, b) } | ||||
func BenchmarkBlsVerifyMulti32(b *testing.B) { benchmarkBlsVerifyMulti(32, b) } | ||||
func BenchmarkBlsVerifyMulti64(b *testing.B) { benchmarkBlsVerifyMulti(64, b) } | ||||
func BenchmarkBlsVerifyMulti128(b *testing.B) { benchmarkBlsVerifyMulti(128, b) } | ||||
|
||||
func benchmarkBlsVerifyAgg(total int, b *testing.B) { | ||||
pks := make([][]*bls.PubKey, total) | ||||
sigs := make([][]byte, total) | ||||
msgs := make([][]byte, total) | ||||
for i := 0; i < total; i++ { | ||||
msgs[i] = []byte(fmt.Sprintf("message %d", i)) | ||||
sk := bls.GenPrivKey() | ||||
pk, ok := sk.PubKey().(*bls.PubKey) | ||||
require.True(b, ok) | ||||
|
||||
sig, err := sk.Sign(msgs[i]) | ||||
require.Nil(b, err) | ||||
|
||||
pks[i] = []*bls.PubKey{pk} | ||||
sigs[i] = sig | ||||
} | ||||
|
||||
aggSig := bls.AggregateSignature(sigs) | ||||
b.ResetTimer() | ||||
|
||||
for i := 0; i < b.N; i++ { | ||||
bls.VerifyAggregateSignature(msgs, aggSig, pks) | ||||
} | ||||
} | ||||
|
||||
func BenchmarkBlsVerifyAgg8(b *testing.B) { benchmarkBlsVerifyAgg(8, b) } | ||||
func BenchmarkBlsVerifyAgg16(b *testing.B) { benchmarkBlsVerifyAgg(16, b) } | ||||
func BenchmarkBlsVerifyAgg32(b *testing.B) { benchmarkBlsVerifyAgg(32, b) } | ||||
func BenchmarkBlsVerifyAgg64(b *testing.B) { benchmarkBlsVerifyAgg(64, b) } | ||||
func BenchmarkBlsVerifyAgg128(b *testing.B) { benchmarkBlsVerifyAgg(128, b) } |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,6 +23,7 @@ message BaseAccount { | |||||||||
[(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; | ||||||||||
uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; | ||||||||||
uint64 sequence = 4; | ||||||||||
bool pop_is_valid = 5; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
// ModuleAccount defines an account for modules that holds coins on a pool. | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -289,6 +289,40 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul | |
return next(ctx, tx, simulate) | ||
} | ||
|
||
// SetPopValidDecorator handles the validation status of the proof-of-possession (POP) of an individual public key. | ||
// A valid transaction and signature can be viewed as a POP for the signer's public key. | ||
// POP is required when forming a compact multisig group in order to prevent rogue public key attacks. | ||
type SetPopValidDecorator struct { | ||
ak AccountKeeper | ||
} | ||
|
||
func NewSetPopValidDecorator(ak AccountKeeper) SetPopValidDecorator { | ||
return SetPopValidDecorator{ | ||
ak: ak, | ||
} | ||
} | ||
|
||
func (spvd SetPopValidDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { | ||
sigTx, ok := tx.(authsigning.SigVerifiableTx) | ||
if !ok { | ||
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") | ||
} | ||
|
||
for _, addr := range sigTx.GetSigners() { | ||
acc := spvd.ak.GetAccount(ctx, addr) | ||
pk := acc.GetPubKey() | ||
|
||
switch pk.(type) { | ||
case *bls12381.PubKey, *secp256k1.PubKey, *ed25519.PubKey: | ||
if err := acc.SetPopValid(true); err != nil { | ||
panic(err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return error instead of |
||
} | ||
} | ||
} | ||
|
||
return next(ctx, tx, simulate) | ||
} | ||
|
||
// IncrementSequenceDecorator handles incrementing sequences of all signers. | ||
// Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note, | ||
// there is no need to execute IncrementSequenceDecorator on RecheckTX since | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.