From eb8905b243e691ea35cc3f9f3edc90d121cdfe9e Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Sat, 5 Mar 2022 10:25:49 +0000 Subject: [PATCH 1/8] implement Signer interface for bls12381 --- asserter/construction.go | 4 +- keys/errors.go | 3 + keys/keys.go | 19 ++++++ keys/signer_bls12381.go | 121 +++++++++++++++++++++++++++++++++++ keys/signer_bls12381_test.go | 113 ++++++++++++++++++++++++++++++++ types/curve_type.go | 1 + types/signature_type.go | 1 + 7 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 keys/signer_bls12381.go create mode 100644 keys/signer_bls12381_test.go diff --git a/asserter/construction.go b/asserter/construction.go index 9bbefbf3..449c64c9 100644 --- a/asserter/construction.go +++ b/asserter/construction.go @@ -215,7 +215,7 @@ func CurveType( curve types.CurveType, ) error { switch curve { - case types.Secp256k1, types.Secp256r1, types.Edwards25519, types.Tweedle, types.Pallas: + case types.Secp256k1, types.Secp256r1, types.Edwards25519, types.Tweedle, types.Pallas, types.Bls12381: return nil default: return fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, curve) @@ -304,7 +304,7 @@ func SignatureType( signature types.SignatureType, ) error { switch signature { - case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon: + case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon, types.BlsG2Element: return nil default: return fmt.Errorf("%w: %s", ErrSignatureTypeNotSupported, signature) diff --git a/keys/errors.go b/keys/errors.go index fa4678bb..0fff3b9f 100644 --- a/keys/errors.go +++ b/keys/errors.go @@ -39,6 +39,9 @@ var ( ErrKeyGenPallasFailed = errors.New( "keygen: error generating key pair for pallas curve type", ) + ErrKeyGenBls12381Failed = errors.New( + "keygen: error generating key pair for bls12381 curve type", + ) ErrCurveTypeNotSupported = errors.New("not a supported CurveType") ErrSignUnsupportedPayloadSignatureType = errors.New( diff --git a/keys/keys.go b/keys/keys.go index 22a15641..1b143cd3 100644 --- a/keys/keys.go +++ b/keys/keys.go @@ -24,6 +24,7 @@ import ( "math/big" "github.com/btcsuite/btcd/btcec" + "github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig" "github.com/coinbase/kryptology/pkg/signatures/schnorr/mina" "github.com/coinbase/rosetta-sdk-go/asserter" @@ -210,6 +211,22 @@ func GenerateKeypair(curve types.CurveType) (*KeyPair, error) { CurveType: curve, } + keyPair = &KeyPair{ + PublicKey: pubKey, + PrivateKey: rawPrivKeyBytes, + } + case types.Bls12381: + rawPubKey, rawPrivKey, err := bls_sig.NewSigBasic().Keygen() + rawPubKeyBytes, _ := rawPubKey.MarshalBinary() + rawPrivKeyBytes, _ := rawPrivKey.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrKeyGenBls12381Failed, err) + } + pubKey := &types.PublicKey{ + Bytes: rawPubKeyBytes, + CurveType: curve, + } + keyPair = &KeyPair{ PublicKey: pubKey, PrivateKey: rawPrivKeyBytes, @@ -252,6 +269,8 @@ func (k *KeyPair) Signer() (Signer, error) { return &SignerSecp256r1{k}, nil case types.Pallas: return &SignerPallas{k}, nil + case types.Bls12381: + return &SignerBls12381{k}, nil default: return nil, fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, k.PublicKey.CurveType) } diff --git a/keys/signer_bls12381.go b/keys/signer_bls12381.go new file mode 100644 index 00000000..a74ea4d9 --- /dev/null +++ b/keys/signer_bls12381.go @@ -0,0 +1,121 @@ +// Copyright 2022 Coinbase, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "errors" + "fmt" + + "github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig" + "github.com/coinbase/rosetta-sdk-go/asserter" + "github.com/coinbase/rosetta-sdk-go/types" +) + +var ErrBlsTransactionValidationErr = errors.New("transaction with bls validation failed") + +type SignerBls12381 struct { + KeyPair *KeyPair +} + +func (s *SignerBls12381) PublicKey() *types.PublicKey { + return s.KeyPair.PublicKey +} + +// Sign transaction payloads using a KeyPair +func (s *SignerBls12381) Sign( + payload *types.SigningPayload, + sigType types.SignatureType, +) (*types.Signature, error) { + err := s.KeyPair.IsValid() + if err != nil { + return nil, err + } + + if !(payload.SignatureType == types.BlsG2Element || payload.SignatureType == "") { + return nil, fmt.Errorf( + "%w: expected %v but got %v", + ErrSignUnsupportedPayloadSignatureType, + types.BlsG2Element, + payload.SignatureType, + ) + } + + if sigType != types.BlsG2Element { + return nil, fmt.Errorf( + "%w: expected %v but got %v", + ErrSignUnsupportedSignatureType, + types.BlsG2Element, + sigType, + ) + } + + // Generate private key bytes + privKeyBytes := s.KeyPair.PrivateKey + privKey := &bls_sig.SecretKey{} + _ = privKey.UnmarshalBinary(privKeyBytes) + + bls := bls_sig.NewSigBasic() + sig, err := bls.Sign(privKey, payload.Bytes) + if err != nil { + return nil, err + } + sigBytes, _ := sig.MarshalBinary() + + return &types.Signature{ + SigningPayload: payload, + PublicKey: s.KeyPair.PublicKey, + SignatureType: payload.SignatureType, + Bytes: sigBytes, + }, nil +} + +// Verify verifies a Signature, by checking the validity of a Signature, +// the SigningPayload, and the PublicKey of the Signature. +func (s *SignerBls12381) Verify(signature *types.Signature) error { + if signature.SignatureType != types.BlsG2Element { + return fmt.Errorf( + "%w: expected %v but got %v", + ErrVerifyUnsupportedPayloadSignatureType, + types.BlsG2Element, + signature.SignatureType, + ) + } + + pubKeyBytes := signature.PublicKey.Bytes + pubKey := &bls_sig.PublicKey{} + _ = pubKey.UnmarshalBinary(pubKeyBytes) + + sigBytes := signature.Bytes + sig := &bls_sig.Signature{} + _ = sig.UnmarshalBinary(sigBytes) + + err := asserter.Signatures([]*types.Signature{signature}) + if err != nil { + return fmt.Errorf("%w: %s", ErrVerifyFailed, err) + } + + bls := bls_sig.NewSigBasic() + result, err := bls.Verify(pubKey, signature.SigningPayload.Bytes, sig) + + if err != nil { + return err + } + + if !result { + return fmt.Errorf("%w: %s", ErrVerifyFailed, "Verify failed") + } + + return nil +} diff --git a/keys/signer_bls12381_test.go b/keys/signer_bls12381_test.go new file mode 100644 index 00000000..eb0dd565 --- /dev/null +++ b/keys/signer_bls12381_test.go @@ -0,0 +1,113 @@ +// Copyright 2022 Coinbase, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/coinbase/rosetta-sdk-go/types" +) + +var signerBls12381 Signer + +func init() { + bls12381Keypair, _ := GenerateKeypair(types.Bls12381) + signerBls12381, _ = bls12381Keypair.Signer() + + unsignedTxStr := "a7ca4bce10200d073ef10c46e9d27c3b4e31263d4c07fbec447650fcc1b286" + + "300e8ecf25c0560f9cb5aa673247fb6a6f95fb73164d1bf5d41288f57ea40517d9da86c44a289ed" + + "2b6f9d3bf9a750ee96dbf905073f8ae56c80100ef47f5585acf70baff8c72c3b8a833181fb3edf4" + + "328fccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb" + txnBytes = []byte(unsignedTxStr) +} + +func TestSignBls12381(t *testing.T) { + type payloadTest struct { + payload *types.SigningPayload + err bool + errMsg error + } + + var payloadTests = []payloadTest{ + {mockPayload(txnBytes, types.BlsG2Element), false, nil}, + {mockPayload(txnBytes, ""), false, nil}, + {mockPayload(txnBytes, types.Ecdsa), true, ErrSignUnsupportedPayloadSignatureType}, + { + mockPayload(txnBytes, types.EcdsaRecovery), + true, + ErrSignUnsupportedPayloadSignatureType, + }, + } + for _, test := range payloadTests { + signature, err := signerBls12381.Sign(test.payload, types.BlsG2Element) + + if !test.err { + assert.NoError(t, err) + assert.Len(t, signature.Bytes, 96) + assert.Equal(t, signerBls12381.PublicKey(), signature.PublicKey) + } else { + assert.Contains(t, err.Error(), test.errMsg.Error()) + } + } +} + +func TestVerifyBls(t *testing.T) { + type signatureTest struct { + signature *types.Signature + errMsg error + } + + payload := mockPayload(txnBytes, types.BlsG2Element) + testSignature, err := signerBls12381.Sign(payload, types.BlsG2Element) + assert.NoError(t, err) + + simpleBytes := make([]byte, 32) + copy(simpleBytes, "hello") + + var signatureTests = []signatureTest{ + {mockSignature( + types.Ecdsa, + signerBls12381.PublicKey(), + txnBytes, + simpleBytes), ErrVerifyUnsupportedPayloadSignatureType}, + {mockSignature( + types.EcdsaRecovery, + signerBls12381.PublicKey(), + txnBytes, + simpleBytes), ErrVerifyUnsupportedPayloadSignatureType}, + {mockSignature( + types.BlsG2Element, + signerBls12381.PublicKey(), + simpleBytes, + testSignature.Bytes), ErrVerifyFailed}, + } + + for _, test := range signatureTests { + err := signerBls12381.Verify(test.signature) + assert.Contains(t, err.Error(), test.errMsg.Error()) + } + + // happy path + goodSignature := mockSignature( + types.BlsG2Element, + signerBls12381.PublicKey(), + txnBytes, + testSignature.Bytes, + ) + + assert.Equal(t, nil, signerBls12381.Verify(goodSignature)) +} diff --git a/types/curve_type.go b/types/curve_type.go index 4100c9c0..0239f411 100644 --- a/types/curve_type.go +++ b/types/curve_type.go @@ -27,6 +27,7 @@ type CurveType string // List of CurveType const ( + Bls12381 CurveType = "bls12381" Secp256k1 CurveType = "secp256k1" Secp256r1 CurveType = "secp256r1" Edwards25519 CurveType = "edwards25519" diff --git a/types/signature_type.go b/types/signature_type.go index 42cfb1f2..e2c10f3b 100644 --- a/types/signature_type.go +++ b/types/signature_type.go @@ -29,6 +29,7 @@ type SignatureType string // List of SignatureType const ( + BlsG2Element SignatureType = "bls12381_g2_element" Ecdsa SignatureType = "ecdsa" EcdsaRecovery SignatureType = "ecdsa_recovery" Ed25519 SignatureType = "ed25519" From c1cfde60739d8a80592a5593a5e6795c8ffbe23c Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Sun, 6 Mar 2022 06:27:18 +0000 Subject: [PATCH 2/8] correctly decode hex string to bytes --- keys/signer_bls12381_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/keys/signer_bls12381_test.go b/keys/signer_bls12381_test.go index eb0dd565..5ddd0414 100644 --- a/keys/signer_bls12381_test.go +++ b/keys/signer_bls12381_test.go @@ -15,6 +15,7 @@ package keys import ( + "encoding/hex" "testing" "github.com/stretchr/testify/assert" @@ -23,16 +24,17 @@ import ( ) var signerBls12381 Signer +var payloadBytes []byte func init() { bls12381Keypair, _ := GenerateKeypair(types.Bls12381) signerBls12381, _ = bls12381Keypair.Signer() - unsignedTxStr := "a7ca4bce10200d073ef10c46e9d27c3b4e31263d4c07fbec447650fcc1b286" + + unsignedPayloadStr := "a7ca4bce10200d073ef10c46e9d27c3b4e31263d4c07fbec447650fcc1b286" + "300e8ecf25c0560f9cb5aa673247fb6a6f95fb73164d1bf5d41288f57ea40517d9da86c44a289ed" + "2b6f9d3bf9a750ee96dbf905073f8ae56c80100ef47f5585acf70baff8c72c3b8a833181fb3edf4" + "328fccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb" - txnBytes = []byte(unsignedTxStr) + payloadBytes, _ = hex.DecodeString(unsignedPayloadStr) } func TestSignBls12381(t *testing.T) { @@ -43,11 +45,11 @@ func TestSignBls12381(t *testing.T) { } var payloadTests = []payloadTest{ - {mockPayload(txnBytes, types.BlsG2Element), false, nil}, - {mockPayload(txnBytes, ""), false, nil}, - {mockPayload(txnBytes, types.Ecdsa), true, ErrSignUnsupportedPayloadSignatureType}, + {mockPayload(payloadBytes, types.BlsG2Element), false, nil}, + {mockPayload(payloadBytes, ""), false, nil}, + {mockPayload(payloadBytes, types.Ecdsa), true, ErrSignUnsupportedPayloadSignatureType}, { - mockPayload(txnBytes, types.EcdsaRecovery), + mockPayload(payloadBytes, types.EcdsaRecovery), true, ErrSignUnsupportedPayloadSignatureType, }, @@ -71,7 +73,7 @@ func TestVerifyBls(t *testing.T) { errMsg error } - payload := mockPayload(txnBytes, types.BlsG2Element) + payload := mockPayload(payloadBytes, types.BlsG2Element) testSignature, err := signerBls12381.Sign(payload, types.BlsG2Element) assert.NoError(t, err) @@ -82,12 +84,12 @@ func TestVerifyBls(t *testing.T) { {mockSignature( types.Ecdsa, signerBls12381.PublicKey(), - txnBytes, + payloadBytes, simpleBytes), ErrVerifyUnsupportedPayloadSignatureType}, {mockSignature( types.EcdsaRecovery, signerBls12381.PublicKey(), - txnBytes, + payloadBytes, simpleBytes), ErrVerifyUnsupportedPayloadSignatureType}, {mockSignature( types.BlsG2Element, @@ -105,7 +107,7 @@ func TestVerifyBls(t *testing.T) { goodSignature := mockSignature( types.BlsG2Element, signerBls12381.PublicKey(), - txnBytes, + payloadBytes, testSignature.Bytes, ) From b8b151587f166a1ccfb3c2ca691095a6433da2c3 Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Sun, 6 Mar 2022 08:02:57 +0000 Subject: [PATCH 3/8] support both basic and aug mpl --- asserter/construction.go | 2 +- keys/signer_bls12381.go | 112 ++++++++++++++++++++++++++--------- keys/signer_bls12381_test.go | 100 ++++++++++++++++++++++++++++--- types/signature_type.go | 13 ++-- 4 files changed, 183 insertions(+), 44 deletions(-) diff --git a/asserter/construction.go b/asserter/construction.go index 449c64c9..2640b9ec 100644 --- a/asserter/construction.go +++ b/asserter/construction.go @@ -304,7 +304,7 @@ func SignatureType( signature types.SignatureType, ) error { switch signature { - case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon, types.BlsG2Element: + case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon, types.Bls12381BasicMpl, types.Bls12381AugMpl: return nil default: return fmt.Errorf("%w: %s", ErrSignatureTypeNotSupported, signature) diff --git a/keys/signer_bls12381.go b/keys/signer_bls12381.go index a74ea4d9..1f69128e 100644 --- a/keys/signer_bls12381.go +++ b/keys/signer_bls12381.go @@ -43,56 +43,80 @@ func (s *SignerBls12381) Sign( return nil, err } - if !(payload.SignatureType == types.BlsG2Element || payload.SignatureType == "") { + if !(payload.SignatureType == types.Bls12381BasicMpl || payload.SignatureType == types.Bls12381AugMpl || payload.SignatureType == "") { return nil, fmt.Errorf( - "%w: expected %v but got %v", + "%w: expected %v or %v but got %v", ErrSignUnsupportedPayloadSignatureType, - types.BlsG2Element, + types.Bls12381BasicMpl, + types.Bls12381AugMpl, payload.SignatureType, ) } - if sigType != types.BlsG2Element { + // Generate private key bytes + privKeyBytes := s.KeyPair.PrivateKey + privKey := &bls_sig.SecretKey{} + _ = privKey.UnmarshalBinary(privKeyBytes) + + switch sigType { + case types.Bls12381BasicMpl: + sigBytes, _ := signBasic(privKey, payload, s.KeyPair.PublicKey) + + return &types.Signature{ + SigningPayload: payload, + PublicKey: s.KeyPair.PublicKey, + SignatureType: payload.SignatureType, + Bytes: sigBytes, + }, nil + case types.Bls12381AugMpl: + sigBytes, _ := signAug(privKey, payload, s.KeyPair.PublicKey) + + return &types.Signature{ + SigningPayload: payload, + PublicKey: s.KeyPair.PublicKey, + SignatureType: payload.SignatureType, + Bytes: sigBytes, + }, nil + default: return nil, fmt.Errorf( - "%w: expected %v but got %v", + "%w: expected %v or %v but got %v", ErrSignUnsupportedSignatureType, - types.BlsG2Element, + types.Bls12381BasicMpl, + types.Bls12381AugMpl, sigType, ) } +} - // Generate private key bytes - privKeyBytes := s.KeyPair.PrivateKey - privKey := &bls_sig.SecretKey{} - _ = privKey.UnmarshalBinary(privKeyBytes) - +func signBasic(sk *bls_sig.SecretKey, payload *types.SigningPayload, pk *types.PublicKey) ([]byte, error) { bls := bls_sig.NewSigBasic() - sig, err := bls.Sign(privKey, payload.Bytes) + + sig, err := bls.Sign(sk, payload.Bytes) + + if err != nil { + return nil, err + } + sigBytes, _ := sig.MarshalBinary() + + return sigBytes, nil +} + +func signAug(sk *bls_sig.SecretKey, payload *types.SigningPayload, pk *types.PublicKey) ([]byte, error) { + bls := bls_sig.NewSigAug() + + sig, err := bls.Sign(sk, payload.Bytes) + if err != nil { return nil, err } sigBytes, _ := sig.MarshalBinary() - return &types.Signature{ - SigningPayload: payload, - PublicKey: s.KeyPair.PublicKey, - SignatureType: payload.SignatureType, - Bytes: sigBytes, - }, nil + return sigBytes, nil } // Verify verifies a Signature, by checking the validity of a Signature, // the SigningPayload, and the PublicKey of the Signature. func (s *SignerBls12381) Verify(signature *types.Signature) error { - if signature.SignatureType != types.BlsG2Element { - return fmt.Errorf( - "%w: expected %v but got %v", - ErrVerifyUnsupportedPayloadSignatureType, - types.BlsG2Element, - signature.SignatureType, - ) - } - pubKeyBytes := signature.PublicKey.Bytes pubKey := &bls_sig.PublicKey{} _ = pubKey.UnmarshalBinary(pubKeyBytes) @@ -106,8 +130,40 @@ func (s *SignerBls12381) Verify(signature *types.Signature) error { return fmt.Errorf("%w: %s", ErrVerifyFailed, err) } + switch signature.SignatureType { + case types.Bls12381BasicMpl: + return verifyBasic(pubKey, signature.SigningPayload.Bytes, sig) + case types.Bls12381AugMpl: + return verifyAug(pubKey, signature.SigningPayload.Bytes, sig) + default: + return fmt.Errorf( + "%w: expected %v or %v but got %v", + ErrVerifyUnsupportedPayloadSignatureType, + types.Bls12381BasicMpl, + types.Bls12381AugMpl, + signature.SignatureType, + ) + } +} + +func verifyBasic(pubKey *bls_sig.PublicKey, payload []byte, signature *bls_sig.Signature) error { bls := bls_sig.NewSigBasic() - result, err := bls.Verify(pubKey, signature.SigningPayload.Bytes, sig) + result, err := bls.Verify(pubKey, payload, signature) + + if err != nil { + return err + } + + if !result { + return fmt.Errorf("%w: %s", ErrVerifyFailed, "Verify failed") + } + + return nil +} + +func verifyAug(pubKey *bls_sig.PublicKey, payload []byte, signature *bls_sig.Signature) error { + bls := bls_sig.NewSigAug() + result, err := bls.Verify(pubKey, payload, signature) if err != nil { return err diff --git a/keys/signer_bls12381_test.go b/keys/signer_bls12381_test.go index 5ddd0414..edfbe9aa 100644 --- a/keys/signer_bls12381_test.go +++ b/keys/signer_bls12381_test.go @@ -20,14 +20,18 @@ import ( "github.com/stretchr/testify/assert" + "github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig" "github.com/coinbase/rosetta-sdk-go/types" ) +var bls12381Keypair *KeyPair var signerBls12381 Signer var payloadBytes []byte +var augSigner Signer +var augPayload []byte func init() { - bls12381Keypair, _ := GenerateKeypair(types.Bls12381) + bls12381Keypair, _ = GenerateKeypair(types.Bls12381) signerBls12381, _ = bls12381Keypair.Signer() unsignedPayloadStr := "a7ca4bce10200d073ef10c46e9d27c3b4e31263d4c07fbec447650fcc1b286" + @@ -37,7 +41,7 @@ func init() { payloadBytes, _ = hex.DecodeString(unsignedPayloadStr) } -func TestSignBls12381(t *testing.T) { +func TestSignBls12381Basic(t *testing.T) { type payloadTest struct { payload *types.SigningPayload err bool @@ -45,7 +49,7 @@ func TestSignBls12381(t *testing.T) { } var payloadTests = []payloadTest{ - {mockPayload(payloadBytes, types.BlsG2Element), false, nil}, + {mockPayload(payloadBytes, types.Bls12381BasicMpl), false, nil}, {mockPayload(payloadBytes, ""), false, nil}, {mockPayload(payloadBytes, types.Ecdsa), true, ErrSignUnsupportedPayloadSignatureType}, { @@ -55,7 +59,7 @@ func TestSignBls12381(t *testing.T) { }, } for _, test := range payloadTests { - signature, err := signerBls12381.Sign(test.payload, types.BlsG2Element) + signature, err := signerBls12381.Sign(test.payload, types.Bls12381BasicMpl) if !test.err { assert.NoError(t, err) @@ -67,14 +71,14 @@ func TestSignBls12381(t *testing.T) { } } -func TestVerifyBls(t *testing.T) { +func TestVerifyBls12381Basic(t *testing.T) { type signatureTest struct { signature *types.Signature errMsg error } - payload := mockPayload(payloadBytes, types.BlsG2Element) - testSignature, err := signerBls12381.Sign(payload, types.BlsG2Element) + payload := mockPayload(payloadBytes, types.Bls12381BasicMpl) + testSignature, err := signerBls12381.Sign(payload, types.Bls12381BasicMpl) assert.NoError(t, err) simpleBytes := make([]byte, 32) @@ -92,7 +96,7 @@ func TestVerifyBls(t *testing.T) { payloadBytes, simpleBytes), ErrVerifyUnsupportedPayloadSignatureType}, {mockSignature( - types.BlsG2Element, + types.Bls12381BasicMpl, signerBls12381.PublicKey(), simpleBytes, testSignature.Bytes), ErrVerifyFailed}, @@ -105,7 +109,7 @@ func TestVerifyBls(t *testing.T) { // happy path goodSignature := mockSignature( - types.BlsG2Element, + types.Bls12381BasicMpl, signerBls12381.PublicKey(), payloadBytes, testSignature.Bytes, @@ -113,3 +117,81 @@ func TestVerifyBls(t *testing.T) { assert.Equal(t, nil, signerBls12381.Verify(goodSignature)) } + +func mockPublicKey( + pubkey_bytes []byte, + curveType types.CurveType, +) *types.PublicKey { + mockPubKey := &types.PublicKey{ + Bytes: pubkey_bytes, + CurveType: curveType, + } + + return mockPubKey +} + +func mockKP( + pubkey *types.PublicKey, + pk_bytes []byte, +) *KeyPair { + mockKP := &KeyPair{ + PublicKey: pubkey, + PrivateKey: pk_bytes, + } + + return mockKP +} + +func TestBls12381AugPrepend(t *testing.T) { + type signatureTest struct { + signature *types.Signature + errMsg error + } + + sk1_bytes, _ := hex.DecodeString("1603e1217d13437657e2716bd51ecb84e803170368e0dcbf2b6eb704d6914d1c") + synthetic_sk_bytes, _ := hex.DecodeString("0c0dd789a90f993feb20dc4ce7d9d689ce9a669341d06d6638696cba1eea3177") + synthetic_offset_sk_bytes, _ := hex.DecodeString("1d0baa87e45f286a6a1cde0566191e6a0e6a6fa7223764f894c4836565207360") + + payload := mockPayload(payloadBytes, types.Bls12381AugMpl) + + sk1 := new(bls_sig.SecretKey) + sk1.UnmarshalBinary(sk1_bytes) + pk1, _ := sk1.GetPublicKey() + pk1_bytes, _ := pk1.MarshalBinary() + pubKey := mockPublicKey(pk1_bytes, types.Bls12381) + keyPair := mockKP(pubKey, sk1_bytes) + signer, _ := keyPair.Signer() + + sig1, _ := signer.Sign(payload, types.Bls12381AugMpl) + assert.Equal(t, nil, signer.Verify(sig1)) + + synthetic_sk := new(bls_sig.SecretKey) + synthetic_sk.UnmarshalBinary(synthetic_sk_bytes) + synthetic_pk, _ := synthetic_sk.GetPublicKey() + synthetic_pk_bytes, _ := synthetic_pk.MarshalBinary() + syntheticPubKey := mockPublicKey(synthetic_pk_bytes, types.Bls12381) + syntheticKeyPair := mockKP(syntheticPubKey, synthetic_sk_bytes) + syntheticSigner, _ := syntheticKeyPair.Signer() + + sig2, _ := syntheticSigner.Sign(payload, types.Bls12381AugMpl) + assert.Equal(t, nil, syntheticSigner.Verify(sig2)) + + synthetic_offset_sk := new(bls_sig.SecretKey) + synthetic_offset_sk.UnmarshalBinary(synthetic_offset_sk_bytes) + synthetic_offset_pk, _ := synthetic_offset_sk.GetPublicKey() + synthetic_offset_pk_bytes, _ := synthetic_offset_pk.MarshalBinary() + syntheticOffsetPubKey := mockPublicKey(synthetic_offset_pk_bytes, types.Bls12381) + syntheticOffsetKeyPair := mockKP(syntheticOffsetPubKey, synthetic_offset_sk_bytes) + syntheticOffsetSigner, _ := syntheticOffsetKeyPair.Signer() + + prependedPayload := mockPayload(append(syntheticPubKey.Bytes, payloadBytes...), types.Bls12381AugMpl) + + offset_sig, _ := syntheticOffsetSigner.Sign(prependedPayload, types.Bls12381AugMpl) + assert.Equal(t, nil, syntheticOffsetSigner.Verify(offset_sig)) + + sig3, _ := signer.Sign(prependedPayload, types.Bls12381AugMpl) + + assert.Equal(t, nil, signer.Verify(sig3)) + assert.Equal(t, nil, syntheticOffsetSigner.Verify(sig3)) + assert.Equal(t, nil, syntheticSigner.Verify(sig3)) +} diff --git a/types/signature_type.go b/types/signature_type.go index e2c10f3b..5dc9685d 100644 --- a/types/signature_type.go +++ b/types/signature_type.go @@ -29,10 +29,11 @@ type SignatureType string // List of SignatureType const ( - BlsG2Element SignatureType = "bls12381_g2_element" - Ecdsa SignatureType = "ecdsa" - EcdsaRecovery SignatureType = "ecdsa_recovery" - Ed25519 SignatureType = "ed25519" - Schnorr1 SignatureType = "schnorr_1" - SchnorrPoseidon SignatureType = "schnorr_poseidon" + Bls12381BasicMpl SignatureType = "bls12381_basic_mpl" + Bls12381AugMpl SignatureType = "bls12381_aug_mpl" + Ecdsa SignatureType = "ecdsa" + EcdsaRecovery SignatureType = "ecdsa_recovery" + Ed25519 SignatureType = "ed25519" + Schnorr1 SignatureType = "schnorr_1" + SchnorrPoseidon SignatureType = "schnorr_poseidon" ) From 7578c5076f4a61ea617de1839e67ec8e77e5cf25 Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Sat, 12 Mar 2022 20:04:54 +0000 Subject: [PATCH 4/8] signAug without pk prepended so we can prepend any pk --- keys/signer_bls12381.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/keys/signer_bls12381.go b/keys/signer_bls12381.go index 1f69128e..5f9e0387 100644 --- a/keys/signer_bls12381.go +++ b/keys/signer_bls12381.go @@ -60,7 +60,7 @@ func (s *SignerBls12381) Sign( switch sigType { case types.Bls12381BasicMpl: - sigBytes, _ := signBasic(privKey, payload, s.KeyPair.PublicKey) + sigBytes, _ := signBasic(privKey, payload) return &types.Signature{ SigningPayload: payload, @@ -69,7 +69,7 @@ func (s *SignerBls12381) Sign( Bytes: sigBytes, }, nil case types.Bls12381AugMpl: - sigBytes, _ := signAug(privKey, payload, s.KeyPair.PublicKey) + sigBytes, _ := signAug(privKey, payload) return &types.Signature{ SigningPayload: payload, @@ -88,7 +88,7 @@ func (s *SignerBls12381) Sign( } } -func signBasic(sk *bls_sig.SecretKey, payload *types.SigningPayload, pk *types.PublicKey) ([]byte, error) { +func signBasic(sk *bls_sig.SecretKey, payload *types.SigningPayload) ([]byte, error) { bls := bls_sig.NewSigBasic() sig, err := bls.Sign(sk, payload.Bytes) @@ -101,8 +101,11 @@ func signBasic(sk *bls_sig.SecretKey, payload *types.SigningPayload, pk *types.P return sigBytes, nil } -func signAug(sk *bls_sig.SecretKey, payload *types.SigningPayload, pk *types.PublicKey) ([]byte, error) { - bls := bls_sig.NewSigAug() +func signAug(sk *bls_sig.SecretKey, payload *types.SigningPayload) ([]byte, error) { + // Domain separation tag for aug scheme + // according to section 4.2.2 in + // https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03 + bls := bls_sig.NewSigBasicWithDst("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_") sig, err := bls.Sign(sk, payload.Bytes) From e8014b09d410096075e9c957628a04ad18936c70 Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Wed, 6 Apr 2022 22:10:00 +0100 Subject: [PATCH 5/8] support bls12-381 private key import --- keys/keys.go | 14 ++++++++++++++ keys/keys_test.go | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/keys/keys.go b/keys/keys.go index 1b143cd3..db95d315 100644 --- a/keys/keys.go +++ b/keys/keys.go @@ -133,6 +133,20 @@ func ImportPrivateKey(privKeyHex string, curve types.CurveType) (*KeyPair, error }, PrivateKey: priKeyBytes, } + case types.Bls12381: + rawPrivKey := &bls_sig.SecretKey{} + _ = rawPrivKey.UnmarshalBinary(privKey) + + pubKey, _ := rawPrivKey.GetPublicKey() + pubKeyBin, _ := pubKey.MarshalBinary() + priKeyBytes, _ := rawPrivKey.MarshalBinary() + keyPair = &KeyPair{ + PublicKey: &types.PublicKey{ + Bytes: pubKeyBin, + CurveType: curve, + }, + PrivateKey: priKeyBytes, + } default: return nil, fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, curve) diff --git a/keys/keys_test.go b/keys/keys_test.go index 9b31b805..91b73b3a 100644 --- a/keys/keys_test.go +++ b/keys/keys_test.go @@ -139,6 +139,11 @@ func TestImportPrivateKey(t *testing.T) { types.Pallas, nil, }, + "simple Bls12381": { + "3ffb736d41737fc1fac8856458650742bfd01997bd96dd016215828b16309f58", + types.Bls12381, + nil, + }, "short ed25519": {"asd", types.Secp256k1, ErrPrivKeyUndecodable}, "short Secp256k1": {"asd", types.Edwards25519, ErrPrivKeyUndecodable}, "short pallas": {"asd", types.Pallas, ErrPrivKeyUndecodable}, From 4d3cafb5454fd21c57a5cd521c774512b4cac35a Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Wed, 6 Apr 2022 22:20:09 +0100 Subject: [PATCH 6/8] test for generating bls12-381 keypair --- keys/keys_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/keys/keys_test.go b/keys/keys_test.go index 91b73b3a..44d54703 100644 --- a/keys/keys_test.go +++ b/keys/keys_test.go @@ -83,6 +83,15 @@ func TestGenerateKeypairPallas(t *testing.T) { assert.Len(t, keypair.PrivateKey, PrivKeyBytesLen) } +func TestGenerateKeypairBls12381(t *testing.T) { + curve := types.Bls12381 + keypair, err := GenerateKeypair(curve) + + assert.NoError(t, err) + assert.Equal(t, keypair.PublicKey.CurveType, curve) + assert.Len(t, keypair.PrivateKey, PrivKeyBytesLen) +} + func mockKeyPair(privKey []byte, curveType types.CurveType) *KeyPair { keypair, _ := GenerateKeypair(curveType) keypair.PrivateKey = privKey From ba0d4e2160753535a396fc43f5cabce0c336064a Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Wed, 6 Apr 2022 22:41:30 +0100 Subject: [PATCH 7/8] bump kryptology --- go.mod | 3 ++- go.sum | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3bd9e30c..2eef88cf 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6 github.com/btcsuite/btcd v0.22.0-beta github.com/cenkalti/backoff v2.2.1+incompatible - github.com/coinbase/kryptology v1.5.5 + github.com/coinbase/kryptology v1.8.0 github.com/dgraph-io/badger/v2 v2.2007.4 github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect @@ -17,6 +17,7 @@ require ( github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77 github.com/mitchellh/mapstructure v1.4.3 github.com/neilotoole/errgroup v0.1.6 + github.com/nixberg/chacha-rng-go v0.1.0 // indirect github.com/segmentio/fasthash v1.0.3 github.com/stretchr/testify v1.7.0 github.com/tidwall/gjson v1.14.0 diff --git a/go.sum b/go.sum index f5de682e..530eeaa9 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/coinbase/kryptology v1.5.5 h1:iSB2AOGYBxvq+/4huDWVEnyCWWWrVBbb7yiCcLrE2rw= github.com/coinbase/kryptology v1.5.5/go.mod h1:3zId6V3k/8v/o5gksjffbWMkH7Ik1UgeZVGTBvlpIxc= +github.com/coinbase/kryptology v1.8.0 h1:Aoq4gdTsJhSU3lNWsD5BWmFSz2pE0GlmrljaOxepdYY= +github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= From 4049bb06bf04696b3e0010164c70148171caf842 Mon Sep 17 00:00:00 2001 From: Freddie Coleman Date: Wed, 6 Apr 2022 22:53:31 +0100 Subject: [PATCH 8/8] merge upstream master --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 45e73442..29883ef5 100644 --- a/go.sum +++ b/go.sum @@ -330,6 +330,7 @@ github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/neilotoole/errgroup v0.1.6 h1:PODGqPXdT5BC/zCYIMoTrwV+ujKcW+gBXM6Ye9Ve3R8= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= +github.com/nixberg/chacha-rng-go v0.1.0/go.mod h1:iPf1i6Vcwgoue86dblORobEXNCd1PFQiysFiImawfCM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=