Skip to content

Commit

Permalink
feat(wasmer/crypto): move sig verifier to crypto pkg (#2057)
Browse files Browse the repository at this point in the history
  • Loading branch information
1garo committed Dec 6, 2021
1 parent 89366c3 commit 2842ba7
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 110 deletions.
18 changes: 18 additions & 0 deletions lib/crypto/ed25519/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ type PublicKey ed25519.PublicKey
// PublicKeyBytes is an encoded ed25519 public key
type PublicKeyBytes [PublicKeyLength]byte

// VerifySignature verifies a signature given a public key and a message
func VerifySignature(publicKey, signature, message []byte) error {
pubKey, err := NewPublicKey(publicKey)
if err != nil {
return fmt.Errorf("ed25519: %w", err)
}

ok, err := pubKey.Verify(message, signature)
if err != nil {
return fmt.Errorf("ed25519: %w", err)
} else if !ok {
return fmt.Errorf("ed25519: %w: for message 0x%x, signature 0x%x and public key 0x%x",
crypto.ErrSignatureVerificationFailed, message, signature, publicKey)
}

return nil
}

// String returns the PublicKeyBytes formatted as a hex string
func (b PublicKeyBytes) String() string {
pk := [PublicKeyLength]byte(b)
Expand Down
63 changes: 62 additions & 1 deletion lib/crypto/ed25519/ed25519_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
package ed25519

import (
ed25519 "crypto/ed25519"
"crypto/ed25519"
"fmt"
"reflect"
"testing"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto"

bip39 "github.com/cosmos/go-bip39"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -99,3 +101,62 @@ func TestNewKeypairFromMnenomic_Again(t *testing.T) {
expectedPubkey := common.MustHexToBytes("0xf56d9231e7b7badd3f1e10ad15ef8aa08b70839723d0a2d10d7329f0ea2b8c61")
require.Equal(t, expectedPubkey, kp.Public().Encode())
}

func TestVerifySignature(t *testing.T) {
t.Parallel()
keypair, err := GenerateKeypair()
require.NoError(t, err)

publicKey := keypair.public.Encode()

message := []byte("Hello world!")

signature, err := keypair.Sign(message)
require.NoError(t, err)

testCase := map[string]struct {
publicKey, signature, message []byte
err error
}{
"success": {
publicKey: publicKey,
signature: signature,
message: message,
},
"bad public key input": {
publicKey: []byte{},
signature: signature,
message: message,
err: fmt.Errorf("ed25519: cannot create public key: input is not 32 bytes"),
},
"invalid signature length": {
publicKey: publicKey,
signature: []byte{},
message: message,
err: fmt.Errorf("ed25519: invalid signature length"),
},
"verification failed": {
publicKey: publicKey,
signature: signature,
message: []byte("a225e8c75da7da319af6335e7642d473"),
err: fmt.Errorf("ed25519: %w: for message 0x%x, signature 0x%x and public key 0x%x",
crypto.ErrSignatureVerificationFailed, []byte("a225e8c75da7da319af6335e7642d473"), signature, publicKey),
},
}

for name, value := range testCase {
testCase := value
t.Run(name, func(t *testing.T) {
t.Parallel()

err := VerifySignature(testCase.publicKey, testCase.signature, testCase.message)

if testCase.err != nil {
require.EqualError(t, err, testCase.err.Error())
return
}
require.NoError(t, err)
})
}

}
12 changes: 12 additions & 0 deletions lib/crypto/secp256k1/secp256k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"crypto/ecdsa"
"encoding/hex"
"errors"
"fmt"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto"
Expand Down Expand Up @@ -36,6 +37,17 @@ type PublicKey struct {
key ecdsa.PublicKey
}

// VerifySignature verifies a signature given a public key and a message
func VerifySignature(publicKey, signature, message []byte) error {
ok := secp256k1.VerifySignature(publicKey, message, signature)
if ok {
return nil
}

return fmt.Errorf("secp256k1: %w: for message 0x%x, signature 0x%x and public key 0x%x",
crypto.ErrSignatureVerificationFailed, message, signature, publicKey)
}

// RecoverPublicKey returns the 64-byte uncompressed public key that created the given signature.
func RecoverPublicKey(msg, sig []byte) ([]byte, error) {
// update recovery bit
Expand Down
47 changes: 47 additions & 0 deletions lib/crypto/secp256k1/secp256k1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
package secp256k1

import (
"fmt"
"reflect"
"testing"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto"

"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -154,3 +156,48 @@ func TestRecoverPublicKeyCompressed(t *testing.T) {
require.NoError(t, err)
require.Equal(t, kp.Public(), r)
}

func TestVerifySignature(t *testing.T) {
t.Parallel()
keypair, err := GenerateKeypair()
require.NoError(t, err)

message := []byte("a225e8c75da7da319af6335e7642d473")

signature, err := keypair.Sign(message)
require.NoError(t, err)

testCase := map[string]struct {
publicKey, signature, message []byte
err error
}{
"success": {
publicKey: keypair.public.Encode(),
signature: signature[:64],
message: message,
},
"verification failed": {
publicKey: keypair.public.Encode(),
signature: []byte{},
message: message,
err: fmt.Errorf("secp256k1: %w: for message 0x%x, signature 0x and public key 0x%x",
crypto.ErrSignatureVerificationFailed, message, keypair.public.Encode()),
},
}

for name, value := range testCase {
testCase := value
t.Run(name, func(t *testing.T) {
t.Parallel()

err := VerifySignature(testCase.publicKey, testCase.signature, testCase.message)

if testCase.err != nil {
require.EqualError(t, err, testCase.err.Error())
return
}
require.NoError(t, err)
})
}

}
70 changes: 21 additions & 49 deletions lib/runtime/sig_verifier.go → lib/crypto/sig_verifier.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package runtime
package crypto

import (
"fmt"
"errors"
"sync"
"time"

"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/crypto"
"github.com/ChainSafe/gossamer/lib/crypto/ed25519"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)

// Signature ...
type Signature struct {
PubKey []byte
Sign []byte
Msg []byte
KeyTypeID crypto.KeyType
var ErrSignatureVerificationFailed = errors.New("failed to verify signature")

// SigVerifyFunc verifies a signature given a public key and a message
type SigVerifyFunc func(pubkey, sig, msg []byte) (err error)

// SignatureInfo ...
type SignatureInfo struct {
PubKey []byte
Sign []byte
Msg []byte
VerifyFunc SigVerifyFunc
}

// SignatureVerifier ...
type SignatureVerifier struct {
batch []*Signature
batch []*SignatureInfo
init bool // Indicates whether the batch processing is started.
invalid bool // Set to true if any signature verification fails.
logger log.LeveledLogger
Expand All @@ -41,7 +42,7 @@ type SignatureVerifier struct {
// Signatures can be added to the batch using Add().
func NewSignatureVerifier(logger log.LeveledLogger) *SignatureVerifier {
return &SignatureVerifier{
batch: make([]*Signature, 0),
batch: make([]*SignatureInfo, 0),
init: false,
invalid: false,
logger: logger,
Expand All @@ -66,11 +67,11 @@ func (sv *SignatureVerifier) Start() {
case <-sv.closeCh:
return
default:
sign := sv.Remove()
if sign == nil {
signature := sv.Remove()
if signature == nil {
continue
}
err := sign.verify()
err := signature.VerifyFunc(signature.PubKey, signature.Sign, signature.Msg)
if err != nil {
sv.logger.Errorf("[ext_crypto_start_batch_verify_version_1]: %s", err)
sv.Invalid()
Expand Down Expand Up @@ -103,7 +104,7 @@ func (sv *SignatureVerifier) Invalid() {
}

// Add ...
func (sv *SignatureVerifier) Add(s *Signature) {
func (sv *SignatureVerifier) Add(s *SignatureInfo) {
if sv.IsInvalid() {
return
}
Expand All @@ -114,7 +115,7 @@ func (sv *SignatureVerifier) Add(s *Signature) {
}

// Remove returns the first signature from the batch. Returns nil if batch is empty.
func (sv *SignatureVerifier) Remove() *Signature {
func (sv *SignatureVerifier) Remove() *SignatureInfo {
sv.Lock()
defer sv.Unlock()
if len(sv.batch) == 0 {
Expand All @@ -130,7 +131,7 @@ func (sv *SignatureVerifier) Reset() {
sv.Lock()
defer sv.Unlock()
sv.init = false
sv.batch = make([]*Signature, 0)
sv.batch = make([]*SignatureInfo, 0)
sv.invalid = false
sv.closeCh = make(chan struct{})
}
Expand All @@ -153,32 +154,3 @@ func (sv *SignatureVerifier) Finish() bool {
sv.Reset()
return !isInvalid
}

func (sig *Signature) verify() error {
switch sig.KeyTypeID {
case crypto.Ed25519Type:
pubKey, err := ed25519.NewPublicKey(sig.PubKey)
if err != nil {
return fmt.Errorf("failed to fetch ed25519 public key: %s", err)
}
ok, err := pubKey.Verify(sig.Msg, sig.Sign)
if err != nil || !ok {
return fmt.Errorf("failed to verify ed25519 signature: %s", err)
}
case crypto.Sr25519Type:
pubKey, err := sr25519.NewPublicKey(sig.PubKey)
if err != nil {
return fmt.Errorf("failed to fetch sr25519 public key: %s", err)
}
ok, err := pubKey.Verify(sig.Msg, sig.Sign)
if err != nil || !ok {
return fmt.Errorf("failed to verify sr25519 signature: %s", err)
}
case crypto.Secp256k1Type:
ok := secp256k1.VerifySignature(sig.PubKey, sig.Msg, sig.Sign)
if !ok {
return fmt.Errorf("failed to verify secp256k1 signature")
}
}
return nil
}
Loading

0 comments on commit 2842ba7

Please sign in to comment.