1
2
3
4
5
6
7
8
9
10
11
12
13 package sha512crypt
14
15 import (
16 "bytes"
17 "crypto/sha512"
18 "strconv"
19
20 crypt "edge-infra.dev/pkg/lib/crypto/osutilcrypt"
21 "edge-infra.dev/pkg/lib/crypto/osutilcrypt/common"
22 )
23
24 func init() {
25 crypt.RegisterCrypt(crypt.SHA512, New, MagicPrefix)
26 }
27
28 const (
29 MagicPrefix = "$6$"
30 SaltLenMin = 1
31 SaltLenMax = 16
32 RoundsMin = 1000
33 RoundsMax = 999999999
34 RoundsDefault = 5000
35 )
36
37 var _rounds = []byte("rounds=")
38
39 type crypter struct{ Salt common.Salt }
40
41
42 func New() crypt.Crypter {
43 return &crypter{GetSalt()}
44 }
45
46 func (c *crypter) Generate(key, salt []byte) (string, error) {
47 var rounds int
48 var isRoundsDef bool
49
50 if len(salt) == 0 {
51 salt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault)
52 }
53 if !bytes.HasPrefix(salt, c.Salt.MagicPrefix) {
54 return "", common.ErrSaltPrefix
55 }
56
57 saltToks := bytes.Split(salt, []byte{'$'})
58 if len(saltToks) < 3 {
59 return "", common.ErrSaltFormat
60 }
61
62 if bytes.HasPrefix(saltToks[2], _rounds) {
63 isRoundsDef = true
64 pr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32)
65 if err != nil {
66 return "", common.ErrSaltRounds
67 }
68 rounds = int(pr)
69 if rounds < RoundsMin {
70 rounds = RoundsMin
71 } else if rounds > RoundsMax {
72 rounds = RoundsMax
73 }
74 salt = saltToks[3]
75 } else {
76 rounds = RoundsDefault
77 salt = saltToks[2]
78 }
79
80 if len(salt) > SaltLenMax {
81 salt = salt[0:SaltLenMax]
82 }
83
84
85 Alternate := sha512.New()
86 Alternate.Write(key)
87 Alternate.Write(salt)
88 Alternate.Write(key)
89 AlternateSum := Alternate.Sum(nil)
90
91 A := sha512.New()
92 A.Write(key)
93 A.Write(salt)
94
95 i := len(key)
96 for ; i > 64; i -= 64 {
97 A.Write(AlternateSum)
98 }
99 A.Write(AlternateSum[0:i])
100
101
102
103 for i = len(key); i > 0; i >>= 1 {
104 if (i & 1) != 0 {
105 A.Write(AlternateSum)
106 } else {
107 A.Write(key)
108 }
109 }
110 Asum := A.Sum(nil)
111
112
113 P := sha512.New()
114
115 for i = 0; i < len(key); i++ {
116 P.Write(key)
117 }
118 Psum := P.Sum(nil)
119
120 Pseq := make([]byte, 0, len(key))
121 for i = len(key); i > 64; i -= 64 {
122 Pseq = append(Pseq, Psum...)
123 }
124 Pseq = append(Pseq, Psum[0:i]...)
125
126
127 S := sha512.New()
128 for i = 0; i < (16 + int(Asum[0])); i++ {
129 S.Write(salt)
130 }
131 Ssum := S.Sum(nil)
132
133 Sseq := make([]byte, 0, len(salt))
134 for i = len(salt); i > 64; i -= 64 {
135 Sseq = append(Sseq, Ssum...)
136 }
137 Sseq = append(Sseq, Ssum[0:i]...)
138
139 Csum := Asum
140
141
142 for i = 0; i < rounds; i++ {
143 C := sha512.New()
144
145
146 if (i & 1) != 0 {
147 C.Write(Pseq)
148 } else {
149 C.Write(Csum)
150 }
151
152 if (i % 3) != 0 {
153 C.Write(Sseq)
154 }
155
156 if (i % 7) != 0 {
157 C.Write(Pseq)
158 }
159
160 if (i & 1) != 0 {
161 C.Write(Csum)
162 } else {
163 C.Write(Pseq)
164 }
165
166 Csum = C.Sum(nil)
167 }
168
169 out := make([]byte, 0, 123)
170 out = append(out, c.Salt.MagicPrefix...)
171 if isRoundsDef {
172 out = append(out, []byte("rounds="+strconv.Itoa(rounds)+"$")...)
173 }
174 out = append(out, salt...)
175 out = append(out, '$')
176 out = append(out, common.Base64_24Bit([]byte{
177 Csum[42], Csum[21], Csum[0],
178 Csum[1], Csum[43], Csum[22],
179 Csum[23], Csum[2], Csum[44],
180 Csum[45], Csum[24], Csum[3],
181 Csum[4], Csum[46], Csum[25],
182 Csum[26], Csum[5], Csum[47],
183 Csum[48], Csum[27], Csum[6],
184 Csum[7], Csum[49], Csum[28],
185 Csum[29], Csum[8], Csum[50],
186 Csum[51], Csum[30], Csum[9],
187 Csum[10], Csum[52], Csum[31],
188 Csum[32], Csum[11], Csum[53],
189 Csum[54], Csum[33], Csum[12],
190 Csum[13], Csum[55], Csum[34],
191 Csum[35], Csum[14], Csum[56],
192 Csum[57], Csum[36], Csum[15],
193 Csum[16], Csum[58], Csum[37],
194 Csum[38], Csum[17], Csum[59],
195 Csum[60], Csum[39], Csum[18],
196 Csum[19], Csum[61], Csum[40],
197 Csum[41], Csum[20], Csum[62],
198 Csum[63],
199 })...)
200
201
202 A.Reset()
203 Alternate.Reset()
204 P.Reset()
205 for i = 0; i < len(Asum); i++ {
206 Asum[i] = 0
207 }
208 for i = 0; i < len(AlternateSum); i++ {
209 AlternateSum[i] = 0
210 }
211 for i = 0; i < len(Pseq); i++ {
212 Pseq[i] = 0
213 }
214
215 return string(out), nil
216 }
217
218 func (c *crypter) Verify(hashedKey string, key []byte) error {
219 newHash, err := c.Generate(key, []byte(hashedKey))
220 if err != nil {
221 return err
222 }
223 if newHash != hashedKey {
224 return crypt.ErrKeyMismatch
225 }
226 return nil
227 }
228
229 func (c *crypter) Cost(hashedKey string) (int, error) {
230 saltToks := bytes.Split([]byte(hashedKey), []byte{'$'})
231 if len(saltToks) < 3 {
232 return 0, common.ErrSaltFormat
233 }
234
235 if !bytes.HasPrefix(saltToks[2], _rounds) {
236 return RoundsDefault, nil
237 }
238 roundToks := bytes.Split(saltToks[2], []byte{'='})
239 cost, err := strconv.ParseInt(string(roundToks[1]), 10, 0)
240 return int(cost), err
241 }
242
243 func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }
244
245 func GetSalt() common.Salt {
246 return common.Salt{
247 MagicPrefix: []byte(MagicPrefix),
248 SaltLenMin: SaltLenMin,
249 SaltLenMax: SaltLenMax,
250 RoundsDefault: RoundsDefault,
251 RoundsMin: RoundsMin,
252 RoundsMax: RoundsMax,
253 }
254 }
255
View as plain text