github.com/cosmos/cosmos-sdk@v0.50.1/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 638 switch signMode { 639 case signing.SignMode_SIGN_MODE_TEXTUAL: 640 sig, err = priv.Sign(msg) 641 if err != nil { 642 return nil, nil, err 643 } 644 case signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: 645 sig, err = priv.SignLedgerAminoJSON(msg) 646 if err != nil { 647 return nil, nil, err 648 } 649 default: 650 return nil, nil, errorsmod.Wrap(ErrInvalidSignMode, fmt.Sprintf("%v", signMode)) 651 } 652 653 if !priv.PubKey().VerifySignature(msg, sig) { 654 return nil, nil, ErrLedgerInvalidSignature 655 } 656 657 return sig, priv.PubKey(), nil 658 } 659 660 func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config { 661 return keyring.Config{ 662 ServiceName: appName, 663 FileDir: dir, 664 KeychainTrustApplication: true, 665 FilePasswordFunc: newRealPrompt(dir, buf), 666 } 667 } 668 669 func newTestBackendKeyringConfig(appName, dir string) keyring.Config { 670 return keyring.Config{ 671 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 672 ServiceName: appName, 673 FileDir: filepath.Join(dir, keyringTestDirName), 674 FilePasswordFunc: func(_ string) (string, error) { 675 return "test", nil 676 }, 677 } 678 } 679 680 func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 681 return keyring.Config{ 682 AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, 683 ServiceName: "kdewallet", 684 KWalletAppID: appName, 685 KWalletFolder: "", 686 } 687 } 688 689 func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 690 prefix := fmt.Sprintf(passKeyringPrefix, appName) 691 692 return keyring.Config{ 693 AllowedBackends: []keyring.BackendType{keyring.PassBackend}, 694 ServiceName: appName, 695 PassPrefix: prefix, 696 } 697 } 698 699 func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config { 700 fileDir := filepath.Join(dir, keyringFileDirName) 701 702 return keyring.Config{ 703 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 704 ServiceName: name, 705 FileDir: fileDir, 706 FilePasswordFunc: newRealPrompt(fileDir, buf), 707 } 708 } 709 710 func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { 711 return func(prompt string) (string, error) { 712 keyhashStored := false 713 keyhashFilePath := filepath.Join(dir, "keyhash") 714 715 var keyhash []byte 716 717 _, err := os.Stat(keyhashFilePath) 718 719 switch { 720 case err == nil: 721 keyhash, err = os.ReadFile(keyhashFilePath) 722 if err != nil { 723 return "", errorsmod.Wrap(err, fmt.Sprintf("failed to read %s", keyhashFilePath)) 724 } 725 726 keyhashStored = true 727 728 case os.IsNotExist(err): 729 keyhashStored = false 730 731 default: 732 return "", errorsmod.Wrap(err, fmt.Sprintf("failed to open %s", keyhashFilePath)) 733 } 734 735 failureCounter := 0 736 737 for { 738 failureCounter++ 739 if failureCounter > maxPassphraseEntryAttempts { 740 return "", ErrMaxPassPhraseAttempts 741 } 742 743 buf := bufio.NewReader(buf) 744 pass, err := input.GetPassword(fmt.Sprintf("Enter keyring passphrase (attempt %d/%d):", failureCounter, maxPassphraseEntryAttempts), buf) 745 if err != nil { 746 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 747 // but we only log the error. 748 // 749 // lgtm [go/clear-text-logging] 750 fmt.Fprintln(os.Stderr, err) 751 continue 752 } 753 754 if keyhashStored { 755 if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { 756 fmt.Fprintln(os.Stderr, "incorrect passphrase") 757 continue 758 } 759 760 return pass, nil 761 } 762 763 reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) 764 if err != nil { 765 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 766 // but we only log the error. 767 // 768 // lgtm [go/clear-text-logging] 769 fmt.Fprintln(os.Stderr, err) 770 continue 771 } 772 773 if pass != reEnteredPass { 774 fmt.Fprintln(os.Stderr, "passphrase do not match") 775 continue 776 } 777 778 passwordHash, err := bcrypt.GenerateFromPassword([]byte(pass), 2) 779 if err != nil { 780 fmt.Fprintln(os.Stderr, err) 781 continue 782 } 783 784 if err := os.WriteFile(keyhashFilePath, passwordHash, 0o600); err != nil { 785 return "", err 786 } 787 788 return pass, nil 789 } 790 } 791 } 792 793 func (ks keystore) writeLocalKey(name string, privKey types.PrivKey) (*Record, error) { 794 k, err := NewLocalRecord(name, privKey, privKey.PubKey()) 795 if err != nil { 796 return nil, err 797 } 798 799 return k, ks.writeRecord(k) 800 } 801 802 // writeRecord persists a keyring item in keystore if it does not exist there. 803 // For each key record, we actually write 2 items: 804 // - one with key `<uid>.info`, with Data = the serialized protobuf key 805 // - another with key `<addr_as_hex>.address`, with Data = the uid (i.e. the key name) 806 // This is to be able to query keys both by name and by address. 807 func (ks keystore) writeRecord(k *Record) error { 808 addr, err := k.GetAddress() 809 if err != nil { 810 return err 811 } 812 813 key := infoKey(k.Name) 814 815 exists, err := ks.existsInDb(addr, key) 816 if err != nil { 817 return err 818 } 819 if exists { 820 return errorsmod.Wrap(ErrKeyAlreadyExists, key) 821 } 822 823 serializedRecord, err := ks.cdc.Marshal(k) 824 if err != nil { 825 return errors.CombineErrors(ErrUnableToSerialize, err) 826 } 827 828 item := keyring.Item{ 829 Key: key, 830 Data: serializedRecord, 831 } 832 833 if err := ks.SetItem(item); err != nil { 834 return err 835 } 836 837 item = keyring.Item{ 838 Key: addrHexKeyAsString(addr), 839 Data: []byte(key), 840 } 841 842 if err := ks.SetItem(item); err != nil { 843 return err 844 } 845 846 return nil 847 } 848 849 // existsInDb returns (true, nil) if either addr or name exist is in keystore DB. 850 // On the other hand, it returns (false, error) if Get method returns error different from keyring.ErrKeyNotFound 851 // In case of inconsistent keyring, it recovers it automatically. 852 func (ks keystore) existsInDb(addr sdk.Address, name string) (bool, error) { 853 _, errAddr := ks.db.Get(addrHexKeyAsString(addr)) 854 if errAddr != nil && !errors.Is(errAddr, keyring.ErrKeyNotFound) { 855 return false, errAddr 856 } 857 858 _, errInfo := ks.db.Get(infoKey(name)) 859 if errInfo == nil { 860 return true, nil // uid lookup succeeds - info exists 861 } else if !errors.Is(errInfo, keyring.ErrKeyNotFound) { 862 return false, errInfo // received unexpected error - returns 863 } 864 865 // looking for an issue, record with meta (getByAddress) exists, but record with public key itself does not 866 if errAddr == nil && errors.Is(errInfo, keyring.ErrKeyNotFound) { 867 fmt.Fprintf(os.Stderr, "address \"%s\" exists but pubkey itself does not\n", hex.EncodeToString(addr.Bytes())) 868 fmt.Fprintln(os.Stderr, "recreating pubkey record") 869 err := ks.db.Remove(addrHexKeyAsString(addr)) 870 if err != nil { 871 return true, err 872 } 873 return false, nil 874 } 875 876 // both lookups failed, info does not exist 877 return false, nil 878 } 879 880 func (ks keystore) writeOfflineKey(name string, pk types.PubKey) (*Record, error) { 881 k, err := NewOfflineRecord(name, pk) 882 if err != nil { 883 return nil, err 884 } 885 886 return k, ks.writeRecord(k) 887 } 888 889 // writeMultisigKey investigate where thisf function is called maybe remove it 890 func (ks keystore) writeMultisigKey(name string, pk types.PubKey) (*Record, error) { 891 k, err := NewMultiRecord(name, pk) 892 if err != nil { 893 return nil, err 894 } 895 896 return k, ks.writeRecord(k) 897 } 898 899 func (ks keystore) MigrateAll() ([]*Record, error) { 900 keys, err := ks.db.Keys() 901 if err != nil { 902 return nil, err 903 } 904 905 if len(keys) == 0 { 906 return nil, nil 907 } 908 909 sort.Strings(keys) 910 var recs []*Record 911 for _, key := range keys { 912 // The keyring items only with `.info` consists the key info. 913 if !strings.HasSuffix(key, infoSuffix) { 914 continue 915 } 916 917 rec, err := ks.migrate(key) 918 if err != nil { 919 fmt.Printf("migrate err for key %s: %q\n", key, err) 920 continue 921 } 922 923 recs = append(recs, rec) 924 } 925 926 return recs, nil 927 } 928 929 // migrate converts keyring.Item from amino to proto serialization format. 930 // the `key` argument can be a key uid (e.g. "alice") or with the '.info' 931 // suffix (e.g. "alice.info"). 932 // 933 // It operates as follows: 934 // 1. retrieve any key 935 // 2. try to decode it using protobuf 936 // 3. if ok, then return the key, do nothing else 937 // 4. if it fails, then try to decode it using amino 938 // 5. convert from the amino struct to the protobuf struct 939 // 6. write the proto-encoded key back to the keyring 940 func (ks keystore) migrate(key string) (*Record, error) { 941 if !strings.HasSuffix(key, infoSuffix) { 942 key = infoKey(key) 943 } 944 945 // 1. get the key. 946 item, err := ks.db.Get(key) 947 if err != nil { 948 return nil, wrapKeyNotFound(err, key) 949 } 950 951 if len(item.Data) == 0 { 952 return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, key) 953 } 954 955 // 2. Try to deserialize using proto 956 k, err := ks.protoUnmarshalRecord(item.Data) 957 // 3. If ok then return the key 958 if err == nil { 959 return k, nil 960 } 961 962 // 4. Try to decode with amino 963 legacyInfo, err := unMarshalLegacyInfo(item.Data) 964 if err != nil { 965 return nil, errorsmod.Wrap(err, "unable to unmarshal item.Data") 966 } 967 968 // 5. Convert and serialize info using proto 969 k, err = ks.convertFromLegacyInfo(legacyInfo) 970 if err != nil { 971 return nil, errorsmod.Wrap(err, "convertFromLegacyInfo") 972 } 973 974 serializedRecord, err := ks.cdc.Marshal(k) 975 if err != nil { 976 return nil, errors.CombineErrors(ErrUnableToSerialize, err) 977 } 978 979 item = keyring.Item{ 980 Key: key, 981 Data: serializedRecord, 982 } 983 984 // 6. Overwrite the keyring entry with the new proto-encoded key. 985 if err := ks.SetItem(item); err != nil { 986 return nil, errorsmod.Wrap(err, "unable to set keyring.Item") 987 } 988 989 fmt.Printf("Successfully migrated key %s.\n", key) 990 991 return k, nil 992 } 993 994 func (ks keystore) protoUnmarshalRecord(bz []byte) (*Record, error) { 995 k := new(Record) 996 if err := ks.cdc.Unmarshal(bz, k); err != nil { 997 return nil, err 998 } 999 1000 return k, nil 1001 } 1002 1003 func (ks keystore) SetItem(item keyring.Item) error { 1004 return ks.db.Set(item) 1005 } 1006 1007 func (ks keystore) convertFromLegacyInfo(info LegacyInfo) (*Record, error) { 1008 if info == nil { 1009 return nil, errorsmod.Wrap(ErrLegacyToRecord, "info is nil") 1010 } 1011 1012 name := info.GetName() 1013 pk := info.GetPubKey() 1014 1015 switch info.GetType() { 1016 case TypeLocal: 1017 priv, err := privKeyFromLegacyInfo(info) 1018 if err != nil { 1019 return nil, err 1020 } 1021 1022 return NewLocalRecord(name, priv, pk) 1023 case TypeOffline: 1024 return NewOfflineRecord(name, pk) 1025 case TypeMulti: 1026 return NewMultiRecord(name, pk) 1027 case TypeLedger: 1028 path, err := info.GetPath() 1029 if err != nil { 1030 return nil, err 1031 } 1032 1033 return NewLedgerRecord(name, pk, path) 1034 default: 1035 return nil, ErrUnknownLegacyType 1036 1037 } 1038 } 1039 1040 func addrHexKeyAsString(address sdk.Address) string { 1041 return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix) 1042 }