Skip to content

Commit

Permalink
implement Signer interface for bls12381
Browse files Browse the repository at this point in the history
  • Loading branch information
freddiecoleman committed Mar 5, 2022
1 parent 0312039 commit c0925f2
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 2 deletions.
4 changes: 2 additions & 2 deletions asserter/construction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions keys/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
19 changes: 19 additions & 0 deletions keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
Expand Down
121 changes: 121 additions & 0 deletions keys/signer_bls12381.go
Original file line number Diff line number Diff line change
@@ -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 != true {
return fmt.Errorf("%w: %s", ErrVerifyFailed, "Verify failed")
}

return nil
}
114 changes: 114 additions & 0 deletions keys/signer_bls12381_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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 bls12381Keypair *KeyPair
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))
}
1 change: 1 addition & 0 deletions types/curve_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type CurveType string

// List of CurveType
const (
Bls12381 CurveType = "bls12381"
Secp256k1 CurveType = "secp256k1"
Secp256r1 CurveType = "secp256r1"
Edwards25519 CurveType = "edwards25519"
Expand Down
1 change: 1 addition & 0 deletions types/signature_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit c0925f2

Please sign in to comment.