Skip to content

Commit

Permalink
internal/ident, and no Verify automation (#352)
Browse files Browse the repository at this point in the history
* internal/ident, and no Verify automation

* just remove the pointless tx/msg Verify methods
  • Loading branch information
jchappelow authored and brennanjl committed Feb 26, 2024
1 parent e437714 commit 33b9871
Show file tree
Hide file tree
Showing 21 changed files with 243 additions and 133 deletions.
4 changes: 2 additions & 2 deletions cmd/kwild/server/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/kwilteam/kwil-db/internal/abci/cometbft"
"github.com/kwilteam/kwil-db/internal/abci/snapshots"
"github.com/kwilteam/kwil-db/internal/accounts"
"github.com/kwilteam/kwil-db/internal/ident"
"github.com/kwilteam/kwil-db/internal/kv/atomic"
"github.com/kwilteam/kwil-db/internal/kv/badger"
"github.com/kwilteam/kwil-db/internal/modules/datasets"
Expand All @@ -33,7 +34,6 @@ import (
"github.com/kwilteam/kwil-db/internal/sessions/wal"
vmgr "github.com/kwilteam/kwil-db/internal/validators"

"github.com/kwilteam/kwil-db/core/crypto/auth"
"github.com/kwilteam/kwil-db/core/log"
admpb "github.com/kwilteam/kwil-db/core/rpc/protobuf/admin/v0"
txpb "github.com/kwilteam/kwil-db/core/rpc/protobuf/tx/v1"
Expand Down Expand Up @@ -207,7 +207,7 @@ func buildEngine(d *coreDependencies, a *sessions.AtomicCommitter) *engine.Engin

e, err := engine.Open(d.ctx, d.opener,
sqlCommitRegister,
auth.GetAddress,
ident.Address,
engine.WithLogger(*d.log.Named("engine")),
engine.WithExtensions(adaptExtensions(extensions)),
)
Expand Down
85 changes: 25 additions & 60 deletions core/crypto/auth/auth.go
Original file line number Diff line number Diff line change
@@ -1,74 +1,39 @@
/*
Package auth provides an Authenticator interface for developers to implement
their own Kwil authentication drivers. Authenticator extensions may be used to
expand the type of signatures that may be verified on transactions and messages
via the (*Signature).Verify method, where the Signature.Type field corresponds
to the unique Authenticator name. It also provides the ability to derive an
address from a public key for a certain network.
Package auth provides the standard signing and verification methods used in
Kwil. These are Ethereum "personal sign" used by wallets to sign a customized
readable message, and plain Ed25519 signing used for validator node signatures.
Similar to Go's database/sql package, developers can implement the Authenticator
interface and register it with the RegisterAuthenticator function. The name used
to register is to be set as the signature type creating signatures.
It also defines an Authenticator interface for developers to implement their own
Kwil authentication drivers. See the extensions/auth package in the kwil-db main
module. Authenticator extensions may be used to expand the type of signatures
that may be verified on transactions and messages. It also provides the ability
to derive an address from a public key for a certain network.
There are presently two Signers defined in the Kwil Go SDK with pre-registered
Authenticators with the same type: EthPersonalSigType and Ed25519SigType. When
registering a new Authenticator, the values of these may not be used.
registering a new Authenticator, the values of these may not be used. This is
the primary reason that the Authenticator interface is defined in this package
instead of the kwil-db main module under extensions/auth. We may consider moving
these two Authenticator implementations out of the SDK and into the main module
where they are only available to the application that needs them, but it may be
awkward to have complementary verification defined in the same place as the
signing.
*/
package auth

import (
"errors"
"fmt"
"strings"
)

// Authenticator is an interface for authenticating an incoming call
// It is made to work with keypair authentication
type Authenticator interface {
// Verifier is satisfied by types that can verify a signature against a public
// key and message. A Verifier implementation will generally pertain to a
// certain message serialization scheme and key type.
type Verifier interface {
// Verify verifies the signature against the given public key and data.
Verify(sender, msg, signature []byte) error

// Address returns an address from a public key
Address(sender []byte) (string, error)
}

var registeredAuthenticators = make(map[string]Authenticator)

// RegisterAuthenticator registers an authenticator with a given name
func RegisterAuthenticator(name string, auth Authenticator) error {
name = strings.ToLower(name)
if _, ok := registeredAuthenticators[name]; ok {
return fmt.Errorf("%w: %s", ErrAuthenticatorExists, name)
}

registeredAuthenticators[name] = auth
return nil
}

// getAuthenticator returns an authenticator by the name it was registered with
func getAuthenticator(name string) (Authenticator, error) {
name = strings.ToLower(name)
auth, ok := registeredAuthenticators[name]
if !ok {
return nil, fmt.Errorf("%w: %s", ErrAuthenticatorNotFound, name)
}

return auth, nil
}

// GetAddress returns an address from a public key and authenticator type
func GetAddress(authType string, sender []byte) (string, error) {
auth, err := getAuthenticator(authType)
if err != nil {
return "", err
}
// Authenticator is an interface for authenticating a message and deriving an
// encoded address for a public key.
type Authenticator interface {
Verifier

return auth.Address(sender)
// Address returns an address from a public key
Address(sender []byte) (string, error)
}

var (
// ErrAuthenticatorExists is returned when an authenticator is already registered
ErrAuthenticatorExists = errors.New("authenticator already exists")
// ErrAuthenticatorNotFound is returned when an authenticator is not found
ErrAuthenticatorNotFound = errors.New("authenticator not found")
)
7 changes: 4 additions & 3 deletions core/crypto/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ func Test_Auth(t *testing.T) {
sig, err := tc.signer.Sign(msg)
assert.NoError(t, err)

authenticator := tc.signer.Authenticator()

// verify the signature
err = sig.Verify(tc.signer.PublicKey(), msg)
err = authenticator.Verify(tc.signer.PublicKey(), msg, sig.Signature)
assert.NoError(t, err)

// check the address
address, err := auth.GetAddress(sig.Type, tc.signer.PublicKey())
address, err := authenticator.Address(tc.signer.PublicKey())
assert.NoError(t, err)

assert.Equal(t, tc.address, address)
})
}
Expand Down
7 changes: 0 additions & 7 deletions core/crypto/auth/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ import (
"github.com/kwilteam/kwil-db/core/crypto"
)

func init() {
err := RegisterAuthenticator(Ed25519Auth, Ed25519Authenticator{})
if err != nil {
panic(err)
}
}

const (
// Ed25519Auth is a plain ed25519 authenticator. This is used for validator
// signature verification. This is intended as the authenticator for the
Expand Down
17 changes: 4 additions & 13 deletions core/crypto/auth/eth_personal_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@ import (
"github.com/kwilteam/kwil-db/core/crypto"

ethAccounts "github.com/ethereum/go-ethereum/accounts"
ethCommon "github.com/ethereum/go-ethereum/common"
ethCrypto "github.com/ethereum/go-ethereum/crypto"
)

func init() {
err := RegisterAuthenticator(EthPersonalSignAuth, EthSecp256k1Authenticator{})
if err != nil {
panic(err)
}
}

const (
// EthPersonalSignAuth is the Ethereum "personal sign" authentication type,
// which uses the secp256k1 signature scheme with a prefixed message and the
Expand All @@ -37,12 +31,9 @@ var _ Authenticator = EthSecp256k1Authenticator{}

// Address generates an ethereum address from a public key.
func (EthSecp256k1Authenticator) Address(publicKey []byte) (string, error) {
ethKey, err := ethCrypto.UnmarshalPubkey(publicKey)
if err != nil {
return "", err
}

return ethCrypto.PubkeyToAddress(*ethKey).Hex(), nil
// See ethCrypto.PubkeyToAddress for this formula:
addr := ethCommon.BytesToAddress(ethCrypto.Keccak256(publicKey[1:])[12:])
return addr.String(), nil
}

// Verify verifies applies the Ethereum TextHash digest and verifies the signature
Expand Down
40 changes: 30 additions & 10 deletions core/crypto/auth/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@ type Signature struct {
Type string `json:"signature_type"`
}

// Verify verifies the signature against the given message and public key.
func (s *Signature) Verify(senderPubKey, msg []byte) error {
a, err := getAuthenticator(s.Type)
if err != nil {
return err
}

return a.Verify(senderPubKey, msg, s.Signature)
}

// Signer is an interface for something that can sign messages.
// It returns signatures with a designated AuthType, which should
// be used to determine how to verify the signature.
Expand All @@ -37,6 +27,12 @@ type Signer interface {

// PublicKey returns the public key of the signer
PublicKey() []byte

// Authenticator returns the corresponding Authenticator that must work with
// signatures produced by this signer. Not all Authenticators have a
// corresponding Signer, but all Signers must have an Authenticator, which
// this method guarantees.
Authenticator() Authenticator
}

// EthPersonalSecp256k1Signer is a signer that signs messages using the
Expand Down Expand Up @@ -66,6 +62,18 @@ func (e *EthPersonalSigner) Sign(msg []byte) (*Signature, error) {
}, nil
}

// Address generates an ethereum address from a public key.
func (e *EthPersonalSigner) Address() (string, error) {
pubBytes := e.PublicKey()
return EthSecp256k1Authenticator{}.Address(pubBytes)
}

// Authenticator returns the Authenticator capable of Verifying signatures
// produced by this Signer.
func (e *EthPersonalSigner) Authenticator() Authenticator {
return EthSecp256k1Authenticator{}
}

// PublicKey returns the public key of the signer
func (e *EthPersonalSigner) PublicKey() []byte {
return e.Secp256k1PrivateKey.PubKey().Bytes()
Expand Down Expand Up @@ -97,3 +105,15 @@ func (e *Ed25519Signer) Sign(msg []byte) (*Signature, error) {
func (e *Ed25519Signer) PublicKey() []byte {
return e.Ed25519PrivateKey.PubKey().Bytes()
}

// Address generates an ethereum address from a public key.
func (e *Ed25519Signer) Address() (string, error) {
pubBytes := e.PublicKey()
return Ed25519Authenticator{}.Address(pubBytes)
}

// Authenticator returns the Authenticator capable of Verifying signatures
// produced by this Signer.
func (e *Ed25519Signer) Authenticator() Authenticator {
return Ed25519Authenticator{}
}
23 changes: 7 additions & 16 deletions core/types/transactions/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,16 @@ func CreateCallMessage(payload *ActionCall) (*CallMessage, error) {
}, nil
}

// SerializeMsg produces the serialization of the message that is to be used in
// both signing and verification of message.
func (s *CallMessage) SerializeMsg() ([]byte, error) {
return s.Body.SerializeMsg(s.Serialization)
}

// Sign signs message body with given signer. It will serialize the message
// body to get message-to-be-sign first, then sign it.
func (s *CallMessage) Sign(signer auth.Signer) error {
msg, err := s.Body.SerializeMsg(s.Serialization)
msg, err := s.SerializeMsg()
if err != nil {
return err
}
Expand All @@ -119,18 +125,3 @@ func (s *CallMessage) Sign(signer auth.Signer) error {
func (s *CallMessage) IsSigned() bool {
return s.Signature != nil && s.Sender != nil
}

// Verify verifies the authenticity of a signed message. It will serialize
// the message body to get message-to-be-sign, then verify it .
func (s *CallMessage) Verify() error {
if !s.IsSigned() {
return errors.New("message is not signed")
}

msg, err := s.Body.SerializeMsg(s.Serialization)
if err != nil {
return err
}

return s.Signature.Verify(s.Sender, msg)
}
9 changes: 7 additions & 2 deletions core/types/transactions/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,13 @@ func TestCallMessage_Sign(t *testing.T) {
require.Equal(t1, hex.EncodeToString(tt.wantSig.Signature),
hex.EncodeToString(msg.Signature.Signature), "mismatch signature")

err = msg.Verify()
require.NoError(t1, err, "error verifying tx")
require.True(t, msg.IsSigned())
msgBts, err := msg.SerializeMsg()
require.NoError(t1, err, "error serializing message")

authenticator := tt.args.signer.Authenticator()
err = authenticator.Verify(msg.Sender, msgBts, msg.Signature.Signature)
require.NoError(t1, err, "error verifying message")
})
}

Expand Down
13 changes: 4 additions & 9 deletions core/types/transactions/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,10 @@ type Transaction struct {
Sender []byte
}

// Verify verifies the signature of the transaction
// It will deserialize the transaction body and verify the signature
func (t *Transaction) Verify() error {
msg, err := t.Body.SerializeMsg(t.Serialization)
if err != nil {
return err
}

return t.Signature.Verify(t.Sender, msg)
// SerializeMsg produces the serialization of the transaction that is to be used
// in both signing and verification of transaction.
func (t *Transaction) SerializeMsg() ([]byte, error) {
return t.Body.SerializeMsg(t.Serialization)
}

// Sign signs transaction body with given signer.
Expand Down
8 changes: 6 additions & 2 deletions core/types/transactions/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,12 @@ func TestTransaction_Sign(t *testing.T) {
require.Equal(t1, hex.EncodeToString(tt.wantSig.Signature),
hex.EncodeToString(tx.Signature.Signature), "mismatch signature")

err = tx.Verify()
require.NoError(t1, err, "error verifying tx")
msgBts, err := tx.SerializeMsg()
require.NoError(t1, err, "error serializing message")

authenticator := tt.args.signer.Authenticator()
err = authenticator.Verify(tx.Sender, msgBts, tx.Signature.Signature)
require.NoError(t1, err, "error verifying message")
})
}
}
Expand Down
17 changes: 17 additions & 0 deletions extensions/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,20 @@ Build constraints a.k.a. build tags are used to enable extensions in a kwild
binary. See README.md in the extensions package for more information.
*/
package auth

import (
"github.com/kwilteam/kwil-db/core/crypto/auth"

// internal/ident is home to the Authenticator registry used by kwild, as
// well as the registry-powered verification functions used by kwild. The
// RegisterAuthenticator helper is provided here so that extension
// implementations may register themselves on import, but it would be fine
// to shift that responsibility to the importing code in kwild (these stubs)
"github.com/kwilteam/kwil-db/internal/ident"
)

func RegisterAuthenticator(name string, auth auth.Authenticator) error {
return ident.RegisterAuthenticator(name, auth)
}

// var RegisterAuthenticator = ident.RegisterAuthenticator
2 changes: 1 addition & 1 deletion extensions/auth/ed25519_sha256.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

func init() {
err := auth.RegisterAuthenticator(Ed25519Sha256Auth, Ed22519Sha256Authenticator{})
err := RegisterAuthenticator(Ed25519Sha256Auth, Ed22519Sha256Authenticator{})
if err != nil {
panic(err)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/abci/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/kwilteam/kwil-db/core/types/transactions"
"github.com/kwilteam/kwil-db/core/utils"
engineTypes "github.com/kwilteam/kwil-db/internal/engine/types"
"github.com/kwilteam/kwil-db/internal/ident"
"github.com/kwilteam/kwil-db/internal/kv"
modDataset "github.com/kwilteam/kwil-db/internal/modules/datasets"
modVal "github.com/kwilteam/kwil-db/internal/modules/validators"
Expand Down Expand Up @@ -236,7 +237,7 @@ func (a *AbciApp) CheckTx(incoming abciTypes.RequestCheckTx) abciTypes.ResponseC
}

// Verify Signature
err = tx.Verify()
err = ident.VerifyTransaction(tx)
if err != nil {
code = CodeInvalidSignature
logger.Error("failed to verify transaction", zap.Error(err))
Expand Down
Loading

0 comments on commit 33b9871

Please sign in to comment.