github.com/Finschia/finschia-sdk@v0.48.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 occrypto "github.com/Finschia/ostracon/crypto" 15 bip39 "github.com/cosmos/go-bip39" 16 "github.com/pkg/errors" 17 "github.com/tendermint/crypto/bcrypt" 18 19 "github.com/Finschia/finschia-sdk/client/input" 20 "github.com/Finschia/finschia-sdk/codec/legacy" 21 "github.com/Finschia/finschia-sdk/crypto" 22 "github.com/Finschia/finschia-sdk/crypto/hd" 23 "github.com/Finschia/finschia-sdk/crypto/ledger" 24 "github.com/Finschia/finschia-sdk/crypto/types" 25 sdk "github.com/Finschia/finschia-sdk/types" 26 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 27 ) 28 29 // Backend options for Keyring 30 const ( 31 BackendFile = "file" 32 BackendOS = "os" 33 BackendKWallet = "kwallet" 34 BackendPass = "pass" 35 BackendTest = "test" 36 BackendMemory = "memory" 37 ) 38 39 const ( 40 keyringFileDirName = "keyring-file" 41 keyringTestDirName = "keyring-test" 42 passKeyringPrefix = "keyring-%s" 43 ) 44 45 var ( 46 _ Keyring = &keystore{} 47 maxPassphraseEntryAttempts = 3 48 ) 49 50 // Keyring exposes operations over a backend supported by github.com/99designs/keyring. 51 type Keyring interface { 52 // List all keys. 53 List() ([]Info, error) 54 55 // Supported signing algorithms for Keyring and Ledger respectively. 56 SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) 57 58 // Key and KeyByAddress return keys by uid and address respectively. 59 Key(uid string) (Info, error) 60 KeyByAddress(address sdk.Address) (Info, error) 61 62 // Delete and DeleteByAddress remove keys from the keyring. 63 Delete(uid string) error 64 DeleteByAddress(address sdk.Address) error 65 66 // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and 67 // persists the key to storage. Returns the generated mnemonic and the key Info. 68 // It returns an error if it fails to generate a key for the given algo type, or if 69 // another key is already stored under the same name or address. 70 // 71 // A passphrase set to the empty string will set the passphrase to the DefaultBIP39Passphrase value. 72 NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error) 73 74 // NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it. 75 // It fails if there is an existing key Info with the same address. 76 NewAccount(uid, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (Info, error) 77 78 // SaveLedgerKey retrieves a public key reference from a Ledger device and persists it. 79 SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) 80 81 // SavePubKey stores a public key and returns the persisted Info structure. 82 SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error) 83 84 // SaveMultisig stores and returns a new multsig (offline) key reference. 85 SaveMultisig(uid string, pubkey types.PubKey) (Info, error) 86 87 Signer 88 89 Importer 90 Exporter 91 } 92 93 // UnsafeKeyring exposes unsafe operations such as unsafe unarmored export in 94 // addition to those that are made available by the Keyring interface. 95 type UnsafeKeyring interface { 96 Keyring 97 UnsafeExporter 98 } 99 100 // Signer is implemented by key stores that want to provide signing capabilities. 101 type Signer interface { 102 // Sign sign byte messages with a user key. 103 Sign(uid string, msg []byte) ([]byte, types.PubKey, error) 104 105 // SignByAddress sign byte messages with a user key providing the address. 106 SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error) 107 } 108 109 // Importer is implemented by key stores that support import of public and private keys. 110 type Importer interface { 111 // ImportPrivKey imports ASCII armored passphrase-encrypted private keys. 112 ImportPrivKey(uid, armor, passphrase string) error 113 114 // ImportPubKey imports ASCII armored public keys. 115 ImportPubKey(uid string, armor string) error 116 } 117 118 // LegacyInfoImporter is implemented by key stores that support import of Info types. 119 type LegacyInfoImporter interface { 120 // ImportInfo import a keyring.Info into the current keyring. 121 // It is used to migrate multisig, ledger, and public key Info structure. 122 ImportInfo(oldInfo Info) error 123 } 124 125 // Exporter is implemented by key stores that support export of public and private keys. 126 type Exporter interface { 127 // Export public key 128 ExportPubKeyArmor(uid string) (string, error) 129 ExportPubKeyArmorByAddress(address sdk.Address) (string, error) 130 131 // ExportPrivKeyArmor returns a private key in ASCII armored format. 132 // It returns an error if the key does not exist or a wrong encryption passphrase is supplied. 133 ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) 134 ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) 135 } 136 137 // UnsafeExporter is implemented by key stores that support unsafe export 138 // of private keys' material. 139 type UnsafeExporter interface { 140 // UnsafeExportPrivKeyHex returns a private key in unarmored hex format 141 UnsafeExportPrivKeyHex(uid string) (string, error) 142 } 143 144 // Option overrides keyring configuration options. 145 type Option func(options *Options) 146 147 // Options define the options of the Keyring. 148 type Options struct { 149 // supported signing algorithms for keyring 150 SupportedAlgos SigningAlgoList 151 // supported signing algorithms for Ledger 152 SupportedAlgosLedger SigningAlgoList 153 } 154 155 // NewInMemory creates a transient keyring useful for testing 156 // purposes and on-the-fly key generation. 157 // Keybase options can be applied when generating this new Keybase. 158 func NewInMemory(opts ...Option) Keyring { 159 return newKeystore(keyring.NewArrayKeyring(nil), opts...) 160 } 161 162 // New creates a new instance of a keyring. 163 // Keyring ptions can be applied when generating the new instance. 164 // Available backends are "os", "file", "kwallet", "memory", "pass", "test". 165 func New( 166 appName, backend, rootDir string, userInput io.Reader, opts ...Option, 167 ) (Keyring, error) { 168 var ( 169 db keyring.Keyring 170 err error 171 ) 172 173 switch backend { 174 case BackendMemory: 175 return NewInMemory(opts...), err 176 case BackendTest: 177 db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir)) 178 case BackendFile: 179 db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput)) 180 case BackendOS: 181 db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput)) 182 case BackendKWallet: 183 db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput)) 184 case BackendPass: 185 db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput)) 186 default: 187 return nil, fmt.Errorf("unknown keyring backend %v", backend) 188 } 189 190 if err != nil { 191 return nil, err 192 } 193 194 return newKeystore(db, opts...), nil 195 } 196 197 type keystore struct { 198 db keyring.Keyring 199 options Options 200 } 201 202 func infoKey(name string) string { return fmt.Sprintf("%s.%s", name, infoSuffix) } 203 func infoKeyBz(name string) []byte { return []byte(infoKey(name)) } 204 205 func newKeystore(kr keyring.Keyring, opts ...Option) keystore { 206 // Default options for keybase 207 options := Options{ 208 SupportedAlgos: SigningAlgoList{hd.Secp256k1}, 209 SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1}, 210 } 211 212 for _, optionFn := range opts { 213 optionFn(&options) 214 } 215 216 return keystore{kr, options} 217 } 218 219 func (ks keystore) ExportPubKeyArmor(uid string) (string, error) { 220 bz, err := ks.Key(uid) 221 if err != nil { 222 return "", err 223 } 224 225 if bz == nil { 226 return "", fmt.Errorf("no key to export with name: %s", uid) 227 } 228 229 return crypto.ArmorPubKeyBytes(legacy.Cdc.MustMarshal(bz.GetPubKey()), string(bz.GetAlgo())), nil 230 } 231 232 func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) { 233 info, err := ks.KeyByAddress(address) 234 if err != nil { 235 return "", err 236 } 237 238 return ks.ExportPubKeyArmor(info.GetName()) 239 } 240 241 func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) { 242 priv, err := ks.ExportPrivateKeyObject(uid) 243 if err != nil { 244 return "", err 245 } 246 247 info, err := ks.Key(uid) 248 if err != nil { 249 return "", err 250 } 251 252 return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil 253 } 254 255 // ExportPrivateKeyObject exports an armored private key object. 256 func (ks keystore) ExportPrivateKeyObject(uid string) (types.PrivKey, error) { 257 info, err := ks.Key(uid) 258 if err != nil { 259 return nil, err 260 } 261 262 var priv types.PrivKey 263 264 switch linfo := info.(type) { 265 case localInfo: 266 if linfo.PrivKeyArmor == "" { 267 err = fmt.Errorf("private key not available") 268 return nil, err 269 } 270 271 priv, err = legacy.PrivKeyFromBytes([]byte(linfo.PrivKeyArmor)) 272 if err != nil { 273 return nil, err 274 } 275 276 case ledgerInfo, offlineInfo, multiInfo: 277 return nil, errors.New("only works on local private keys") 278 } 279 280 return priv, nil 281 } 282 283 func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) { 284 byAddress, err := ks.KeyByAddress(address) 285 if err != nil { 286 return "", err 287 } 288 289 return ks.ExportPrivKeyArmor(byAddress.GetName(), encryptPassphrase) 290 } 291 292 func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { 293 if _, err := ks.Key(uid); err == nil { 294 return fmt.Errorf("cannot overwrite key: %s", uid) 295 } 296 297 privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase) 298 if err != nil { 299 return errors.Wrap(err, "failed to decrypt private key") 300 } 301 302 _, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo)) 303 if err != nil { 304 return err 305 } 306 307 return nil 308 } 309 310 func (ks keystore) ImportPubKey(uid string, armor string) error { 311 if _, err := ks.Key(uid); err == nil { 312 return fmt.Errorf("cannot overwrite key: %s", uid) 313 } 314 315 pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor) 316 if err != nil { 317 return err 318 } 319 320 pubKey, err := legacy.PubKeyFromBytes(pubBytes) 321 if err != nil { 322 return err 323 } 324 325 _, err = ks.writeOfflineKey(uid, pubKey, hd.PubKeyType(algo)) 326 if err != nil { 327 return err 328 } 329 330 return nil 331 } 332 333 // ImportInfo implements Importer.MigrateInfo. 334 func (ks keystore) ImportInfo(oldInfo Info) error { 335 if _, err := ks.Key(oldInfo.GetName()); err == nil { 336 return fmt.Errorf("cannot overwrite key: %s", oldInfo.GetName()) 337 } 338 339 return ks.writeInfo(oldInfo) 340 } 341 342 func (ks keystore) Sign(uid string, msg []byte) ([]byte, types.PubKey, error) { 343 info, err := ks.Key(uid) 344 if err != nil { 345 return nil, nil, err 346 } 347 348 var priv types.PrivKey 349 350 switch i := info.(type) { 351 case localInfo: 352 if i.PrivKeyArmor == "" { 353 return nil, nil, fmt.Errorf("private key not available") 354 } 355 356 priv, err = legacy.PrivKeyFromBytes([]byte(i.PrivKeyArmor)) 357 if err != nil { 358 return nil, nil, err 359 } 360 361 case ledgerInfo: 362 return SignWithLedger(info, msg) 363 364 case offlineInfo, multiInfo: 365 return nil, info.GetPubKey(), errors.New("cannot sign with offline keys") 366 } 367 368 sig, err := priv.Sign(msg) 369 if err != nil { 370 return nil, nil, err 371 } 372 373 return sig, priv.PubKey(), nil 374 } 375 376 func (ks keystore) SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error) { 377 key, err := ks.KeyByAddress(address) 378 if err != nil { 379 return nil, nil, err 380 } 381 382 return ks.Sign(key.GetName(), msg) 383 } 384 385 func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) { 386 if !ks.options.SupportedAlgosLedger.Contains(algo) { 387 return nil, fmt.Errorf( 388 "%w: signature algo %s is not defined in the keyring options", 389 ErrUnsupportedSigningAlgo, algo.Name(), 390 ) 391 } 392 393 hdPath := hd.NewFundraiserParams(account, coinType, index) 394 395 priv, _, err := ledger.NewPrivKeySecp256k1(*hdPath, hrp) 396 if err != nil { 397 return nil, fmt.Errorf("failed to generate ledger key: %w", err) 398 } 399 400 return ks.writeLedgerKey(uid, priv.PubKey(), *hdPath, algo.Name()) 401 } 402 403 func (ks keystore) writeLedgerKey(name string, pub types.PubKey, path hd.BIP44Params, algo hd.PubKeyType) (Info, error) { 404 info := newLedgerInfo(name, pub, path, algo) 405 if err := ks.writeInfo(info); err != nil { 406 return nil, err 407 } 408 409 return info, nil 410 } 411 412 func (ks keystore) SaveMultisig(uid string, pubkey types.PubKey) (Info, error) { 413 return ks.writeMultisigKey(uid, pubkey) 414 } 415 416 func (ks keystore) SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error) { 417 return ks.writeOfflineKey(uid, pubkey, algo) 418 } 419 420 func (ks keystore) DeleteByAddress(address sdk.Address) error { 421 info, err := ks.KeyByAddress(address) 422 if err != nil { 423 return err 424 } 425 426 err = ks.Delete(info.GetName()) 427 if err != nil { 428 return err 429 } 430 431 return nil 432 } 433 434 func (ks keystore) Delete(uid string) error { 435 info, err := ks.Key(uid) 436 if err != nil { 437 return err 438 } 439 440 err = ks.db.Remove(addrHexKeyAsString(info.GetAddress())) 441 if err != nil { 442 return err 443 } 444 445 err = ks.db.Remove(infoKey(uid)) 446 if err != nil { 447 return err 448 } 449 450 return nil 451 } 452 453 func (ks keystore) KeyByAddress(address sdk.Address) (Info, error) { 454 ik, err := ks.db.Get(addrHexKeyAsString(address)) 455 if err != nil { 456 return nil, wrapKeyNotFound(err, fmt.Sprint("key with address", address, "not found")) 457 } 458 459 if len(ik.Data) == 0 { 460 return nil, wrapKeyNotFound(err, fmt.Sprint("key with address", address, "not found")) 461 } 462 return ks.key(string(ik.Data)) 463 } 464 465 func wrapKeyNotFound(err error, msg string) error { 466 if err == keyring.ErrKeyNotFound { 467 return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, msg) 468 } 469 return err 470 } 471 472 func (ks keystore) List() ([]Info, error) { 473 res := []Info{} 474 475 keys, err := ks.db.Keys() 476 if err != nil { 477 return nil, err 478 } 479 480 if len(keys) == 0 { 481 return res, nil 482 } 483 484 sort.Strings(keys) 485 for _, key := range keys { 486 if strings.HasSuffix(key, infoSuffix) { 487 rawInfo, err := ks.db.Get(key) 488 if err != nil { 489 fmt.Printf("err for key %s: %q\n", key, err) 490 491 // add the name of the key in case the user wants to retrieve it 492 // afterwards 493 info := newOfflineInfo(key, nil, hd.PubKeyType("")) 494 res = append(res, info) 495 continue 496 } 497 498 if len(rawInfo.Data) == 0 { 499 fmt.Println(sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key)) 500 501 // add the name of the key in case the user wants to retrieve it 502 // afterwards 503 info := newOfflineInfo(key, nil, hd.PubKeyType("")) 504 res = append(res, info) 505 continue 506 } 507 508 info, err := unmarshalInfo(rawInfo.Data) 509 if err != nil { 510 fmt.Printf("err for key %s: %q\n", key, err) 511 512 // add the name of the key in case the user wants to retrieve it 513 // afterwards 514 info = newOfflineInfo(key, nil, hd.PubKeyType("")) 515 } 516 517 res = append(res, info) 518 } 519 } 520 521 return res, nil 522 } 523 524 func (ks keystore) NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error) { 525 if language != English { 526 return nil, "", ErrUnsupportedLanguage 527 } 528 529 if !ks.isSupportedSigningAlgo(algo) { 530 return nil, "", ErrUnsupportedSigningAlgo 531 } 532 533 // Default number of words (24): This generates a mnemonic directly from the 534 // number of words by reading system entropy. 535 entropy, err := bip39.NewEntropy(defaultEntropySize) 536 if err != nil { 537 return nil, "", err 538 } 539 540 mnemonic, err := bip39.NewMnemonic(entropy) 541 if err != nil { 542 return nil, "", err 543 } 544 545 if bip39Passphrase == "" { 546 bip39Passphrase = DefaultBIP39Passphrase 547 } 548 549 info, err := ks.NewAccount(uid, mnemonic, bip39Passphrase, hdPath, algo) 550 if err != nil { 551 return nil, "", err 552 } 553 554 return info, mnemonic, nil 555 } 556 557 func (ks keystore) NewAccount(name string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (Info, error) { 558 if !ks.isSupportedSigningAlgo(algo) { 559 return nil, ErrUnsupportedSigningAlgo 560 } 561 562 // create master key and derive first key for keyring 563 derivedPriv, err := algo.Derive()(mnemonic, bip39Passphrase, hdPath) 564 if err != nil { 565 return nil, err 566 } 567 568 privKey := algo.Generate()(derivedPriv) 569 570 // check if the a key already exists with the same address and return an error 571 // if found 572 address := sdk.AccAddress(privKey.PubKey().Address()) 573 if _, err := ks.KeyByAddress(address); err == nil { 574 return nil, fmt.Errorf("account with address %s already exists in keyring, delete the key first if you want to recreate it", address) 575 } 576 577 return ks.writeLocalKey(name, privKey, algo.Name()) 578 } 579 580 func (ks keystore) isSupportedSigningAlgo(algo SignatureAlgo) bool { 581 return ks.options.SupportedAlgos.Contains(algo) 582 } 583 584 func (ks keystore) key(infoKey string) (Info, error) { 585 bs, err := ks.db.Get(infoKey) 586 if err != nil { 587 return nil, wrapKeyNotFound(err, infoKey) 588 } 589 if len(bs.Data) == 0 { 590 return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, infoKey) 591 } 592 return unmarshalInfo(bs.Data) 593 } 594 595 func (ks keystore) Key(uid string) (Info, error) { 596 return ks.key(infoKey(uid)) 597 } 598 599 // SupportedAlgorithms returns the keystore Options' supported signing algorithm. 600 // for the keyring and Ledger. 601 func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) { 602 return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger 603 } 604 605 // SignWithLedger signs a binary message with the ledger device referenced by an Info object 606 // and returns the signed bytes and the public key. It returns an error if the device could 607 // not be queried or it returned an error. 608 func SignWithLedger(info Info, msg []byte) (sig []byte, pub types.PubKey, err error) { 609 switch info.(type) { 610 case *ledgerInfo, ledgerInfo: 611 default: 612 return nil, nil, errors.New("not a ledger object") 613 } 614 615 path, err := info.GetPath() 616 if err != nil { 617 return 618 } 619 620 priv, err := ledger.NewPrivKeySecp256k1Unsafe(*path) 621 if err != nil { 622 return 623 } 624 625 sig, err = priv.Sign(msg) 626 if err != nil { 627 return nil, nil, err 628 } 629 630 return sig, priv.PubKey(), nil 631 } 632 633 func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config { 634 return keyring.Config{ 635 ServiceName: appName, 636 FileDir: dir, 637 KeychainTrustApplication: true, 638 FilePasswordFunc: newRealPrompt(dir, buf), 639 } 640 } 641 642 func newTestBackendKeyringConfig(appName, dir string) keyring.Config { 643 return keyring.Config{ 644 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 645 ServiceName: appName, 646 FileDir: filepath.Join(dir, keyringTestDirName), 647 FilePasswordFunc: func(_ string) (string, error) { 648 return "test", nil 649 }, 650 } 651 } 652 653 func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 654 return keyring.Config{ 655 AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, 656 ServiceName: "kdewallet", 657 KWalletAppID: appName, 658 KWalletFolder: "", 659 } 660 } 661 662 func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { 663 prefix := fmt.Sprintf(passKeyringPrefix, appName) 664 665 return keyring.Config{ 666 AllowedBackends: []keyring.BackendType{keyring.PassBackend}, 667 ServiceName: appName, 668 PassPrefix: prefix, 669 } 670 } 671 672 func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config { 673 fileDir := filepath.Join(dir, keyringFileDirName) 674 675 return keyring.Config{ 676 AllowedBackends: []keyring.BackendType{keyring.FileBackend}, 677 ServiceName: name, 678 FileDir: fileDir, 679 FilePasswordFunc: newRealPrompt(fileDir, buf), 680 } 681 } 682 683 func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { 684 return func(prompt string) (string, error) { 685 keyhashStored := false 686 keyhashFilePath := filepath.Join(dir, "keyhash") 687 688 var keyhash []byte 689 690 _, err := os.Stat(keyhashFilePath) 691 692 switch { 693 case err == nil: 694 keyhash, err = os.ReadFile(keyhashFilePath) 695 if err != nil { 696 return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err) 697 } 698 699 keyhashStored = true 700 701 case os.IsNotExist(err): 702 keyhashStored = false 703 704 default: 705 return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err) 706 } 707 708 failureCounter := 0 709 710 for { 711 failureCounter++ 712 if failureCounter > maxPassphraseEntryAttempts { 713 return "", fmt.Errorf("too many failed passphrase attempts") 714 } 715 716 buf := bufio.NewReader(buf) 717 pass, err := input.GetPassword("Enter keyring passphrase:", buf) 718 if err != nil { 719 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 720 // but we only log the error. 721 // 722 // lgtm [go/clear-text-logging] 723 fmt.Fprintln(os.Stderr, err) 724 continue 725 } 726 727 if keyhashStored { 728 if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { 729 fmt.Fprintln(os.Stderr, "incorrect passphrase") 730 continue 731 } 732 733 return pass, nil 734 } 735 736 reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) 737 if err != nil { 738 // NOTE: LGTM.io reports a false positive alert that states we are printing the password, 739 // but we only log the error. 740 // 741 // lgtm [go/clear-text-logging] 742 fmt.Fprintln(os.Stderr, err) 743 continue 744 } 745 746 if pass != reEnteredPass { 747 fmt.Fprintln(os.Stderr, "passphrase do not match") 748 continue 749 } 750 751 saltBytes := occrypto.CRandBytes(16) 752 passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2) 753 if err != nil { 754 fmt.Fprintln(os.Stderr, err) 755 continue 756 } 757 758 if err := os.WriteFile(dir+"/keyhash", passwordHash, 0o600); err != nil { 759 return "", err 760 } 761 762 return pass, nil 763 } 764 } 765 } 766 767 func (ks keystore) writeLocalKey(name string, priv types.PrivKey, algo hd.PubKeyType) (Info, error) { 768 // encrypt private key using keyring 769 pub := priv.PubKey() 770 info := newLocalInfo(name, pub, string(legacy.Cdc.MustMarshal(priv)), algo) 771 if err := ks.writeInfo(info); err != nil { 772 return nil, err 773 } 774 775 return info, nil 776 } 777 778 func (ks keystore) writeInfo(info Info) error { 779 key := infoKeyBz(info.GetName()) 780 serializedInfo := marshalInfo(info) 781 782 exists, err := ks.existsInDb(info) 783 if err != nil { 784 return err 785 } 786 if exists { 787 return errors.New("public key already exists in keybase") 788 } 789 790 err = ks.db.Set(keyring.Item{ 791 Key: string(key), 792 Data: serializedInfo, 793 }) 794 if err != nil { 795 return err 796 } 797 798 err = ks.db.Set(keyring.Item{ 799 Key: addrHexKeyAsString(info.GetAddress()), 800 Data: key, 801 }) 802 if err != nil { 803 return err 804 } 805 806 return nil 807 } 808 809 // existsInDb returns true if key is in DB. Error is returned only when we have error 810 // different thant ErrKeyNotFound 811 func (ks keystore) existsInDb(info Info) (bool, error) { 812 if _, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil { 813 return true, nil // address lookup succeeds - info exists 814 } else if err != keyring.ErrKeyNotFound { 815 return false, err // received unexpected error - returns error 816 } 817 818 if _, err := ks.db.Get(infoKey(info.GetName())); err == nil { 819 return true, nil // uid lookup succeeds - info exists 820 } else if err != keyring.ErrKeyNotFound { 821 return false, err // received unexpected error - returns 822 } 823 824 // both lookups failed, info does not exist 825 return false, nil 826 } 827 828 func (ks keystore) writeOfflineKey(name string, pub types.PubKey, algo hd.PubKeyType) (Info, error) { 829 info := newOfflineInfo(name, pub, algo) 830 err := ks.writeInfo(info) 831 if err != nil { 832 return nil, err 833 } 834 835 return info, nil 836 } 837 838 func (ks keystore) writeMultisigKey(name string, pub types.PubKey) (Info, error) { 839 info, err := NewMultiInfo(name, pub) 840 if err != nil { 841 return nil, err 842 } 843 if err = ks.writeInfo(info); err != nil { 844 return nil, err 845 } 846 847 return info, nil 848 } 849 850 type unsafeKeystore struct { 851 keystore 852 } 853 854 // NewUnsafe returns a new keyring that provides support for unsafe operations. 855 func NewUnsafe(kr Keyring) UnsafeKeyring { 856 // The type assertion is against the only keystore 857 // implementation that is currently provided. 858 ks := kr.(keystore) 859 860 return unsafeKeystore{ks} 861 } 862 863 // UnsafeExportPrivKeyHex exports private keys in unarmored hexadecimal format. 864 func (ks unsafeKeystore) UnsafeExportPrivKeyHex(uid string) (privkey string, err error) { 865 priv, err := ks.ExportPrivateKeyObject(uid) 866 if err != nil { 867 return "", err 868 } 869 870 return hex.EncodeToString(priv.Bytes()), nil 871 } 872 873 func addrHexKeyAsString(address sdk.Address) string { 874 return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix) 875 }