-
Notifications
You must be signed in to change notification settings - Fork 240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem: no exposed db keyring used in the keystore #1419
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,18 @@ | ||
package keyring | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"reflect" | ||
Check notice Code scanning / CodeQL Sensitive package import Note
Certain system packages contain functions which may be a possible source of non-determinism
|
||
"unsafe" | ||
Check notice Code scanning / CodeQL Sensitive package import Note
Certain system packages contain functions which may be a possible source of non-determinism
|
||
|
||
"github.com/99designs/keyring" | ||
"golang.org/x/crypto/bcrypt" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
"github.com/cosmos/cosmos-sdk/client/input" | ||
sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
) | ||
|
||
const ( | ||
keyringFileDirName = "e2ee-keyring-file" | ||
keyringTestDirName = "e2ee-keyring-test" | ||
passKeyringPrefix = "e2ee-keyring-%s" //nolint: gosec | ||
maxPassphraseEntryAttempts = 3 | ||
) | ||
const keyringDirPrefix = "e2ee-keyring-%s" | ||
|
||
type Keyring interface { | ||
Get(string) ([]byte, error) | ||
|
@@ -30,60 +22,31 @@ | |
func New( | ||
appName, backend, rootDir string, userInput io.Reader, | ||
) (Keyring, error) { | ||
var ( | ||
db keyring.Keyring | ||
err error | ||
) | ||
serviceName := appName + "-e2ee" | ||
switch backend { | ||
case sdkkeyring.BackendMemory: | ||
return newKeystore(keyring.NewArrayKeyring(nil), sdkkeyring.BackendMemory), nil | ||
case sdkkeyring.BackendTest: | ||
db, err = keyring.Open(keyring.Config{ | ||
AllowedBackends: []keyring.BackendType{keyring.FileBackend}, | ||
ServiceName: serviceName, | ||
FileDir: filepath.Join(rootDir, keyringTestDirName), | ||
FilePasswordFunc: func(_ string) (string, error) { | ||
return "test", nil | ||
}, | ||
}) | ||
case sdkkeyring.BackendFile: | ||
fileDir := filepath.Join(rootDir, keyringFileDirName) | ||
db, err = keyring.Open(keyring.Config{ | ||
AllowedBackends: []keyring.BackendType{keyring.FileBackend}, | ||
ServiceName: serviceName, | ||
FileDir: fileDir, | ||
FilePasswordFunc: newRealPrompt(fileDir, userInput), | ||
}) | ||
case sdkkeyring.BackendOS: | ||
db, err = keyring.Open(keyring.Config{ | ||
ServiceName: serviceName, | ||
FileDir: rootDir, | ||
KeychainTrustApplication: true, | ||
FilePasswordFunc: newRealPrompt(rootDir, userInput), | ||
}) | ||
case sdkkeyring.BackendKWallet: | ||
db, err = keyring.Open(keyring.Config{ | ||
AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, | ||
ServiceName: "kdewallet", | ||
KWalletAppID: serviceName, | ||
KWalletFolder: "", | ||
}) | ||
case sdkkeyring.BackendPass: | ||
prefix := fmt.Sprintf(passKeyringPrefix, serviceName) | ||
db, err = keyring.Open(keyring.Config{ | ||
AllowedBackends: []keyring.BackendType{keyring.PassBackend}, | ||
ServiceName: serviceName, | ||
PassPrefix: prefix, | ||
}) | ||
default: | ||
return nil, errorsmod.Wrap(sdkkeyring.ErrUnknownBacked, backend) | ||
} | ||
|
||
if err != nil { | ||
return nil, err | ||
var db keyring.Keyring | ||
if backend == sdkkeyring.BackendMemory { | ||
db = keyring.NewArrayKeyring(nil) | ||
} else { | ||
kr, err := sdkkeyring.New(serviceName, backend, rootDir, userInput, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
db = kr.DB() | ||
switch backend { | ||
case sdkkeyring.BackendTest, sdkkeyring.BackendFile: | ||
fileDir := filepath.Join(rootDir, fmt.Sprintf(keyringDirPrefix, backend)) | ||
el := reflect.ValueOf(db).Elem() | ||
if f := el.FieldByName("dir"); f.IsValid() { | ||
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().SetString(fileDir) | ||
} | ||
case sdkkeyring.BackendPass: | ||
prefix := fmt.Sprintf(keyringDirPrefix, serviceName) | ||
el := reflect.ValueOf(db).Elem() | ||
if f := el.FieldByName("prefix"); f.IsValid() { | ||
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().SetString(prefix) | ||
} | ||
} | ||
Comment on lines
+26
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of reflection and unsafe operations in the |
||
} | ||
|
||
return newKeystore(db, backend), nil | ||
} | ||
|
||
|
@@ -117,86 +80,3 @@ | |
Label: name, | ||
}) | ||
} | ||
|
||
func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { | ||
return func(prompt string) (string, error) { | ||
keyhashStored := false | ||
keyhashFilePath := filepath.Join(dir, "keyhash") | ||
|
||
var keyhash []byte | ||
|
||
_, err := os.Stat(keyhashFilePath) | ||
|
||
switch { | ||
case err == nil: | ||
keyhash, err = os.ReadFile(keyhashFilePath) | ||
if err != nil { | ||
return "", errorsmod.Wrap(err, fmt.Sprintf("failed to read %s", keyhashFilePath)) | ||
} | ||
|
||
keyhashStored = true | ||
|
||
case os.IsNotExist(err): | ||
keyhashStored = false | ||
|
||
default: | ||
return "", errorsmod.Wrap(err, fmt.Sprintf("failed to open %s", keyhashFilePath)) | ||
} | ||
|
||
failureCounter := 0 | ||
|
||
for { | ||
failureCounter++ | ||
if failureCounter > maxPassphraseEntryAttempts { | ||
return "", sdkkeyring.ErrMaxPassPhraseAttempts | ||
} | ||
|
||
buf := bufio.NewReader(buf) | ||
pass, err := input.GetPassword(fmt.Sprintf("Enter keyring passphrase (attempt %d/%d):", failureCounter, maxPassphraseEntryAttempts), buf) | ||
if err != nil { | ||
// NOTE: LGTM.io reports a false positive alert that states we are printing the password, | ||
// but we only log the error. | ||
// | ||
// lgtm [go/clear-text-logging] | ||
fmt.Fprintln(os.Stderr, err) | ||
continue | ||
} | ||
|
||
if keyhashStored { | ||
if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { | ||
fmt.Fprintln(os.Stderr, "incorrect passphrase") | ||
continue | ||
} | ||
|
||
return pass, nil | ||
} | ||
|
||
reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) | ||
if err != nil { | ||
// NOTE: LGTM.io reports a false positive alert that states we are printing the password, | ||
// but we only log the error. | ||
// | ||
// lgtm [go/clear-text-logging] | ||
fmt.Fprintln(os.Stderr, err) | ||
continue | ||
} | ||
|
||
if pass != reEnteredPass { | ||
fmt.Fprintln(os.Stderr, "passphrase do not match") | ||
continue | ||
} | ||
|
||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(pass), 2) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
continue | ||
} | ||
|
||
if err := os.WriteFile(keyhashFilePath, passwordHash, 0o600); err != nil { | ||
return "", err | ||
} | ||
|
||
return pass, nil | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,18 @@ | ||
package keyring | ||
package keyring_test | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"testing" | ||
|
||
"filippo.io/age" | ||
"github.com/test-go/testify/require" | ||
|
||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
"github.com/crypto-org-chain/cronos/v2/x/e2ee/keyring" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestKeyring(t *testing.T) { | ||
kr, err := New("cronosd", keyring.BackendTest, t.TempDir(), nil) | ||
kr, err := keyring.New("cronosd", sdkkeyring.BackendTest, t.TempDir(), nil) | ||
require.NoError(t, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function call |
||
|
||
identity, err := age.GenerateX25519Identity() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure the PR number is specified for the e2ee module keyring simplification.
It appears that the PR number is missing in the changelog entry for the e2ee module keyring simplification. This should be corrected to maintain accurate documentation and traceability. Here's a suggested change:
Committable suggestion
Tools
LanguageTool