/
hash.go
176 lines (151 loc) · 4.87 KB
/
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package secret
import (
"crypto/md5"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"strconv"
"strings"
"time"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2"
)
// This package provides a way to generate a different type of hash for each day of the week.
//
// If the hashing algorithm does not allow for a salt, we will append the salt to the data.
//
// The hashing algorithm is chosen based on the day of the week.
//
// Algorithms:
// 0 - SHA256 (Sunday)
// 1 - MD5 (Monday)
// 2 - SHA512 (Tuesday)
// 3 - SHA384 (Wednesday)
// 4 - Bcrypt (Thursday)
// 5 - ARGON2 (Friday)
// 6 - PBKDF2 (Saturday)
//
// The hash is in the format: <day>-<first letter of day>$<hash>
// Example: 0-S$<hash>
//
// Multiple iterations of hashing is not supported.
// IE: If the hash is 0-S$<hash>, it should not be hashed again.
//
// We allow overwriting of the daily hashers.
var (
Day0Hasher = Hasher{hash0SHA256, compare0SHA256} // Sunday (SHA256)
Day1Hasher = Hasher{hash1MD5, compare1MD5} // Monday (MD5)
Day2Hasher = Hasher{hash2SHA512, compare2SHA512} // Tuesday (SHA512)
Day3Hasher = Hasher{hash3SHA384, compare3SHA384} // Wednesday (SHA384)
Day4Hasher = Hasher{hash4Bcrypt, compare4Bcrypt} // Thursday (Bcrypt)
Day5Hasher = Hasher{hash5ARGON2, compare5ARGON2} // Friday (ARGON2)
Day6Hasher = Hasher{hash6PBKDF2, compare6PBKDF2} // Saturday (PBKDF2)
)
var weekDays = map[string]Hasher{
"0-S$": Day0Hasher, // Sunday
"1-M$": Day1Hasher, // Monday
"2-T$": Day2Hasher, // Tuesday
"3-W$": Day3Hasher, // Wednesday
"4-T$": Day4Hasher, // Thursday
"5-F$": Day5Hasher, // Friday
"6-S$": Day6Hasher, // Saturday
}
// A hash function should return a hash of the data and salt.
type HashFunc func(data, salt []byte) string
// A compare function should return true if the data and salt match the hash.
type CompareFunc func(data, salt []byte, hash string) bool
// A hasher is a struct that contains a hash function and a compare function.
//
// The hash function should return a hash of the data and salt.
//
// The compare function should return true if the data and salt match the hash.
type Hasher struct {
hashFunc HashFunc
validFunc CompareFunc
}
// Recommended use is only in testing!
func HashForDay(data, salt []byte, day time.Weekday) string {
var b strings.Builder
var dayString = strconv.Itoa(int(day))
var weekDayString = day.String()[0:1]
b.WriteString(dayString)
b.WriteString("-")
b.WriteString(weekDayString)
b.WriteString("$")
var hash, ok = weekDays[b.String()]
if !ok {
panic("Hasher not found")
}
b.WriteString(hash.hashFunc(data, salt))
return b.String()
}
// Hashes the data with the salt, if the algorithm for that day supports it.
// If not, hash the data, appending the salt to it.
func Hash(data, salt []byte) string {
var day = time.Now().Weekday()
return HashForDay(data, salt, day)
}
// Compares the data with the salt and the hash.
// If the hash is not in the correct format, it will return false.
func Compare(data, salt []byte, hash string) bool {
if len(hash) <= 4 {
return false
}
var hashNum = hash[0:4]
var hasher, ok = weekDays[hashNum]
if !ok {
return false
}
hash = hash[4:]
return hasher.validFunc(data, salt, hash)
}
func hash0SHA256(data, salt []byte) string {
var hash = sha256.Sum256(append(data, salt...))
return hex.EncodeToString(hash[:])
}
func compare0SHA256(data, salt []byte, hash string) bool {
return hash0SHA256(data, salt) == hash
}
func hash1MD5(data, salt []byte) string {
var hash = md5.Sum(append(data, salt...))
return hex.EncodeToString(hash[:])
}
func compare1MD5(data, salt []byte, hash string) bool {
return hash1MD5(data, salt) == hash
}
func hash2SHA512(data, salt []byte) string {
var hash = sha512.Sum512(append(data, salt...))
return hex.EncodeToString(hash[:])
}
func compare2SHA512(data, salt []byte, hash string) bool {
return hash2SHA512(data, salt) == hash
}
func hash3SHA384(data, salt []byte) string {
var hash = sha512.Sum384(append(data, salt...))
return hex.EncodeToString(hash[:])
}
func compare3SHA384(data, salt []byte, hash string) bool {
return hash3SHA384(data, salt) == hash
}
func hash4Bcrypt(data, salt []byte) string {
bcryptHash, _ := bcrypt.GenerateFromPassword(append(data, salt...), bcrypt.DefaultCost)
return string(bcryptHash)
}
func compare4Bcrypt(data, salt []byte, hash string) bool {
return bcrypt.CompareHashAndPassword([]byte(hash), append(data, salt...)) == nil
}
func hash5ARGON2(data, salt []byte) string {
var hash = argon2.Key(data, salt, 1, 64*1024, 4, 32)
return hex.EncodeToString(hash)
}
func compare5ARGON2(data, salt []byte, hash string) bool {
return hash5ARGON2(data, salt) == hash
}
func hash6PBKDF2(data, salt []byte) string {
var hash = pbkdf2.Key(data, salt, 100000, 32, sha256.New)
return hex.EncodeToString(hash)
}
func compare6PBKDF2(data, salt []byte, hash string) bool {
return hash6PBKDF2(data, salt) == hash
}