Skip to content

Commit

Permalink
Add unlocking through api
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradStaniec committed Nov 27, 2024
1 parent 93839a8 commit 983edf2
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 34 deletions.
2 changes: 0 additions & 2 deletions covenant-signer/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ key-directory = "{{ .KeyStore.CosmosKeyStore.KeyDirectory }}"
keyring-backend = "{{ .KeyStore.CosmosKeyStore.KeyringBackend }}"
# The name of the key to use
key-name = "{{ .KeyStore.CosmosKeyStore.KeyName }}"
# Passphrase
passphrase = "{{ .KeyStore.CosmosKeyStore.Passphrase }}"
[server-config]
# The address to listen on
Expand Down
1 change: 0 additions & 1 deletion covenant-signer/config/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type CosmosKeyStoreConfig struct {
KeyDirectory string `mapstructure:"key-directory"`
KeyringBackend string `mapstructure:"keyring-backend"`
KeyName string `mapstructure:"key-name"`
Passphrase string `mapstructure:"passphrase"`
}

type KeyStoreConfig struct {
Expand Down
2 changes: 0 additions & 2 deletions covenant-signer/example/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ key-directory = ""
keyring-backend = ""
# The name of the key to use
key-name = ""
# Passphrase
passphrase = ""

[server-config]
# The address to listen on
Expand Down
56 changes: 50 additions & 6 deletions covenant-signer/itest/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,27 @@ func buildDataToSign(t *testing.T, covnenantPublicKey *btcec.PublicKey) signerap

func TestGetPublicKey(t *testing.T) {
tm := StartManager(t, 100, false)
// default passphrase is empty in non encrypted keyring
err := signerservice.Unlock(context.Background(), tm.SigningServerUrl(), 10*time.Second, "")
require.NoError(t, err)

pubKey, err := signerservice.GetPublicKey(context.Background(), tm.SigningServerUrl(), 10*time.Second)
require.NoError(t, err)
require.NotNil(t, pubKey)

pubKeyBytes := pubKey.SerializeCompressed()
require.Equal(t, hex.EncodeToString(pubKeyBytes), hex.EncodeToString(tm.covenantPrivKey.PubKey().SerializeCompressed()))
}

func TestSigningTransactions(t *testing.T) {
tm := StartManager(t, 100, false)
// default passphrase is empty in non encrypted keyring
err := signerservice.Unlock(context.Background(), tm.SigningServerUrl(), 10*time.Second, "")
require.NoError(t, err)

dataToSign := buildDataToSign(t, tm.covenantPrivKey.PubKey())
pubKey, err := signerservice.GetPublicKey(context.Background(), tm.SigningServerUrl(), 10*time.Second)
require.NoError(t, err)
require.NotNil(t, pubKey)

dataToSign := buildDataToSign(t, pubKey)

sigs, err := signerservice.RequestCovenantSignaure(
context.Background(),
Expand All @@ -155,7 +163,7 @@ func TestSigningTransactions(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, sigs)

err = tm.verifyResponse(sigs, &dataToSign)
err = tm.verifyResponse(sigs, &dataToSign, pubKey)
require.NoError(t, err)
}

Expand Down Expand Up @@ -194,7 +202,14 @@ func TestRejectToLargeRequest(t *testing.T) {
func TestSigningTransactionsUsingEncryptedFileKeyRing(t *testing.T) {
tm := StartManager(t, 100, true)

dataToSign := buildDataToSign(t, tm.covenantPrivKey.PubKey())
err := signerservice.Unlock(context.Background(), tm.SigningServerUrl(), 10*time.Second, "testtest")
require.NoError(t, err)

pubKey, err := signerservice.GetPublicKey(context.Background(), tm.SigningServerUrl(), 10*time.Second)
require.NoError(t, err)
require.NotNil(t, pubKey)

dataToSign := buildDataToSign(t, pubKey)

sigs, err := signerservice.RequestCovenantSignaure(
context.Background(),
Expand All @@ -206,6 +221,35 @@ func TestSigningTransactionsUsingEncryptedFileKeyRing(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, sigs)

err = tm.verifyResponse(sigs, &dataToSign)
err = tm.verifyResponse(sigs, &dataToSign, pubKey)
require.NoError(t, err)
}

func TestLockingKeyring(t *testing.T) {
tm := StartManager(t, 100, true)

err := signerservice.Unlock(context.Background(), tm.SigningServerUrl(), 10*time.Second, "testtest")
require.NoError(t, err)

pubKey, err := signerservice.GetPublicKey(context.Background(), tm.SigningServerUrl(), 10*time.Second)
require.NoError(t, err)
require.NotNil(t, pubKey)

dataToSign := buildDataToSign(t, pubKey)

// lock the keyring, and clear the private key from memory
err = signerservice.Lock(context.Background(), tm.SigningServerUrl(), 10*time.Second)
require.NoError(t, err)

// try to sign a transaction with a locked keyring, it should fail
sigs, err := signerservice.RequestCovenantSignaure(
context.Background(),
tm.SigningServerUrl(),
10*time.Second,
&dataToSign,
)

require.Error(t, err)
require.Nil(t, sigs)

}
19 changes: 8 additions & 11 deletions covenant-signer/itest/testmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type TestManager struct {
t *testing.T
bitcoindHandler *BitcoindTestHandler
walletPass string
covenantPrivKey *btcec.PrivateKey
signerConfig *config.Config
app *signerapp.SignerApp
server *signerservice.SigningServer
Expand Down Expand Up @@ -64,7 +63,6 @@ func StartManager(
appConfig.KeyStore.KeyStoreType = "cosmos"
// ChainID does not influence keyring with key imported from hex string
appConfig.KeyStore.CosmosKeyStore.ChainID = ""
appConfig.KeyStore.CosmosKeyStore.Passphrase = "testtest"
appConfig.KeyStore.CosmosKeyStore.KeyName = "test"
appConfig.KeyStore.CosmosKeyStore.KeyDirectory = "./testkeyring"
appConfig.KeyStore.CosmosKeyStore.KeyringBackend = "file"
Expand All @@ -74,7 +72,6 @@ func StartManager(
} else {
appConfig.KeyStore.KeyStoreType = "cosmos"
appConfig.KeyStore.CosmosKeyStore.ChainID = "test-chain"
appConfig.KeyStore.CosmosKeyStore.Passphrase = passphrase
appConfig.KeyStore.CosmosKeyStore.KeyName = "test-key"
appConfig.KeyStore.CosmosKeyStore.KeyDirectory = ""
appConfig.KeyStore.CosmosKeyStore.KeyringBackend = "memory"
Expand All @@ -96,9 +93,6 @@ func StartManager(
require.NoError(t, err)
}

privKey, err := retriever.PrivKey(context.Background())
require.NoError(t, err)

app := signerapp.NewSignerApp(
retriever,
)
Expand Down Expand Up @@ -131,7 +125,6 @@ func StartManager(
t: t,
bitcoindHandler: h,
walletPass: passphrase,
covenantPrivKey: privKey,
signerConfig: appConfig,
app: app,
server: server,
Expand All @@ -142,7 +135,11 @@ func (tm *TestManager) SigningServerUrl() string {
return fmt.Sprintf("http://%s:%d", tm.signerConfig.Server.Host, tm.signerConfig.Server.Port)
}

func (tm *TestManager) verifyResponse(resp *signerapp.ParsedSigningResponse, req *signerapp.ParsedSigningRequest) error {
func (tm *TestManager) verifyResponse(
resp *signerapp.ParsedSigningResponse,
req *signerapp.ParsedSigningRequest,
covenantPubKey *btcec.PublicKey,
) error {

slashAdaptorSig, err := asig.NewAdaptorSignatureFromBytes(resp.SlashAdaptorSigs[0])

Expand All @@ -154,7 +151,7 @@ func (tm *TestManager) verifyResponse(resp *signerapp.ParsedSigningResponse, req
req.SlashingTx,
req.StakingTx.TxOut[req.StakingOutputIdx],
req.SlashingScript,
tm.covenantPrivKey.PubKey(),
covenantPubKey,
req.FpEncKeys[0],
slashAdaptorSig,
)
Expand All @@ -173,7 +170,7 @@ func (tm *TestManager) verifyResponse(resp *signerapp.ParsedSigningResponse, req
req.SlashUnbondingTx,
req.UnbondingTx.TxOut[0],
req.UnbondingSlashingScript,
tm.covenantPrivKey.PubKey(),
covenantPubKey,
req.FpEncKeys[0],
slashUnbondingAdaptorSig,
)
Expand All @@ -186,7 +183,7 @@ func (tm *TestManager) verifyResponse(resp *signerapp.ParsedSigningResponse, req
req.UnbondingTx,
req.StakingTx.TxOut[req.StakingOutputIdx],
req.UnbondingScript,
tm.covenantPrivKey.PubKey(),
covenantPubKey,
resp.UnbondingSig.Serialize(),
)

Expand Down
53 changes: 46 additions & 7 deletions covenant-signer/keystore/cosmos/cosmoskeyretriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"sync"

"github.com/babylonlabs-io/covenant-emulator/covenant-signer/config"
"github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerapp"
Expand All @@ -13,8 +14,9 @@ import (
var _ signerapp.PrivKeyRetriever = &CosmosKeyringRetriever{}

type CosmosKeyringRetriever struct {
Kr *ChainKeyringController
passphrase string
Kr *ChainKeyringController
mu sync.Mutex
btcecPrivKey *btcec.PrivateKey
}

func NewCosmosKeyringRetriever(cfg *config.CosmosKeyStoreConfig) (*CosmosKeyringRetriever, error) {
Expand All @@ -29,18 +31,55 @@ func NewCosmosKeyringRetriever(cfg *config.CosmosKeyStoreConfig) (*CosmosKeyring
return nil, err
}
return &CosmosKeyringRetriever{
Kr: kc,
passphrase: cfg.Passphrase,
Kr: kc,
btcecPrivKey: nil,
}, nil
}

func (k *CosmosKeyringRetriever) PrivKey(ctx context.Context) (*btcec.PrivateKey, error) {
privKey, err := k.Kr.GetChainPrivKey(k.passphrase)
k.mu.Lock()
defer k.mu.Unlock()

if k.btcecPrivKey == nil {
return nil, fmt.Errorf("private key is not unlocked. Please call Unlock() first")
}

return k.btcecPrivKey, nil
}

func (k *CosmosKeyringRetriever) Unlock(ctx context.Context, passphrase string) error {
k.mu.Lock()
defer k.mu.Unlock()

if k.btcecPrivKey != nil {
// already unlocked
return nil
}

privKey, err := k.Kr.GetChainPrivKey(passphrase)
if err != nil {
return nil, err
return fmt.Errorf("failed to unlock the key ring: %w", err)
}

btcecPrivKey, _ := btcec.PrivKeyFromBytes(privKey.Key)

return btcecPrivKey, nil
k.btcecPrivKey = btcecPrivKey
return nil
}

func (k *CosmosKeyringRetriever) Lock(ctx context.Context) error {
k.mu.Lock()
defer k.mu.Unlock()

if k.btcecPrivKey == nil {
// already locked
return nil
}

// First zero out the memory associated with the private key
k.btcecPrivKey.Zero()
// Clear the reference to the private key
k.btcecPrivKey = nil

return nil
}
2 changes: 2 additions & 0 deletions covenant-signer/signerapp/expected_interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ import (
// the signing
type PrivKeyRetriever interface {
PrivKey(ctx context.Context) (*btcec.PrivateKey, error)
Unlock(ctx context.Context, passphrase string) error
Lock(ctx context.Context) error
}
10 changes: 10 additions & 0 deletions covenant-signer/signerapp/hardcodedprivkeyretriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ func (r *HardcodedPrivKeyRetriever) PrivKey(ctx context.Context) (*btcec.Private

return newPrivKey, nil
}

// Key is always unlocked in this implementation
func (r *HardcodedPrivKeyRetriever) Unlock(ctx context.Context, passphrase string) error {
return nil
}

// Key is always unlocked in this implementation
func (r *HardcodedPrivKeyRetriever) Lock(ctx context.Context) error {
return nil
}
13 changes: 8 additions & 5 deletions covenant-signer/signerapp/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ func (s *SignerApp) SignTransactions(
) (*ParsedSigningResponse, error) {
privKey, err := s.pkr.PrivKey(ctx)

defer func() {
privKey.Zero()
}()

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -78,12 +74,19 @@ func (s *SignerApp) SignTransactions(
}, nil
}

func (s *SignerApp) Unlock(ctx context.Context, passphrase string) error {
return s.pkr.Unlock(ctx, passphrase)
}

func (s *SignerApp) Lock(ctx context.Context) error {
return s.pkr.Lock(ctx)
}

func (s *SignerApp) PubKey(ctx context.Context) (*btcec.PublicKey, error) {
privKey, err := s.pkr.PrivKey(ctx)
if err != nil {
return nil, err
}
defer privKey.Zero()

return privKey.PubKey(), nil
}
Expand Down
Loading

0 comments on commit 983edf2

Please sign in to comment.