-
Notifications
You must be signed in to change notification settings - Fork 151
/
pwd_hash.go
146 lines (120 loc) · 3.81 KB
/
pwd_hash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package toolset
import (
"bytes"
"encoding/hex"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/pkg/errors"
flag "github.com/spf13/pflag"
"golang.org/x/term"
"github.com/iotaledger/hive.go/app/configuration"
"github.com/iotaledger/hive.go/web/basicauth"
"github.com/iotaledger/hornet/v2/pkg/utils"
)
func readPasswordFromEnv() ([]byte, error) {
passwordEnv, err := utils.LoadStringFromEnvironment(passwordEnvKey)
if err != nil {
return nil, err
}
return []byte(passwordEnv), nil
}
func readPasswordFromStdin() ([]byte, error) {
var password []byte
// get terminal state to be able to restore it in case of an interrupt
originalTerminalState, err := term.GetState(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows
if err != nil {
return nil, errors.New("failed to get terminal state")
}
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
<-signalChan
// reset the terminal to the original state if we receive an interrupt
_ = term.Restore(int(syscall.Stdin), originalTerminalState) //nolint:nolintlint,unconvert // int cast is needed for windows
fmt.Println("\naborted ... Bye!")
os.Exit(1)
}()
fmt.Print("Enter a password: ")
password, err = term.ReadPassword(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows
if err != nil {
return nil, fmt.Errorf("read password failed: %w", err)
}
fmt.Print("\nRe-enter your password: ")
passwordReenter, err := term.ReadPassword(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows
if err != nil {
return nil, fmt.Errorf("read password failed: %w", err)
}
if !bytes.Equal(password, passwordReenter) {
return nil, errors.New("re-entered password doesn't match")
}
fmt.Println()
return password, nil
}
func hashPasswordAndSalt(args []string) error {
fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
saltFlag := fs.String(FlagToolSalt, "", "salt used to hash the password (optional)")
passwordFlag := fs.String(FlagToolPassword, "", fmt.Sprintf("password to hash. Can also be passed as %s environment variable.", passwordEnvKey))
outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
fs.Usage = func() {
_, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolPwdHash)
fs.PrintDefaults()
println(fmt.Sprintf("\nexample: %s --%s %s",
ToolPwdHash,
FlagToolPassword,
"[PASSWORD]",
))
}
if err := parseFlagSet(fs, args); err != nil {
return err
}
var err error
var passwordSalt []byte
if len(*saltFlag) > 0 {
// Salt passed over flag
if len(*saltFlag) != 64 {
return errors.New("the given salt must be 64 (hex encoded) in length")
}
passwordSalt, err = hex.DecodeString(*saltFlag)
if err != nil {
return fmt.Errorf("parsing given salt failed: %w", err)
}
} else {
passwordSalt, err = basicauth.SaltGenerator(32)
if err != nil {
return fmt.Errorf("generating random salt failed: %w", err)
}
}
var password []byte
if p, err := readPasswordFromEnv(); err == nil {
// Password passed over the environment
password = p
} else if len(*passwordFlag) > 0 {
// Password passed over flag
password = []byte(*passwordFlag)
} else {
// Read from stdin
p, err := readPasswordFromStdin()
if err != nil {
return err
}
password = p
}
passwordKey, err := basicauth.DerivePasswordKey(password, passwordSalt)
if err != nil {
return fmt.Errorf("deriving password key failed: %w", err)
}
if *outputJSONFlag {
result := struct {
Password string `json:"passwordHash"`
Salt string `json:"passwordSalt"`
}{
Password: hex.EncodeToString(passwordKey),
Salt: hex.EncodeToString(passwordSalt),
}
return printJSON(result)
}
fmt.Printf("\nSuccess!\nYour hash: %x\nYour salt: %x\n", passwordKey, passwordSalt)
return nil
}