You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
3.0 KiB

  1. package cmd
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/bgentry/speakeasy"
  9. "github.com/mattn/go-isatty"
  10. "github.com/pkg/errors"
  11. "github.com/spf13/viper"
  12. data "github.com/tendermint/go-wire/data"
  13. "github.com/tendermint/tmlibs/cli"
  14. keys "github.com/tendermint/go-crypto/keys"
  15. "github.com/tendermint/go-crypto/keys/cryptostore"
  16. "github.com/tendermint/go-crypto/keys/storage/filestorage"
  17. )
  18. const MinPassLength = 10
  19. // GetKeyManager initializes a key manager based on the configuration
  20. func GetKeyManager() keys.Manager {
  21. if manager == nil {
  22. // store the keys directory
  23. rootDir := viper.GetString(cli.HomeFlag)
  24. keyDir := filepath.Join(rootDir, KeySubdir)
  25. // TODO: smarter loading??? with language and fallback?
  26. codec := keys.MustLoadCodec("english")
  27. // and construct the key manager
  28. manager = cryptostore.New(
  29. cryptostore.SecretBox,
  30. filestorage.New(keyDir),
  31. codec,
  32. )
  33. }
  34. return manager
  35. }
  36. // if we read from non-tty, we just need to init the buffer reader once,
  37. // in case we try to read multiple passwords (eg. update)
  38. var buf *bufio.Reader
  39. func inputIsTty() bool {
  40. return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
  41. }
  42. func stdinPassword() (string, error) {
  43. if buf == nil {
  44. buf = bufio.NewReader(os.Stdin)
  45. }
  46. pass, err := buf.ReadString('\n')
  47. if err != nil {
  48. return "", err
  49. }
  50. return strings.TrimSpace(pass), nil
  51. }
  52. func getPassword(prompt string) (pass string, err error) {
  53. if inputIsTty() {
  54. pass, err = speakeasy.Ask(prompt)
  55. } else {
  56. pass, err = stdinPassword()
  57. }
  58. if err != nil {
  59. return "", err
  60. }
  61. if len(pass) < MinPassLength {
  62. return "", errors.Errorf("Password must be at least %d characters", MinPassLength)
  63. }
  64. return pass, nil
  65. }
  66. func getSeed(prompt string) (seed string, err error) {
  67. if inputIsTty() {
  68. fmt.Println(prompt)
  69. }
  70. seed, err = stdinPassword()
  71. seed = strings.TrimSpace(seed)
  72. return
  73. }
  74. func getCheckPassword(prompt, prompt2 string) (string, error) {
  75. // simple read on no-tty
  76. if !inputIsTty() {
  77. return getPassword(prompt)
  78. }
  79. // TODO: own function???
  80. pass, err := getPassword(prompt)
  81. if err != nil {
  82. return "", err
  83. }
  84. pass2, err := getPassword(prompt2)
  85. if err != nil {
  86. return "", err
  87. }
  88. if pass != pass2 {
  89. return "", errors.New("Passphrases don't match")
  90. }
  91. return pass, nil
  92. }
  93. func printInfo(info keys.Info) {
  94. switch viper.Get(cli.OutputFlag) {
  95. case "text":
  96. addr, err := data.ToText(info.Address)
  97. if err != nil {
  98. panic(err) // really shouldn't happen...
  99. }
  100. sep := "\t\t"
  101. if len(info.Name) > 7 {
  102. sep = "\t"
  103. }
  104. fmt.Printf("%s%s%s\n", info.Name, sep, addr)
  105. case "json":
  106. json, err := data.ToJSON(info)
  107. if err != nil {
  108. panic(err) // really shouldn't happen...
  109. }
  110. fmt.Println(string(json))
  111. }
  112. }
  113. func printInfos(infos keys.Infos) {
  114. switch viper.Get(cli.OutputFlag) {
  115. case "text":
  116. fmt.Println("All keys:")
  117. for _, i := range infos {
  118. printInfo(i)
  119. }
  120. case "json":
  121. json, err := data.ToJSON(infos)
  122. if err != nil {
  123. panic(err) // really shouldn't happen...
  124. }
  125. fmt.Println(string(json))
  126. }
  127. }