Skip to content

Commit c0eced8

Browse files
alessioAlessio Tregliakocubinskijulienrbrttac0turtle
authored
feat(crypto/keyring): add Linux's keyctl support (#21653)
Signed-off-by: Alessio Treglia <al@essio.dev> Co-authored-by: Alessio Treglia <alessio@jur.io> Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com> Co-authored-by: Julien Robert <julien@rbrt.fr> Co-authored-by: Marko <marko@baricevic.me>
1 parent 924798f commit c0eced8

File tree

6 files changed

+175
-18
lines changed

6 files changed

+175
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
4343
### Features
4444

4545
* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simulate nested messages.
46+
* (cli) [#21372](https://github.com/cosmos/cosmos-sdk/pull/21372) Add a `bulk-add-genesis-account` genesis command to add many genesis accounts at once.
47+
* (crypto/keyring) [#21653](https://github.com/cosmos/cosmos-sdk/pull/21653) New Linux-only backend that adds Linux kernel's `keyctl` support.
4648
* (runtime) [#21704](https://github.com/cosmos/cosmos-sdk/pull/21704) Add StoreLoader in simappv2.
4749

4850
### Improvements

crypto/keyring/doc.go

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

crypto/keyring/keyring.go

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -147,23 +147,6 @@ type Exporter interface {
147147
// Option overrides keyring configuration options.
148148
type Option func(options *Options)
149149

150-
// Options define the options of the Keyring.
151-
type Options struct {
152-
// supported signing algorithms for keyring
153-
SupportedAlgos SigningAlgoList
154-
// supported signing algorithms for Ledger
155-
SupportedAlgosLedger SigningAlgoList
156-
// define Ledger Derivation function
157-
LedgerDerivation func() (ledger.SECP256K1, error)
158-
// define Ledger key generation function
159-
LedgerCreateKey func([]byte) types.PubKey
160-
// define Ledger app name
161-
LedgerAppName string
162-
// indicate whether Ledger should skip DER Conversion on signature,
163-
// depending on which format (DER or BER) the Ledger app returns signatures
164-
LedgerSigSkipDERConv bool
165-
}
166-
167150
// NewInMemory creates a transient keyring useful for testing
168151
// purposes and on-the-fly key generation.
169152
// Keybase options can be applied when generating this new Keybase.
@@ -180,7 +163,7 @@ func NewInMemoryWithKeyring(kr keyring.Keyring, cdc codec.Codec, opts ...Option)
180163
// New creates a new instance of a keyring.
181164
// Keyring options can be applied when generating the new instance.
182165
// Available backends are "os", "file", "kwallet", "memory", "pass", "test".
183-
func New(
166+
func newKeyringGeneric(
184167
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
185168
) (Keyring, error) {
186169
var (

crypto/keyring/keyring_linux.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//go:build linux
2+
// +build linux
3+
4+
package keyring
5+
6+
import (
7+
"fmt"
8+
"io"
9+
10+
"github.com/99designs/keyring"
11+
12+
"github.com/cosmos/cosmos-sdk/codec"
13+
"github.com/cosmos/cosmos-sdk/crypto/ledger"
14+
"github.com/cosmos/cosmos-sdk/crypto/types"
15+
)
16+
17+
// Linux-only backend options.
18+
const BackendKeyctl = "keyctl"
19+
20+
func KeyctlScopeUser(options *Options) { setKeyctlScope(options, "user") }
21+
func KeyctlScopeUserSession(options *Options) { setKeyctlScope(options, "usersession") }
22+
func KeyctlScopeSession(options *Options) { setKeyctlScope(options, "session") }
23+
func KeyctlScopeProcess(options *Options) { setKeyctlScope(options, "process") }
24+
func KeyctlScopeThread(options *Options) { setKeyctlScope(options, "thread") }
25+
26+
// Options define the options of the Keyring.
27+
type Options struct {
28+
// supported signing algorithms for keyring
29+
SupportedAlgos SigningAlgoList
30+
// supported signing algorithms for Ledger
31+
SupportedAlgosLedger SigningAlgoList
32+
// define Ledger Derivation function
33+
LedgerDerivation func() (ledger.SECP256K1, error)
34+
// define Ledger key generation function
35+
LedgerCreateKey func([]byte) types.PubKey
36+
// define Ledger app name
37+
LedgerAppName string
38+
// indicate whether Ledger should skip DER Conversion on signature,
39+
// depending on which format (DER or BER) the Ledger app returns signatures
40+
LedgerSigSkipDERConv bool
41+
// KeyctlScope defines the scope of the keyctl's keyring.
42+
KeyctlScope string
43+
}
44+
45+
func newKeyctlBackendConfig(appName, _ string, _ io.Reader, opts ...Option) keyring.Config {
46+
options := Options{
47+
KeyctlScope: keyctlDefaultScope, // currently "process"
48+
}
49+
50+
for _, optionFn := range opts {
51+
optionFn(&options)
52+
}
53+
54+
return keyring.Config{
55+
AllowedBackends: []keyring.BackendType{keyring.KeyCtlBackend},
56+
ServiceName: appName,
57+
KeyCtlScope: options.KeyctlScope,
58+
}
59+
}
60+
61+
// New creates a new instance of a keyring.
62+
// Keyring options can be applied when generating the new instance.
63+
// Available backends are "os", "file", "kwallet", "memory", "pass", "test", "keyctl".
64+
func New(
65+
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
66+
) (Keyring, error) {
67+
if backend != BackendKeyctl {
68+
return newKeyringGeneric(appName, backend, rootDir, userInput, cdc, opts...)
69+
}
70+
71+
db, err := keyring.Open(newKeyctlBackendConfig(appName, "", userInput, opts...))
72+
if err != nil {
73+
return nil, fmt.Errorf("couldn't open keyring for %q: %w", appName, err)
74+
}
75+
76+
return newKeystore(db, cdc, backend, opts...), nil
77+
}
78+
79+
func setKeyctlScope(options *Options, scope string) { options.KeyctlScope = scope }
80+
81+
// this is private as it is meant to be here for SDK devs convenience
82+
// as the user does not need to pick any default when he wants to
83+
// initialize keyctl with the default scope.
84+
const keyctlDefaultScope = "process"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//go:build linux
2+
// +build linux
3+
4+
package keyring
5+
6+
import (
7+
"errors"
8+
"io"
9+
"strings"
10+
"testing"
11+
12+
"github.com/stretchr/testify/require"
13+
14+
"github.com/cosmos/cosmos-sdk/codec"
15+
)
16+
17+
func TestNewKeyctlKeyring(t *testing.T) {
18+
cdc := getCodec()
19+
20+
tests := []struct {
21+
name string
22+
appName string
23+
backend string
24+
dir string
25+
userInput io.Reader
26+
cdc codec.Codec
27+
expectedErr error
28+
}{
29+
{
30+
name: "keyctl backend",
31+
appName: "cosmos",
32+
backend: BackendKeyctl,
33+
dir: t.TempDir(),
34+
userInput: strings.NewReader(""),
35+
cdc: cdc,
36+
expectedErr: nil,
37+
},
38+
}
39+
for _, tt := range tests {
40+
t.Run(tt.name, func(t *testing.T) {
41+
kr, err := New(tt.appName, tt.backend, tt.dir, tt.userInput, tt.cdc)
42+
if tt.expectedErr == nil {
43+
require.NoError(t, err)
44+
} else {
45+
require.Error(t, err)
46+
require.Nil(t, kr)
47+
require.True(t, errors.Is(err, tt.expectedErr))
48+
}
49+
})
50+
}
51+
}

crypto/keyring/keyring_other.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build !linux
2+
// +build !linux
3+
4+
package keyring
5+
6+
import (
7+
"io"
8+
9+
"github.com/cosmos/cosmos-sdk/codec"
10+
"github.com/cosmos/cosmos-sdk/crypto/ledger"
11+
"github.com/cosmos/cosmos-sdk/crypto/types"
12+
)
13+
14+
// Options define the options of the Keyring.
15+
type Options struct {
16+
// supported signing algorithms for keyring
17+
SupportedAlgos SigningAlgoList
18+
// supported signing algorithms for Ledger
19+
SupportedAlgosLedger SigningAlgoList
20+
// define Ledger Derivation function
21+
LedgerDerivation func() (ledger.SECP256K1, error)
22+
// define Ledger key generation function
23+
LedgerCreateKey func([]byte) types.PubKey
24+
// define Ledger app name
25+
LedgerAppName string
26+
// indicate whether Ledger should skip DER Conversion on signature,
27+
// depending on which format (DER or BER) the Ledger app returns signatures
28+
LedgerSigSkipDERConv bool
29+
}
30+
31+
func New(
32+
appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option,
33+
) (Keyring, error) {
34+
return newKeyringGeneric(appName, backend, rootDir, userInput, cdc, opts...)
35+
}

0 commit comments

Comments
 (0)