Skip to content

Commit eb8905b

Browse files
implement Signer interface for bls12381
1 parent 0312039 commit eb8905b

File tree

7 files changed

+260
-2
lines changed

7 files changed

+260
-2
lines changed

asserter/construction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func CurveType(
215215
curve types.CurveType,
216216
) error {
217217
switch curve {
218-
case types.Secp256k1, types.Secp256r1, types.Edwards25519, types.Tweedle, types.Pallas:
218+
case types.Secp256k1, types.Secp256r1, types.Edwards25519, types.Tweedle, types.Pallas, types.Bls12381:
219219
return nil
220220
default:
221221
return fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, curve)
@@ -304,7 +304,7 @@ func SignatureType(
304304
signature types.SignatureType,
305305
) error {
306306
switch signature {
307-
case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon:
307+
case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon, types.BlsG2Element:
308308
return nil
309309
default:
310310
return fmt.Errorf("%w: %s", ErrSignatureTypeNotSupported, signature)

keys/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ var (
3939
ErrKeyGenPallasFailed = errors.New(
4040
"keygen: error generating key pair for pallas curve type",
4141
)
42+
ErrKeyGenBls12381Failed = errors.New(
43+
"keygen: error generating key pair for bls12381 curve type",
44+
)
4245
ErrCurveTypeNotSupported = errors.New("not a supported CurveType")
4346

4447
ErrSignUnsupportedPayloadSignatureType = errors.New(

keys/keys.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"math/big"
2525

2626
"github.com/btcsuite/btcd/btcec"
27+
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
2728
"github.com/coinbase/kryptology/pkg/signatures/schnorr/mina"
2829

2930
"github.com/coinbase/rosetta-sdk-go/asserter"
@@ -210,6 +211,22 @@ func GenerateKeypair(curve types.CurveType) (*KeyPair, error) {
210211
CurveType: curve,
211212
}
212213

214+
keyPair = &KeyPair{
215+
PublicKey: pubKey,
216+
PrivateKey: rawPrivKeyBytes,
217+
}
218+
case types.Bls12381:
219+
rawPubKey, rawPrivKey, err := bls_sig.NewSigBasic().Keygen()
220+
rawPubKeyBytes, _ := rawPubKey.MarshalBinary()
221+
rawPrivKeyBytes, _ := rawPrivKey.MarshalBinary()
222+
if err != nil {
223+
return nil, fmt.Errorf("%w: %v", ErrKeyGenBls12381Failed, err)
224+
}
225+
pubKey := &types.PublicKey{
226+
Bytes: rawPubKeyBytes,
227+
CurveType: curve,
228+
}
229+
213230
keyPair = &KeyPair{
214231
PublicKey: pubKey,
215232
PrivateKey: rawPrivKeyBytes,
@@ -252,6 +269,8 @@ func (k *KeyPair) Signer() (Signer, error) {
252269
return &SignerSecp256r1{k}, nil
253270
case types.Pallas:
254271
return &SignerPallas{k}, nil
272+
case types.Bls12381:
273+
return &SignerBls12381{k}, nil
255274
default:
256275
return nil, fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, k.PublicKey.CurveType)
257276
}

keys/signer_bls12381.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2022 Coinbase, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package keys
16+
17+
import (
18+
"errors"
19+
"fmt"
20+
21+
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
22+
"github.com/coinbase/rosetta-sdk-go/asserter"
23+
"github.com/coinbase/rosetta-sdk-go/types"
24+
)
25+
26+
var ErrBlsTransactionValidationErr = errors.New("transaction with bls validation failed")
27+
28+
type SignerBls12381 struct {
29+
KeyPair *KeyPair
30+
}
31+
32+
func (s *SignerBls12381) PublicKey() *types.PublicKey {
33+
return s.KeyPair.PublicKey
34+
}
35+
36+
// Sign transaction payloads using a KeyPair
37+
func (s *SignerBls12381) Sign(
38+
payload *types.SigningPayload,
39+
sigType types.SignatureType,
40+
) (*types.Signature, error) {
41+
err := s.KeyPair.IsValid()
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
if !(payload.SignatureType == types.BlsG2Element || payload.SignatureType == "") {
47+
return nil, fmt.Errorf(
48+
"%w: expected %v but got %v",
49+
ErrSignUnsupportedPayloadSignatureType,
50+
types.BlsG2Element,
51+
payload.SignatureType,
52+
)
53+
}
54+
55+
if sigType != types.BlsG2Element {
56+
return nil, fmt.Errorf(
57+
"%w: expected %v but got %v",
58+
ErrSignUnsupportedSignatureType,
59+
types.BlsG2Element,
60+
sigType,
61+
)
62+
}
63+
64+
// Generate private key bytes
65+
privKeyBytes := s.KeyPair.PrivateKey
66+
privKey := &bls_sig.SecretKey{}
67+
_ = privKey.UnmarshalBinary(privKeyBytes)
68+
69+
bls := bls_sig.NewSigBasic()
70+
sig, err := bls.Sign(privKey, payload.Bytes)
71+
if err != nil {
72+
return nil, err
73+
}
74+
sigBytes, _ := sig.MarshalBinary()
75+
76+
return &types.Signature{
77+
SigningPayload: payload,
78+
PublicKey: s.KeyPair.PublicKey,
79+
SignatureType: payload.SignatureType,
80+
Bytes: sigBytes,
81+
}, nil
82+
}
83+
84+
// Verify verifies a Signature, by checking the validity of a Signature,
85+
// the SigningPayload, and the PublicKey of the Signature.
86+
func (s *SignerBls12381) Verify(signature *types.Signature) error {
87+
if signature.SignatureType != types.BlsG2Element {
88+
return fmt.Errorf(
89+
"%w: expected %v but got %v",
90+
ErrVerifyUnsupportedPayloadSignatureType,
91+
types.BlsG2Element,
92+
signature.SignatureType,
93+
)
94+
}
95+
96+
pubKeyBytes := signature.PublicKey.Bytes
97+
pubKey := &bls_sig.PublicKey{}
98+
_ = pubKey.UnmarshalBinary(pubKeyBytes)
99+
100+
sigBytes := signature.Bytes
101+
sig := &bls_sig.Signature{}
102+
_ = sig.UnmarshalBinary(sigBytes)
103+
104+
err := asserter.Signatures([]*types.Signature{signature})
105+
if err != nil {
106+
return fmt.Errorf("%w: %s", ErrVerifyFailed, err)
107+
}
108+
109+
bls := bls_sig.NewSigBasic()
110+
result, err := bls.Verify(pubKey, signature.SigningPayload.Bytes, sig)
111+
112+
if err != nil {
113+
return err
114+
}
115+
116+
if !result {
117+
return fmt.Errorf("%w: %s", ErrVerifyFailed, "Verify failed")
118+
}
119+
120+
return nil
121+
}

keys/signer_bls12381_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2022 Coinbase, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package keys
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
22+
"github.com/coinbase/rosetta-sdk-go/types"
23+
)
24+
25+
var signerBls12381 Signer
26+
27+
func init() {
28+
bls12381Keypair, _ := GenerateKeypair(types.Bls12381)
29+
signerBls12381, _ = bls12381Keypair.Signer()
30+
31+
unsignedTxStr := "a7ca4bce10200d073ef10c46e9d27c3b4e31263d4c07fbec447650fcc1b286" +
32+
"300e8ecf25c0560f9cb5aa673247fb6a6f95fb73164d1bf5d41288f57ea40517d9da86c44a289ed" +
33+
"2b6f9d3bf9a750ee96dbf905073f8ae56c80100ef47f5585acf70baff8c72c3b8a833181fb3edf4" +
34+
"328fccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb"
35+
txnBytes = []byte(unsignedTxStr)
36+
}
37+
38+
func TestSignBls12381(t *testing.T) {
39+
type payloadTest struct {
40+
payload *types.SigningPayload
41+
err bool
42+
errMsg error
43+
}
44+
45+
var payloadTests = []payloadTest{
46+
{mockPayload(txnBytes, types.BlsG2Element), false, nil},
47+
{mockPayload(txnBytes, ""), false, nil},
48+
{mockPayload(txnBytes, types.Ecdsa), true, ErrSignUnsupportedPayloadSignatureType},
49+
{
50+
mockPayload(txnBytes, types.EcdsaRecovery),
51+
true,
52+
ErrSignUnsupportedPayloadSignatureType,
53+
},
54+
}
55+
for _, test := range payloadTests {
56+
signature, err := signerBls12381.Sign(test.payload, types.BlsG2Element)
57+
58+
if !test.err {
59+
assert.NoError(t, err)
60+
assert.Len(t, signature.Bytes, 96)
61+
assert.Equal(t, signerBls12381.PublicKey(), signature.PublicKey)
62+
} else {
63+
assert.Contains(t, err.Error(), test.errMsg.Error())
64+
}
65+
}
66+
}
67+
68+
func TestVerifyBls(t *testing.T) {
69+
type signatureTest struct {
70+
signature *types.Signature
71+
errMsg error
72+
}
73+
74+
payload := mockPayload(txnBytes, types.BlsG2Element)
75+
testSignature, err := signerBls12381.Sign(payload, types.BlsG2Element)
76+
assert.NoError(t, err)
77+
78+
simpleBytes := make([]byte, 32)
79+
copy(simpleBytes, "hello")
80+
81+
var signatureTests = []signatureTest{
82+
{mockSignature(
83+
types.Ecdsa,
84+
signerBls12381.PublicKey(),
85+
txnBytes,
86+
simpleBytes), ErrVerifyUnsupportedPayloadSignatureType},
87+
{mockSignature(
88+
types.EcdsaRecovery,
89+
signerBls12381.PublicKey(),
90+
txnBytes,
91+
simpleBytes), ErrVerifyUnsupportedPayloadSignatureType},
92+
{mockSignature(
93+
types.BlsG2Element,
94+
signerBls12381.PublicKey(),
95+
simpleBytes,
96+
testSignature.Bytes), ErrVerifyFailed},
97+
}
98+
99+
for _, test := range signatureTests {
100+
err := signerBls12381.Verify(test.signature)
101+
assert.Contains(t, err.Error(), test.errMsg.Error())
102+
}
103+
104+
// happy path
105+
goodSignature := mockSignature(
106+
types.BlsG2Element,
107+
signerBls12381.PublicKey(),
108+
txnBytes,
109+
testSignature.Bytes,
110+
)
111+
112+
assert.Equal(t, nil, signerBls12381.Verify(goodSignature))
113+
}

types/curve_type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type CurveType string
2727

2828
// List of CurveType
2929
const (
30+
Bls12381 CurveType = "bls12381"
3031
Secp256k1 CurveType = "secp256k1"
3132
Secp256r1 CurveType = "secp256r1"
3233
Edwards25519 CurveType = "edwards25519"

types/signature_type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type SignatureType string
2929

3030
// List of SignatureType
3131
const (
32+
BlsG2Element SignatureType = "bls12381_g2_element"
3233
Ecdsa SignatureType = "ecdsa"
3334
EcdsaRecovery SignatureType = "ecdsa_recovery"
3435
Ed25519 SignatureType = "ed25519"

0 commit comments

Comments
 (0)