pass.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package gopass
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. var defaultGetCh = func() (byte, error) {
  9. buf := make([]byte, 1)
  10. if n, err := os.Stdin.Read(buf); n == 0 || err != nil {
  11. if err != nil {
  12. return 0, err
  13. }
  14. return 0, io.EOF
  15. }
  16. return buf[0], nil
  17. }
  18. var (
  19. maxLength = 512
  20. ErrInterrupted = errors.New("interrupted")
  21. ErrMaxLengthExceeded = fmt.Errorf("maximum byte limit (%v) exceeded", maxLength)
  22. // Provide variable so that tests can provide a mock implementation.
  23. getch = defaultGetCh
  24. )
  25. // getPasswd returns the input read from terminal.
  26. // If masked is true, typing will be matched by asterisks on the screen.
  27. // Otherwise, typing will echo nothing.
  28. func getPasswd(masked bool) ([]byte, error) {
  29. var err error
  30. var pass, bs, mask []byte
  31. if masked {
  32. bs = []byte("\b \b")
  33. mask = []byte("*")
  34. }
  35. if isTerminal(os.Stdin.Fd()) {
  36. if oldState, err := makeRaw(os.Stdin.Fd()); err != nil {
  37. return pass, err
  38. } else {
  39. defer restore(os.Stdin.Fd(), oldState)
  40. }
  41. }
  42. // Track total bytes read, not just bytes in the password. This ensures any
  43. // errors that might flood the console with nil or -1 bytes infinitely are
  44. // capped.
  45. var counter int
  46. for counter = 0; counter <= maxLength; counter++ {
  47. if v, e := getch(); e != nil {
  48. err = e
  49. break
  50. } else if v == 127 || v == 8 {
  51. if l := len(pass); l > 0 {
  52. pass = pass[:l-1]
  53. fmt.Print(string(bs))
  54. }
  55. } else if v == 13 || v == 10 {
  56. break
  57. } else if v == 3 {
  58. err = ErrInterrupted
  59. break
  60. } else if v != 0 {
  61. pass = append(pass, v)
  62. fmt.Print(string(mask))
  63. }
  64. }
  65. if counter > maxLength {
  66. err = ErrMaxLengthExceeded
  67. }
  68. fmt.Println()
  69. return pass, err
  70. }
  71. // GetPasswd returns the password read from the terminal without echoing input.
  72. // The returned byte array does not include end-of-line characters.
  73. func GetPasswd() ([]byte, error) {
  74. return getPasswd(false)
  75. }
  76. // GetPasswdMasked returns the password read from the terminal, echoing asterisks.
  77. // The returned byte array does not include end-of-line characters.
  78. func GetPasswdMasked() ([]byte, error) {
  79. return getPasswd(true)
  80. }