-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
104 lines (96 loc) · 2.62 KB
/
main.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
// Command-line version of Stanford PwdHash (https://www.pwdhash.com/)
package main
import (
"bytes"
"code.google.com/p/gopass"
"crypto/hmac"
"crypto/md5"
"encoding/base64"
"fmt"
"io"
"net/url"
"os"
"regexp"
"strings"
"unicode"
)
var nonWord, _ = regexp.Compile(`\W`)
func main() {
if len(os.Args) != 2 {
usage()
return
}
realm := extractDomain(os.Args[1])
password, _ := gopass.GetPass(fmt.Sprintf("Password for %s: ", realm))
fmt.Println(pwdhash(realm, password))
}
func usage() {
fmt.Println("Usage: pwdhash <url>")
fmt.Println("Example: pwdhash http://www.google.com")
}
func extractDomain(urlString string) string {
if hasScheme, _ := regexp.MatchString(`https?://.*`, urlString); !hasScheme {
urlString = "http://" + urlString
}
u, err := url.Parse(urlString)
if err != nil {
fmt.Println("Provided argument is not a valid URL!")
os.Exit(1)
}
d := strings.Split(strings.Split(u.Host, ":")[0], ".")
return strings.Join(d[len(d)-2:], ".")
}
func pwdhash(realm, password string) string {
hmac := hmac.New(md5.New, []byte(password))
io.WriteString(hmac, realm)
hash := base64.StdEncoding.EncodeToString(hmac.Sum(nil))
size := len(password) + 2
nonalphanumeric := nonWord.FindStringIndex(password) != nil
return applyConstraints(hash, size, nonalphanumeric)
}
// Make sure that the resulting password has at least one capital, one lowercase
// and one numeric character.
// If the input contained a non-alphanumeric, make sure
// the hashed password also has one, otherwise make sure it doesn't have one.
func applyConstraints(hash string, size int, nonalphanumeric bool) string {
startingSize := size - 4
result := make([]byte, startingSize, size)
copy(result, hash[:startingSize])
extras := []byte(hash[startingSize:])
next := extras[0]
extras = extras[1:]
if bytes.IndexFunc(result, unicode.IsUpper) < 0 {
next = 'A' + next%26
}
result = append(result, next)
next = extras[0]
extras = extras[1:]
if bytes.IndexFunc(result, unicode.IsLower) < 0 {
next = 'a' + next%26
}
result = append(result, next)
next = extras[0]
extras = extras[1:]
if bytes.IndexFunc(result, unicode.IsDigit) < 0 {
next = '0' + next%10
}
result = append(result, next)
if !nonalphanumeric || nonWord.FindIndex(result) == nil {
next = '+'
} else {
next = extras[0]
extras = extras[1:]
}
result = append(result, next)
if !nonalphanumeric {
loc := nonWord.FindIndex(result)
for ; loc != nil; loc = nonWord.FindIndex(result) {
next = extras[0]
extras = extras[1:]
result[loc[0]] = 'A' + next%26
}
}
next = extras[0] % byte(len(result))
rotated := append(result[next:], result[:next]...)
return string(rotated)
}