github.com/cosmos/cosmos-sdk@v0.50.10/crypto/keyring/keyring.go (about) 1 package keyring 2 3 import ( 4 "bufio" 5 "encoding/hex" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "sort" 11 "strings" 12 13 "github.com/99designs/keyring" 14 "github.com/cockroachdb/errors" 15 "github.com/cosmos/go-bip39" 16 "golang.org/x/crypto/bcrypt" 17 18 errorsmod "cosmossdk.io/errors" 19 20 "github.com/cosmos/cosmos-sdk/client/input" 21 "github.com/cosmos/cosmos-sdk/codec" 22 "github.com/cosmos/cosmos-sdk/crypto" 23 "github.com/cosmos/cosmos-sdk/crypto/hd" 24 "github.com/cosmos/cosmos-sdk/crypto/ledger" 25 "github.com/cosmos/cosmos-sdk/crypto/types" 26 sdk "github.com/cosmos/cosmos-sdk/types" 27 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 28 "github.com/cosmos/cosmos-sdk/types/tx/signing" 29 ) 30 31 // Backend options for Keyring 32 const ( 33 BackendFile = "file" 34 BackendOS = "os" 35 BackendKWallet = "kwallet" 36 BackendPass = "pass" 37 BackendTest = "test" 38 BackendMemory = "memory" 39 ) 40 41 const ( 42 keyringFileDirName = "keyring-file" 43 keyringTestDirName = "keyring-test" 44 passKeyringPrefix = "keyring-%s" 45 46 // temporary pass phrase for exporting a key during a key rename 47 passPhrase = "temp" 48 // prefix for exported hex private keys 49 hexPrefix = "0x" 50 ) 51 52 var ( 53 _ Keyring = &keystore{} 54 maxPassphraseEntryAttempts = 3 55 ) 56 57 // Keyring exposes operations over a backend supported by github.com/99designs/keyring. 58 type Keyring interface { 59 // Get the backend type used in the keyring config: "file", "os", "kwallet", "pass", "test", "memory". 60 Backend() string 61 // List all keys. 62 List() ([]*Record, error) 63 64 // Supported signing algorithms for Keyring and Ledger respectively. 65 SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) 66 67 // Key and KeyByAddress return keys by uid and address respectively. 68 Key(uid string) (*Record, error) 69 KeyByAddress(address sdk.Address) (*Record, error) 70 71 // Delete and DeleteByAddress remove keys from the keyring. 72 Delete(uid string) error 73 DeleteByAddress(address sdk.Address) error 74 75 // Rename an existing key from the Keyring 76 Rename(from, to string) error 77 78 // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and 79 // persists the key to storage. Returns the generated mnemonic and the key Info. 80 // It returns an error if it fails to generate a key for the given algo type, or if 81 // another key is already stored under the same name or address. 82 // 83 // A passphrase set to the empty string will set the passphrase to the DefaultBIP39Passphrase value. 84 NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (*Record, string, error) 85 86 // NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it. 87 // It fails if there is an existing key Info with the same address. 88 NewAccount(uid, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (*Record, error) 89 90 // SaveLedgerKey retrieves a public key reference from a Ledger device and persists it. 91 SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (*Record, error) 92 93 // SaveOfflineKey stores a public key and returns the persisted Info structure. 94 SaveOfflineKey(uid string, pubkey types.PubKey) (*Record, error) 95 96 // SaveMultisig stores and returns a new multsig (offline) key reference. 97 SaveMultisig(uid string, pubkey types.PubKey) (*Record, error) 98 99 Signer 100 101 Importer 102 Exporter 103 104 Migrator 105 } 106 107 // Signer is implemented by key stores that want to provide signing capabilities. 108 type Signer interface { 109 // Sign sign byte messages with a user key. 110 Sign(uid string, msg []byte, signMode signing.SignMode) ([]byte, types.PubKey, error) 111 112 // SignByAddress sign byte messages with a user key providing the address. 113 SignByAddress(address sdk.Address, msg []byte, signMode signing.SignMode) ([]byte, types.PubKey, error) 114 } 115 116 // Importer is implemented by key stores that support import of public and private keys. 117 type Importer interface { 118 // ImportPrivKey imports ASCII armored passphrase-encrypted private keys. 119 ImportPrivKey(uid, armor, passphrase string) error 120 // ImportPrivKeyHex imports hex encoded keys. 121 ImportPrivKeyHex(uid, privKey, algoStr string) error 122 // ImportPubKey imports ASCII armored public keys. 123 ImportPubKey(uid, armor string) error 124 } 125 126 // Migrator is implemented by key stores and enables migration of keys from amino to proto 127 type Migrator interface { 128 MigrateAll() ([]*Record, error) 129 } 130 131 // Exporter is implemented by key stores that support export of public and private keys. 132 type Exporter interface { 133 // Export public key 134 ExportPubKeyArmor(uid string) (string, error) 135 ExportPubKeyArmorByAddress(address sdk.Address) (string, error) 136 137 // ExportPrivKeyArmor returns a private key in ASCII armored format. 138 // It returns an error if the key does not exist or a wrong encryption passphrase is supplied. 139 ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) 140 ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) 141 } 142 143 // Option overrides keyring configuration options. 144 type Option func(options *Options) 145 146 // Options define the options of the Keyring. 147 type Options struct { 148 // supported signing algorithms for keyring 149 SupportedAlgos SigningAlgoList 150 // supported signing algorithms for Ledger 151 SupportedAlgosLedger SigningAlgoList 152 // define Ledger Derivation function 153 LedgerDerivation func() (ledger.SECP256K1, error) 154 // define Ledger key generation function 155 LedgerCreateKey func([]byte) types.PubKey 156 // define Ledger app name 157 LedgerAppName string 158 // indicate whether Ledger should skip DER Conversion on signature, 159 // depending on which format (DER or BER) the Ledger app returns signatures 160 LedgerSigSkipDERConv bool 161 } 162 163 // NewInMemory creates a transient keyring useful for testing 164 // purposes and on-the-fly key generation. 165 // Keybase options can be applied when generating this new Keybase. 166 func NewInMemory(cdc codec.Codec, opts ...Option) Keyring { 167 return NewInMemoryWithKeyring(keyring.NewArrayKeyring(nil), cdc, opts...) 168 } 169 170 // NewInMemoryWithKeyring returns an in memory keyring using the specified keyring.Keyring 171 // as the backing keyring. 172 func NewInMemoryWithKeyring(kr keyring.Keyring, cdc codec.Codec, opts ...Option) Keyring { 173 return newKeystore(kr, cdc, BackendMemory, opts...) 174 } 175 176 // New creates a new instance of a keyring. 177 // Keyring options can be applied when generating the new instance. 178 // Available backends are "os", "file", "kwallet", "memory", "pass", "test". 179 func New( 180 appName, backend, rootDir string, userInput io.Reader, cdc codec.Codec, opts ...Option, 181 ) (Keyring, error) { 182 var ( 183 db keyring.Keyring 184 err error 185 ) 186 187 switch backend { 188 case BackendMemory: 189 return NewInMemory(cdc, opts...), err 190 case BackendTest: 191 db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir)) 192 case BackendFile: 193 db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput)) 194 case BackendOS: 195 db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput)) 196 case BackendKWallet: 197 db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput)) 198 case BackendPass: 199 db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput)) 200 default: 201 return nil, errorsmod.Wrap(ErrUnknownBacked, backend) 202 } 203 204 if err != nil { 205 return nil, err 206 } 207 208 return newKeystore(db, cdc, backend, opts...), nil 209 } 210 211 type keystore struct { 212 db keyring.Keyring 213 cdc codec.Codec 214 backend string 215 options Options 216 } 217 218 func newKeystore(kr keyring.Keyring, cdc codec.Codec, backend string, opts ...Option) keystore { 219 // Default options for keybase, these can be overwritten using the 220 // Option function 221 options := Options{ 222 SupportedAlgos: SigningAlgoList{hd.Secp256k1}, 223 SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1}, 224 } 225 226 for _, optionFn := range opts { 227 optionFn(&options) 228 } 229 230 if options.LedgerDerivation != nil { 231 ledger.SetDiscoverLedger(options.LedgerDerivation) 232 } 233 234 if options.LedgerCreateKey != nil { 235 ledger.SetCreatePubkey(options.LedgerCreateKey) 236 } 237 238 if options.LedgerAppName != "" { 239 ledger.SetAppName(options.LedgerAppName) 240 } 241 242 if options.LedgerSigSkipDERConv { 243 ledger.SetSkipDERConversion() 244 } 245 246 return keystore{ 247 db: kr, 248 cdc: cdc, 249 backend: backend, 250 options: options, 251 } 252 } 253 254 // Backend returns the keyring backend option used in the config 255 func (ks keystore) Backend() string { 256 return ks.backend 257 } 258 259 func (ks keystore) ExportPubKeyArmor(uid string) (string, error) { 260 k, err := ks.Key(uid) 261 if err != nil { 262 return "", err 263 } 264 265 key, err := k.GetPubKey() 266 if err != nil { 267 return "", err 268 } 269 270 bz, err := ks.cdc.MarshalInterface(key) 271 if err != nil { 272 return "", err 273 } 274 275 return crypto.ArmorPubKeyBytes(bz, key.Type()), nil 276 } 277 278 func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) { 279 k, err := ks.KeyByAddress(address) 280 if err != nil { 281 return "", err 282 } 283 284 return ks.ExportPubKeyArmor(k.Name) 285 } 286 287 // ExportPrivKeyArmor exports encrypted privKey 288 func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) { 289 priv, err := ks.ExportPrivateKeyObject(uid) 290 if err != nil { 291 return "", err 292 } 293 294 return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, priv.Type()), nil 295 } 296 297 // ExportPrivateKeyObject exports an armored private key object. 298 func (ks keystore) ExportPrivateKeyObject(uid string) (types.PrivKey, error) { 299 k, err := ks.Key(uid) 300 if err != nil { 301 return nil, err 302 } 303 304 priv, err := extractPrivKeyFromRecord(k) 305 if err != nil { 306 return nil, err 307 } 308 309 return priv, err 310 } 311 312 func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) { 313 k, err := ks.KeyByAddress(address) 314 if err != nil { 315 return "", err 316 } 317 318 return ks.ExportPrivKeyArmor(k.Name, encryptPassphrase) 319 } 320 321 func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { 322 if k, err := ks.Key(uid); err == nil { 323 if uid == k.Name { 324 return errorsmod.Wrap(ErrOverwriteKey, uid) 325 } 326 } 327 328 privKey, _, err := crypto.UnarmorDecryptPrivKey(armor, passphrase) 329 if err != nil { 330 return errorsmod.Wrap(err, "failed to decrypt private key") 331 } 332 333 _, err = ks.writeLocalKey(uid, privKey) 334 if err != nil { 335 return err 336 } 337 338 return nil 339 } 340 341 func (ks keystore) ImportPrivKeyHex(uid, privKey, algoStr string) error { 342 if _, err := ks.Key(uid); err == nil { 343 return errorsmod.Wrap(ErrOverwriteKey, uid) 344 } 345 if privKey[:2] == hexPrefix { 346 privKey = privKey[2:] 347 } 348 decodedPriv, err := hex.DecodeString(privKey) 349 if err != nil { 350 return err 351 } 352 algo, err := NewSigningAlgoFromString(algoStr, ks.options.SupportedAlgos) 353 if err != nil { 354 return err 355 } 356 priv := algo.Generate()(decodedPriv) 357 _, err = ks.writeLocalKey(uid, priv) 358 if err != nil { 359 return err 360 } 361 return nil 362 } 363 364 func (ks keystore) ImportPubKey(uid, armor string) error { 365 if _, err := ks.Key(uid); err == nil { 366 return errorsmod.Wrap(ErrOverwriteKey, uid) 367 } 368 369 pubBytes, _, err := crypto.UnarmorPubKeyBytes(armor) 370 if err != nil { 371 return err 372 } 373 374 var pubKey types.PubKey 375 if err := ks.cdc.UnmarshalInterface(pubBytes, &pubKey); err != nil { 376 return err 377 } 378 379 _, err = ks.writeOfflineKey(uid, pubKey) 380 if err != nil { 381 return err 382 } 383 384 return nil 385 } 386 387 func (ks keystore) Sign(uid string, msg []byte, signMode signing.SignMode) ([]byte, types.PubKey, error) { 388 k, err := ks.Key(uid) 389 if err != nil { 390 return nil, nil, err 391 } 392 393 switch { 394 case k.GetLocal() != nil: 395 priv, err := extractPrivKeyFromLocal(k.GetLocal()) 396 if err != nil { 397 return nil, nil, err 398 } 399 400 sig, err := priv.Sign(msg) 401 if err != nil { 402 return nil, nil, err 403 } 404 405 return sig, priv.PubKey(), nil 406 407 case k.GetLedger() != nil: 408 return SignWithLedger(k, msg, signMode) 409 410 // multi or offline record 411 default: 412 pub, err := k.GetPubKey() 413 if err != nil { 414 return nil, nil, err 415 } 416 return nil, pub, ErrOfflineSign 417 } 418 } 419 420 func (ks keystore) SignByAddress(address sdk.Address, msg []byte, signMode signing.SignMode) ([]byte, types.PubKey, error) { 421 k, err := ks.KeyByAddress(address) 422 if err != nil { 423 return nil, nil, err 424 } 425 426 return ks.Sign(k.Name, msg, signMode) 427 } 428 429 func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (*Record, error) { 430 if !ks.options.SupportedAlgosLedger.Contains(algo) { 431 return nil, errorsmod.Wrap(ErrUnsupportedSigningAlgo, fmt.Sprintf("signature algo %s is not defined in the keyring options", algo.Name())) 432 } 433 434 hdPath := hd.NewFundraiserParams(account, coinType, index) 435 436 priv, _, err := ledger.NewPrivKeySecp256k1(*hdPath, hrp) 437 if err != nil { 438 return nil, errors.CombineErrors(ErrLedgerGenerateKey, err) 439 } 440 441 return ks.writeLedgerKey(uid, priv.PubKey(), hdPath) 442 } 443 444 func (ks keystore) writeLedgerKey(name string, pk types.PubKey, path *hd.BIP44Params) (*Record, error) { 445 k, err := NewLedgerRecord(name, pk, path) 446 if err != nil { 447 return nil, err 448 } 449 450 return k, ks.writeRecord(k) 451 } 452 453 func (ks keystore) SaveMultisig(uid string, pubkey types.PubKey) (*Record, error) { 454 return ks.writeMultisigKey(uid, pubkey) 455 } 456 457 func (ks keystore) SaveOfflineKey(uid string, pubkey types.PubKey) (*Record, error) { 458 return ks.writeOfflineKey(uid, pubkey) 459 } 460 461 func (ks keystore) DeleteByAddress(address sdk.Address) error { 462 k, err := ks.KeyByAddress(address) 463 if err != nil { 464 return err 465 } 466 467 err = ks.Delete(k.Name) 468 if err != nil { 469 return err 470 } 471 472 return nil 473 } 474 475 func (ks keystore) Rename(oldName, newName string) error { 476 _, err := ks.Key(newName) 477 if err == nil { 478 return errorsmod.Wrap(ErrKeyAlreadyExists, fmt.Sprintf("rename failed, %s", newName)) 479 } 480 481 armor, err := ks.ExportPrivKeyArmor(oldName, passPhrase) 482 if err != nil { 483 return err 484 } 485 486 if err := ks.Delete(oldName); err != nil { 487 return err 488 } 489 490 if err := ks.ImportPrivKey(newName, armor, passPhrase); err != nil { 491 return err 492 } 493 494 return nil 495 } 496 497 // Delete deletes a key in the keyring. `uid` represents the key name, without 498 // the `.info` suffix. 499 func (ks keystore) Delete(uid string) error { 500 k, err := ks.Key(uid) 501 if err != nil { 502 return err 503 } 504 505 addr, err := k.GetAddress() 506 if err != nil { 507 return err 508 } 509 510 err = ks.db.Remove(addrHexKeyAsString(addr)) 511 if err != nil { 512 return err 513 } 514 515 err = ks.db.Remove(infoKey(uid)) 516 if err != nil { 517 return err 518 } 519 520 return nil 521 } 522 523 func (ks keystore) KeyByAddress(address sdk.Address) (*Record, error) { 524 ik, err := ks.db.Get(addrHexKeyAsString(address)) 525 if err != nil { 526 return nil, wrapKeyNotFound(err, fmt.Sprintf("key with address %s not found", address.String())) 527 } 528 529 if len(ik.Data) == 0 { 530 return nil, wrapKeyNotFound(err, fmt.Sprintf("key with address %s not found", address.String())) 531 } 532 533 return ks.Key(string(ik.Data)) 534 } 535 536 func wrapKeyNotFound(err error, msg string) error { 537 if err == keyring.ErrKeyNotFound { 538 return errorsmod.Wrap(sdkerrors.ErrKeyNotFound, msg) 539 } 540 return err 541 } 542 543 func (ks keystore) List() ([]*Record, error) { 544 return ks.MigrateAll() 545 } 546 547 func (ks keystore) NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (*Record, string, error) { 548 if language != English { 549 return nil, "", ErrUnsupportedLanguage 550 } 551 552 if !ks.isSupportedSigningAlgo(algo) { 553 return nil, "", ErrUnsupportedSigningAlgo 554 } 555 556 // Default number of words (24): This generates a mnemonic directly from the 557 // number of words by reading system entropy. 558 entropy, err := bip39.NewEntropy(defaultEntropySize) 559 if err != nil { 560 return nil, "", err 561 } 562 563 mnemonic, err := bip39.NewMnemonic(entropy) 564 if err != nil { 565 return nil, "", err 566 } 567 568 if bip39Passphrase == "" { 569 bip39Passphrase = DefaultBIP39Passphrase 570 } 571 572 k, err := ks.NewAccount(uid, mnemonic, bip39Passphrase, hdPath, algo) 573 if err != nil { 574 return nil, "", err 575 } 576 577 return k, mnemonic, nil 578 } 579 580 func (ks keystore) NewAccount(name, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (*Record, error) { 581 if !ks.isSupportedSigningAlgo(algo) { 582 return nil, ErrUnsupportedSigningAlgo 583 } 584 585 // create master key and derive first key for keyring 586 derivedPriv, err := algo.Derive()(mnemonic, bip39Passphrase, hdPath) 587 if err != nil { 588 return nil, err 589 } 590 591 privKey := algo.Generate()(derivedPriv) 592 593 // check if the key already exists with the same address and return an error 594 // if found 595 address := sdk.AccAddress(privKey.PubKey().Address()) 596 if _, err := ks.KeyByAddress(address); err == nil { 597 return nil, ErrDuplicatedAddress 598 } 599 600 return ks.writeLocalKey(name, privKey) 601 } 602 603 func (ks keystore) isSupportedSigningAlgo(algo SignatureAlgo) bool { 604 return ks.options.SupportedAlgos.Contains(algo) 605 } 606 607 func (ks keystore) Key(uid string) (*Record, error) { 608 k, err := ks.migrate(uid) 609 if err != nil { 610 return nil, err 611 } 612 613 return k, nil 614 } 615 616 // SupportedAlgorithms returns the keystore Options' supported signing algorithm. 617 // for the keyring and Ledger. 618 func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) { 619 return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger 620 } 621 622 // SignWithLedger signs a binary message with the ledger device referenced by an Info object 623 // and returns the signed bytes and the public key. It returns an error if the device could 624 // not be queried or it returned an error. 625 func SignWithLedger(k *Record, msg []byte, signMode signing.SignMode) (sig []byte, pub types.PubKey, err error) { 626 ledgerInfo := k.GetLedger() 627 if ledgerInfo == nil { 628 return nil, nil, ErrNotLedgerObj 629 } 630 631 path := ledgerInfo.GetPath() 632 633 priv, err := ledger.NewPrivKeySecp256k1Unsafe(*path) 634 if err != nil { 635 return 636 } 637 ledgerPubKey := priv.PubKey() 638 pubKey, err := k.GetPubKey() 639 if err != nil { 640 return nil, nil, err 641 } 642 if !pubKey.Equals(ledgerPubKey) { 643 return nil, nil, fmt.Errorf("the public key that the user attempted to sign with does not match the public key on the ledger device. %v does not match %v", pubKey.String(), ledgerPubKey.String()) 644 } 645 646 switch signMode { 647 case signing.SignMode_SIGN_MODE_TEXTUAL: 648 sig, err = priv.Sign(msg) 649 if err != nil { 650 return nil, nil, err 651 } 652 case signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: 653 sig, err = priv.SignLedgerAminoJSON(msg) 654 if err != nil { 655 return nil, nil, err 656 } 657 default: 658 return nil, nil, errorsmod.Wrap(ErrInvalidSignMode, fmt.Sprintf("%v", signMode)) 659 } 660 661 if !priv.PubKey().VerifySignature(msg, sig) { 662 return nil, nil, ErrLedgerInvalidSignature 663 } 664 665 return sig, priv.PubKey(), nil 666 } 667 668 func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config { 669 return keyring.Config{ 670 ServiceName: appName, 671 FileDir: dir, 672 KeychainTrustApplication: true, 673 FilePasswordFunc: newRealPrompt(dir, buf), 674 } 675 } 676 677 func newTestBackendKeyringConfig(appName, dir string) keyring.Config { 678 return keyring.Config{ 679 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 680 ServiceName: appName, 681 FileDir: filepath.Join(dir, keyringTestDirName), 682 FilePasswordFunc: func(_ string) (string, error) { 683 return "test", nil 684 }, 685 } 686 } 687 688 func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 689 return keyring.Config{ 690 AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, 691 ServiceName: "kdewallet", 692 KWalletAppID: appName, 693 KWalletFolder: "", 694 } 695 } 696 697 func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 698 prefix := fmt.Sprintf(passKeyringPrefix, appName) 699 700 return keyring.Config{ 701 AllowedBackends: []keyring.BackendType{keyring.PassBackend}, 702 ServiceName: appName, 703 PassPrefix: prefix, 704 } 705 } 706 707 func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config { 708 fileDir := filepath.Join(dir, keyringFileDirName) 709 710 return keyring.Config{ 711 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 712 ServiceName: name, 713 FileDir: fileDir, 714 FilePasswordFunc: newRealPrompt(fileDir, buf), 715 } 716 } 717 718 func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { 719 return func(prompt string) (string, error) { 720 keyhashStored := false 721 keyhashFilePath := filepath.Join(dir, "keyhash") 722 723 var keyhash []byte 724 725 _, err := os.Stat(keyhashFilePath) 726 727 switch { 728 case err == nil: 729 keyhash, err = os.ReadFile(keyhashFilePath) 730 if err != nil { 731 return "", errorsmod.Wrap(err, fmt.Sprintf("failed to read %s", keyhashFilePath)) 732 } 733 734 keyhashStored = true 735 736 case os.IsNotExist(err): 737 keyhashStored = false 738 739 default: 740 return "", errorsmod.Wrap(err, fmt.Sprintf("failed to open %s", keyhashFilePath)) 741 } 742 743 failureCounter := 0 744 745 for { 746 failureCounter++ 747 if failureCounter > maxPassphraseEntryAttempts { 748 return "", ErrMaxPassPhraseAttempts 749 } 750 751 buf := bufio.NewReader(buf) 752 pass, err := input.GetPassword(fmt.Sprintf("Enter keyring passphrase (attempt %d/%d):", failureCounter, maxPassphraseEntryAttempts), buf) 753 if err != nil { 754 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 755 // but we only log the error. 756 // 757 // lgtm [go/clear-text-logging] 758 fmt.Fprintln(os.Stderr, err) 759 continue 760 } 761 762 if keyhashStored { 763 if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { 764 fmt.Fprintln(os.Stderr, "incorrect passphrase") 765 continue 766 } 767 768 return pass, nil 769 } 770 771 reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) 772 if err != nil { 773 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 774 // but we only log the error. 775 // 776 // lgtm [go/clear-text-logging] 777 fmt.Fprintln(os.Stderr, err) 778 continue 779 } 780 781 if pass != reEnteredPass { 782 fmt.Fprintln(os.Stderr, "passphrase do not match") 783 continue 784 } 785 786 passwordHash, err := bcrypt.GenerateFromPassword([]byte(pass), 2) 787 if err != nil { 788 fmt.Fprintln(os.Stderr, err) 789 continue 790 } 791 792 if err := os.WriteFile(keyhashFilePath, passwordHash, 0o600); err != nil { 793 return "", err 794 } 795 796 return pass, nil 797 } 798 } 799 } 800 801 func (ks keystore) writeLocalKey(name string, privKey types.PrivKey) (*Record, error) { 802 k, err := NewLocalRecord(name, privKey, privKey.PubKey()) 803 if err != nil { 804 return nil, err 805 } 806 807 return k, ks.writeRecord(k) 808 } 809 810 // writeRecord persists a keyring item in keystore if it does not exist there. 811 // For each key record, we actually write 2 items: 812 // - one with key `<uid>.info`, with Data = the serialized protobuf key 813 // - another with key `<addr_as_hex>.address`, with Data = the uid (i.e. the key name) 814 // This is to be able to query keys both by name and by address. 815 func (ks keystore) writeRecord(k *Record) error { 816 addr, err := k.GetAddress() 817 if err != nil { 818 return err 819 } 820 821 key := infoKey(k.Name) 822 823 exists, err := ks.existsInDb(addr, key) 824 if err != nil { 825 return err 826 } 827 if exists { 828 return errorsmod.Wrap(ErrKeyAlreadyExists, key) 829 } 830 831 serializedRecord, err := ks.cdc.Marshal(k) 832 if err != nil { 833 return errors.CombineErrors(ErrUnableToSerialize, err) 834 } 835 836 item := keyring.Item{ 837 Key: key, 838 Data: serializedRecord, 839 } 840 841 if err := ks.SetItem(item); err != nil { 842 return err 843 } 844 845 item = keyring.Item{ 846 Key: addrHexKeyAsString(addr), 847 Data: []byte(key), 848 } 849 850 if err := ks.SetItem(item); err != nil { 851 return err 852 } 853 854 return nil 855 } 856 857 // existsInDb returns (true, nil) if either addr or name exist is in keystore DB. 858 // On the other hand, it returns (false, error) if Get method returns error different from keyring.ErrKeyNotFound 859 // In case of inconsistent keyring, it recovers it automatically. 860 func (ks keystore) existsInDb(addr sdk.Address, name string) (bool, error) { 861 _, errAddr := ks.db.Get(addrHexKeyAsString(addr)) 862 if errAddr != nil && !errors.Is(errAddr, keyring.ErrKeyNotFound) { 863 return false, errAddr 864 } 865 866 _, errInfo := ks.db.Get(infoKey(name)) 867 if errInfo == nil { 868 return true, nil // uid lookup succeeds - info exists 869 } else if !errors.Is(errInfo, keyring.ErrKeyNotFound) { 870 return false, errInfo // received unexpected error - returns 871 } 872 873 // looking for an issue, record with meta (getByAddress) exists, but record with public key itself does not 874 if errAddr == nil && errors.Is(errInfo, keyring.ErrKeyNotFound) { 875 fmt.Fprintf(os.Stderr, "address \"%s\" exists but pubkey itself does not\n", hex.EncodeToString(addr.Bytes())) 876 fmt.Fprintln(os.Stderr, "recreating pubkey record") 877 err := ks.db.Remove(addrHexKeyAsString(addr)) 878 if err != nil { 879 return true, err 880 } 881 return false, nil 882 } 883 884 // both lookups failed, info does not exist 885 return false, nil 886 } 887 888 func (ks keystore) writeOfflineKey(name string, pk types.PubKey) (*Record, error) { 889 k, err := NewOfflineRecord(name, pk) 890 if err != nil { 891 return nil, err 892 } 893 894 return k, ks.writeRecord(k) 895 } 896 897 // writeMultisigKey investigate where thisf function is called maybe remove it 898 func (ks keystore) writeMultisigKey(name string, pk types.PubKey) (*Record, error) { 899 k, err := NewMultiRecord(name, pk) 900 if err != nil { 901 return nil, err 902 } 903 904 return k, ks.writeRecord(k) 905 } 906 907 func (ks keystore) MigrateAll() ([]*Record, error) { 908 keys, err := ks.db.Keys() 909 if err != nil { 910 return nil, err 911 } 912 913 if len(keys) == 0 { 914 return nil, nil 915 } 916 917 sort.Strings(keys) 918 var recs []*Record 919 for _, key := range keys { 920 // The keyring items only with `.info` consists the key info. 921 if !strings.HasSuffix(key, infoSuffix) { 922 continue 923 } 924 925 rec, err := ks.migrate(key) 926 if err != nil { 927 fmt.Fprintf(os.Stderr, "migrate err for key %s: %q\n", key, err) 928 continue 929 } 930 931 recs = append(recs, rec) 932 } 933 934 return recs, nil 935 } 936 937 // migrate converts keyring.Item from amino to proto serialization format. 938 // the `key` argument can be a key uid (e.g. "alice") or with the '.info' 939 // suffix (e.g. "alice.info"). 940 // 941 // It operates as follows: 942 // 1. retrieve any key 943 // 2. try to decode it using protobuf 944 // 3. if ok, then return the key, do nothing else 945 // 4. if it fails, then try to decode it using amino 946 // 5. convert from the amino struct to the protobuf struct 947 // 6. write the proto-encoded key back to the keyring 948 func (ks keystore) migrate(key string) (*Record, error) { 949 if !strings.HasSuffix(key, infoSuffix) { 950 key = infoKey(key) 951 } 952 953 // 1. get the key. 954 item, err := ks.db.Get(key) 955 if err != nil { 956 return nil, wrapKeyNotFound(err, key) 957 } 958 959 if len(item.Data) == 0 { 960 return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, key) 961 } 962 963 // 2. Try to deserialize using proto 964 k, err := ks.protoUnmarshalRecord(item.Data) 965 // 3. If ok then return the key 966 if err == nil { 967 return k, nil 968 } 969 970 // 4. Try to decode with amino 971 legacyInfo, err := unMarshalLegacyInfo(item.Data) 972 if err != nil { 973 return nil, errorsmod.Wrap(err, "unable to unmarshal item.Data") 974 } 975 976 // 5. Convert and serialize info using proto 977 k, err = ks.convertFromLegacyInfo(legacyInfo) 978 if err != nil { 979 return nil, errorsmod.Wrap(err, "convertFromLegacyInfo") 980 } 981 982 serializedRecord, err := ks.cdc.Marshal(k) 983 if err != nil { 984 return nil, errors.CombineErrors(ErrUnableToSerialize, err) 985 } 986 987 item = keyring.Item{ 988 Key: key, 989 Data: serializedRecord, 990 } 991 992 // 6. Overwrite the keyring entry with the new proto-encoded key. 993 if err := ks.SetItem(item); err != nil { 994 return nil, errorsmod.Wrap(err, "unable to set keyring.Item") 995 } 996 997 fmt.Fprintf(os.Stderr, "Successfully migrated key %s.\n", key) 998 999 return k, nil 1000 } 1001 1002 func (ks keystore) protoUnmarshalRecord(bz []byte) (*Record, error) { 1003 k := new(Record) 1004 if err := ks.cdc.Unmarshal(bz, k); err != nil { 1005 return nil, err 1006 } 1007 1008 return k, nil 1009 } 1010 1011 func (ks keystore) SetItem(item keyring.Item) error { 1012 return ks.db.Set(item) 1013 } 1014 1015 func (ks keystore) convertFromLegacyInfo(info LegacyInfo) (*Record, error) { 1016 if info == nil { 1017 return nil, errorsmod.Wrap(ErrLegacyToRecord, "info is nil") 1018 } 1019 1020 name := info.GetName() 1021 pk := info.GetPubKey() 1022 1023 switch info.GetType() { 1024 case TypeLocal: 1025 priv, err := privKeyFromLegacyInfo(info) 1026 if err != nil { 1027 return nil, err 1028 } 1029 1030 return NewLocalRecord(name, priv, pk) 1031 case TypeOffline: 1032 return NewOfflineRecord(name, pk) 1033 case TypeMulti: 1034 return NewMultiRecord(name, pk) 1035 case TypeLedger: 1036 path, err := info.GetPath() 1037 if err != nil { 1038 return nil, err 1039 } 1040 1041 return NewLedgerRecord(name, pk, path) 1042 default: 1043 return nil, ErrUnknownLegacyType 1044 1045 } 1046 } 1047 1048 func addrHexKeyAsString(address sdk.Address) string { 1049 return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix) 1050 }