feat(crypto/keyring): add Linux's keyctl support (backport #21653) (#21839)

Co-authored-by: Alessio Treglia <al@essio.dev>
Co-authored-by: marbar3778 <marbar3778@yahoo.com>
This commit is contained in:
mergify[bot] 2024-09-21 11:10:31 +00:00 committed by GitHub
parent e655fd535e
commit d4bde7b614
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 173 additions and 18 deletions

View File

@ -33,6 +33,8 @@
// https://github.com/KDE/kwallet
// pass This backend uses the pass command line utility to store and retrieve keys:
// https://www.passwordstore.org/
// keyctl This backend leverages the Linux's kernel security key management system
// to store cryptographic keys securely in memory. This is available on Linux only.
// test This backend stores keys insecurely to disk. It does not prompt for a password to
// be unlocked and it should be used only for testing purposes.
// memory Same instance as returned by NewInMemory. This backend uses a transient storage. Keys

View File

@ -147,23 +147,6 @@ type Exporter interface {
// Option overrides keyring configuration options.
type Option func(options *Options)
// Options define the options of the Keyring.
type Options struct {
// supported signing algorithms for keyring
SupportedAlgos SigningAlgoList
// supported signing algorithms for Ledger
SupportedAlgosLedger SigningAlgoList
// define Ledger Derivation function
LedgerDerivation func() (ledger.SECP256K1, error)
// define Ledger key generation function
LedgerCreateKey func([]byte) types.PubKey
// define Ledger app name
LedgerAppName string
// indicate whether Ledger should skip DER Conversion on signature,
// depending on which format (DER or BER) the Ledger app returns signatures
LedgerSigSkipDERConv bool
}
// NewInMemory creates a transient keyring useful for testing
// purposes and on-the-fly key generation.
// Keybase options can be applied when generating this new Keybase.
@ -180,7 +163,7 @@ func NewInMemoryWithKeyring(kr keyring.Keyring, cdc codec.Codec, opts ...Option)
// New creates a new instance of a keyring.
// Keyring options can be applied when generating the new instance.
// Available backends are "os", "file", "kwallet", "memory", "pass", "test".
func New(
func newKeyringGeneric(
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
) (Keyring, error) {
var (

View File

@ -0,0 +1,84 @@
//go:build linux
// +build linux
package keyring
import (
"fmt"
"io"
"github.com/99designs/keyring"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/ledger"
"github.com/cosmos/cosmos-sdk/crypto/types"
)
// Linux-only backend options.
const BackendKeyctl = "keyctl"
func KeyctlScopeUser(options *Options) { setKeyctlScope(options, "user") }
func KeyctlScopeUserSession(options *Options) { setKeyctlScope(options, "usersession") }
func KeyctlScopeSession(options *Options) { setKeyctlScope(options, "session") }
func KeyctlScopeProcess(options *Options) { setKeyctlScope(options, "process") }
func KeyctlScopeThread(options *Options) { setKeyctlScope(options, "thread") }
// Options define the options of the Keyring.
type Options struct {
// supported signing algorithms for keyring
SupportedAlgos SigningAlgoList
// supported signing algorithms for Ledger
SupportedAlgosLedger SigningAlgoList
// define Ledger Derivation function
LedgerDerivation func() (ledger.SECP256K1, error)
// define Ledger key generation function
LedgerCreateKey func([]byte) types.PubKey
// define Ledger app name
LedgerAppName string
// indicate whether Ledger should skip DER Conversion on signature,
// depending on which format (DER or BER) the Ledger app returns signatures
LedgerSigSkipDERConv bool
// KeyctlScope defines the scope of the keyctl's keyring.
KeyctlScope string
}
func newKeyctlBackendConfig(appName, _ string, _ io.Reader, opts ...Option) keyring.Config {
options := Options{
KeyctlScope: keyctlDefaultScope, // currently "process"
}
for _, optionFn := range opts {
optionFn(&options)
}
return keyring.Config{
AllowedBackends: []keyring.BackendType{keyring.KeyCtlBackend},
ServiceName: appName,
KeyCtlScope: options.KeyctlScope,
}
}
// New creates a new instance of a keyring.
// Keyring options can be applied when generating the new instance.
// Available backends are "os", "file", "kwallet", "memory", "pass", "test", "keyctl".
func New(
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
) (Keyring, error) {
if backend != BackendKeyctl {
return newKeyringGeneric(appName, backend, rootDir, userInput, cdc, opts...)
}
db, err := keyring.Open(newKeyctlBackendConfig(appName, "", userInput, opts...))
if err != nil {
return nil, fmt.Errorf("couldn't open keyring for %q: %w", appName, err)
}
return newKeystore(db, cdc, backend, opts...), nil
}
func setKeyctlScope(options *Options, scope string) { options.KeyctlScope = scope }
// this is private as it is meant to be here for SDK devs convenience
// as the user does not need to pick any default when he wants to
// initialize keyctl with the default scope.
const keyctlDefaultScope = "process"

View File

@ -0,0 +1,51 @@
//go:build linux
// +build linux
package keyring
import (
"errors"
"io"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
)
func TestNewKeyctlKeyring(t *testing.T) {
cdc := getCodec()
tests := []struct {
name string
appName string
backend string
dir string
userInput io.Reader
cdc codec.Codec
expectedErr error
}{
{
name: "keyctl backend",
appName: "cosmos",
backend: BackendKeyctl,
dir: t.TempDir(),
userInput: strings.NewReader(""),
cdc: cdc,
expectedErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kr, err := New(tt.appName, tt.backend, tt.dir, tt.userInput, tt.cdc)
if tt.expectedErr == nil {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Nil(t, kr)
require.True(t, errors.Is(err, tt.expectedErr))
}
})
}
}

View File

@ -0,0 +1,35 @@
//go:build !linux
// +build !linux
package keyring
import (
"io"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/ledger"
"github.com/cosmos/cosmos-sdk/crypto/types"
)
// Options define the options of the Keyring.
type Options struct {
// supported signing algorithms for keyring
SupportedAlgos SigningAlgoList
// supported signing algorithms for Ledger
SupportedAlgosLedger SigningAlgoList
// define Ledger Derivation function
LedgerDerivation func() (ledger.SECP256K1, error)
// define Ledger key generation function
LedgerCreateKey func([]byte) types.PubKey
// define Ledger app name
LedgerAppName string
// indicate whether Ledger should skip DER Conversion on signature,
// depending on which format (DER or BER) the Ledger app returns signatures
LedgerSigSkipDERConv bool
}
func New(
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
) (Keyring, error) {
return newKeyringGeneric(appName, backend, rootDir, userInput, cdc, opts...)
}