Skip to content

Commit

Permalink
added extensible auth using build tags (#339)
Browse files Browse the repository at this point in the history
* added extensible auth using build tags

* go mod tidied

* made gavins requested changes
  • Loading branch information
brennanjl authored and charithabandi committed Oct 9, 2023
1 parent cb6e8cb commit ae3e964
Show file tree
Hide file tree
Showing 39 changed files with 845 additions and 423 deletions.
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ tasks:
test:unit:
desc: Run unit tests
cmds:
- go test $(go list ./... | grep -v /kwil-db\/test/) -count=1
- go test $(go list ./... | grep -v /kwil-db\/test/) -tags=ext_test -count=1

test:unit:race:
desc: Run unit tests with race
Expand Down
2 changes: 1 addition & 1 deletion cmd/kwil-cli/cmds/utils/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func getExampleTxQueryResponse() *types.TcTxQueryResponse {
secp256k1EpSigBytes, _ := hex.DecodeString(secp256k1EpSigHex)
secpSig := auth.Signature{
Signature: secp256k1EpSigBytes,
Type: auth.EthAuth,
Type: auth.EthPersonalSignAuth,
}

rawPayload := transactions.ActionExecution{
Expand Down
2 changes: 2 additions & 0 deletions cmd/kwild/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (

"github.com/spf13/cobra"
"github.com/spf13/viper"

_ "github.com/kwilteam/kwil-db/extensions/auth"
)

var (
Expand Down
21 changes: 21 additions & 0 deletions extensions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Extensions

Extensions are compile-time loaded pieces of code that impact core `kwild` functionality. Typically, extensions impact core consensus code, and therefore great care should be taken when implementing and choosing to use certain extensions.

## Interfaces and Drivers

Extensions can be made by implementing a driver for one of many interfaces. These implementations should be registered using Go's `init()` function, which will register the driver when the package is loaded. This is conceptually similar to Go's `database/sql` package, where users can implement custom `database/sql/driver/Driver` implementations.

## Build Tags

To include an extension in a build, users should use [Go's build tags](<https://www.digitalocean.com/community/tutorials/customizing-go-binaries-with-build-tags>). Users can specify what extensions they include by including their respective tags:

### Tag Naming

While you can give any name to your extension's tag, this repo adopts the best practice of prefixing the type of extension with the rest of the name. For example, if we were adding an extension that added standard RSA signatures for authentication, we might name the build tag `auth_rsa`. We could then include this by running:

```bash
go build -tags auth_rsa
```

Additionally, the build tag `ext_test` is added if the extension should be included as a part of `kwild`'s automated testing.
1 change: 1 addition & 0 deletions extensions/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package auth
58 changes: 58 additions & 0 deletions extensions/auth/ed25519_sha256.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//go:build auth_ed25519_sha256 || ext_test

package auth

import (
"crypto/ed25519"
"crypto/sha256"
"encoding/hex"
"fmt"

"github.com/kwilteam/kwil-db/pkg/auth"
"github.com/kwilteam/kwil-db/pkg/crypto"
)

func init() {
err := auth.RegisterAuthenticator(Ed25519Sha256Auth, Ed22519Sha256Authenticator{})
if err != nil {
panic(err)
}
}

const (
// Ed25519Sha256Auth is the authenticator name
// the "nr" suffix is for NEAR, and provides backwards compatibility
Ed25519Sha256Auth = "ed25519_nr"
// ed25519SignatureLength is the expected length of a signature
ed25519SignatureLength = 64
)

// Ed22519Sha256Authenticator is an authenticator that applies the sha256 hash to the message
// before verifying the signature. This is a common standard in ecosystems like NEAR.
type Ed22519Sha256Authenticator struct{}

var _ auth.Authenticator = Ed22519Sha256Authenticator{}

// Address generates a NEAR implicit address from a public key
func (e Ed22519Sha256Authenticator) Address(publicKey []byte) (string, error) {
if len(publicKey) != ed25519.PublicKeySize {
return "", fmt.Errorf("invalid ed25519 public key size for generating near address: %d", len(publicKey))
}

return hex.EncodeToString(publicKey), nil
}

// Verify verifies the signature against the given public key and data.
func (e Ed22519Sha256Authenticator) Verify(publicKey []byte, msg []byte, signature []byte) error {
pubkey, err := crypto.Ed25519PublicKeyFromBytes(publicKey)
if err != nil {
return err
}

if len(signature) != ed25519SignatureLength {
return fmt.Errorf("invalid signature length: expected %d, received %d", ed25519SignatureLength, len(signature))
}

hash := sha256.Sum256(msg)
return pubkey.Verify(signature, hash[:])
}
27 changes: 27 additions & 0 deletions extensions/auth/ed25519_sha256_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build auth_ed25519_sha256 || ext_test

package auth_test

import (
"encoding/hex"
"testing"

"github.com/kwilteam/kwil-db/extensions/auth"
"github.com/stretchr/testify/require"
)

func Test_Ed25519Sha256Near(t *testing.T) {
publicKey := "0aa611bf555596912bc6f9a9f169f8785918e7bab9924001895798ff13f05842"
signature := "089bcf52220dad77abc2cfcb1639bcb2944fdf64e0b173f40cd0d144bdbf7808f4eff3716eb3e98ed40be3ab126e1449d5f57efbe5626673059edc90e9cd9801"
message := []byte("foo")
pubKeyBts, err := hex.DecodeString(publicKey)
require.NoError(t, err, "error decode public key")

signatureBts, err := hex.DecodeString(signature)
require.NoError(t, err, "error decode signature")

authenticator := auth.Ed22519Sha256Authenticator{}

err = authenticator.Verify(pubKeyBts, message, signatureBts)
require.NoError(t, err, "error verifying signature")
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ require (
github.com/tidwall/wal v1.1.7
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
go.uber.org/zap v1.25.0
golang.org/x/crypto v0.12.0
golang.org/x/sync v0.3.0
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e
google.golang.org/grpc v1.57.0
Expand Down Expand Up @@ -222,6 +221,7 @@ require (
go.opentelemetry.io/otel/trace v1.15.1 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
Expand Down
12 changes: 10 additions & 2 deletions internal/app/kwild/server/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func buildServer(d *coreDependencies, closers *closeFuncs) *Server {

bootstrapperModule := buildBootstrapper(d)

abciApp := buildAbci(d, closers, datasetsModule, validatorModule,
abciApp := buildAbci(d, closers, accs, datasetsModule, validatorModule,
ac, snapshotModule, bootstrapperModule)

cometBftNode := buildCometNode(d, closers, abciApp)
Expand Down Expand Up @@ -132,7 +132,7 @@ func (c *closeFuncs) closeAll() error {
return err
}

func buildAbci(d *coreDependencies, closer *closeFuncs, datasetsModule abci.DatasetsModule, validatorModule abci.ValidatorModule,
func buildAbci(d *coreDependencies, closer *closeFuncs, accountsModule abci.AccountsModule, datasetsModule abci.DatasetsModule, validatorModule abci.ValidatorModule,
atomicCommitter *sessions.AtomicCommitter, snapshotter *snapshots.SnapshotStore, bootstrapper *snapshots.Bootstrapper) *abci.AbciApp {
badgerPath := filepath.Join(d.cfg.RootDir, abciDirName, kwild.ABCIInfoSubDirName)
badgerKv, err := badger.NewBadgerDB(d.ctx, badgerPath, &badger.Options{
Expand All @@ -157,6 +157,7 @@ func buildAbci(d *coreDependencies, closer *closeFuncs, datasetsModule abci.Data

genesisHash := d.genesisCfg.ComputeGenesisHash()
return abci.NewAbciApp(
accountsModule,
datasetsModule,
validatorModule,
atomicKv,
Expand All @@ -165,6 +166,7 @@ func buildAbci(d *coreDependencies, closer *closeFuncs, datasetsModule abci.Data
bootstrapper,
genesisHash,
abci.WithLogger(*d.log.Named("abci")),
abci.WithoutGasCosts(d.genesisCfg.ConsensusParams.WithoutGasCosts),
)
}

Expand Down Expand Up @@ -251,9 +253,15 @@ func buildValidatorManager(d *coreDependencies, closer *closeFuncs, ac *sessions
closer.addCloser(db.Close)

joinExpiry := d.genesisCfg.ConsensusParams.Validator.JoinExpiry
feeMultiplier := 1
if d.genesisCfg.ConsensusParams.WithoutGasCosts {
feeMultiplier = 0
}

v, err := vmgr.NewValidatorMgr(d.ctx, db,
vmgr.WithLogger(*d.log.Named("validatorStore")),
vmgr.WithJoinExpiry(joinExpiry),
vmgr.WithFeeMultiplier(int64(feeMultiplier)),
)
if err != nil {
failBuild(err, "failed to build validator store")
Expand Down
6 changes: 3 additions & 3 deletions internal/controller/grpc/txsvc/v1/pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ func (s *Service) priceAction(ctx context.Context, txBody *txpb.Transaction_Body

// TODO: Later to be moved to validator module (or) manager
func (s *Service) priceValidatorJoin(ctx context.Context, txBody *txpb.Transaction_Body) (*big.Int, error) {
return big.NewInt(10000000000000), nil
return s.vstore.PriceJoin(ctx)
}

func (s *Service) priceValidatorLeave(ctx context.Context, txBody *txpb.Transaction_Body) (*big.Int, error) {
return big.NewInt(10000000000000), nil
return s.vstore.PriceLeave(ctx)
}

func (s *Service) priceValidatorApprove(ctx context.Context, txBody *txpb.Transaction_Body) (*big.Int, error) {
return big.NewInt(10000000000000), nil
return s.vstore.PriceApprove(ctx)
}
3 changes: 3 additions & 0 deletions internal/controller/grpc/txsvc/v1/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@ type ValidatorReader interface {
CurrentValidators(ctx context.Context) ([]*validators.Validator, error)
ActiveVotes(ctx context.Context) ([]*validators.JoinRequest, error)
// JoinStatus(ctx context.Context, joiner []byte) ([]*JoinRequest, error)
PriceJoin(ctx context.Context) (*big.Int, error)
PriceLeave(ctx context.Context) (*big.Int, error)
PriceApprove(ctx context.Context) (*big.Int, error)
}
Loading

0 comments on commit ae3e964

Please sign in to comment.