Skip to content

Commit 4669be6

Browse files
authored
fix: Modify the pure-ftpd encryption method (#8312)
1 parent 4a249ba commit 4669be6

3 files changed

Lines changed: 339 additions & 36 deletions

File tree

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package helper
2+
3+
import (
4+
"bytes"
5+
"crypto/rand"
6+
"crypto/sha512"
7+
"errors"
8+
"strconv"
9+
)
10+
11+
const (
12+
SaltLenMin = 1
13+
SaltLenMax = 16
14+
RoundsMin = 1000
15+
RoundsMax = 999999999
16+
RoundsDefault = 5000
17+
)
18+
19+
var _rounds = []byte("rounds=")
20+
21+
func Generate(key []byte) (string, error) {
22+
var rounds int
23+
var isRoundsDef bool
24+
25+
salt := generateWRounds()
26+
magicPrefix := []byte("$6$")
27+
if !bytes.HasPrefix(salt, magicPrefix) {
28+
return "", errors.New("invalid magic prefix")
29+
}
30+
31+
saltItem := bytes.Split(salt, []byte{'$'})
32+
if len(saltItem) < 3 {
33+
return "", errors.New("invalid salt format")
34+
}
35+
36+
if bytes.HasPrefix(saltItem[2], _rounds) {
37+
isRoundsDef = true
38+
pr, err := strconv.ParseInt(string(saltItem[2][7:]), 10, 32)
39+
if err != nil {
40+
return "", errors.New("invalid rounds")
41+
}
42+
rounds = int(pr)
43+
if rounds < RoundsMin {
44+
rounds = RoundsMin
45+
} else if rounds > RoundsMax {
46+
rounds = RoundsMax
47+
}
48+
salt = saltItem[3]
49+
} else {
50+
rounds = RoundsDefault
51+
salt = saltItem[2]
52+
}
53+
54+
if len(salt) > SaltLenMax {
55+
salt = salt[0:SaltLenMax]
56+
}
57+
58+
Alternate := sha512.New()
59+
Alternate.Write(key)
60+
Alternate.Write(salt)
61+
Alternate.Write(key)
62+
AlternateSum := Alternate.Sum(nil)
63+
64+
A := sha512.New()
65+
A.Write(key)
66+
A.Write(salt)
67+
i := len(key)
68+
for ; i > 64; i -= 64 {
69+
A.Write(AlternateSum)
70+
}
71+
A.Write(AlternateSum[0:i])
72+
73+
for i = len(key); i > 0; i >>= 1 {
74+
if (i & 1) != 0 {
75+
A.Write(AlternateSum)
76+
} else {
77+
A.Write(key)
78+
}
79+
}
80+
A_sum := A.Sum(nil)
81+
82+
P := sha512.New()
83+
for i = 0; i < len(key); i++ {
84+
P.Write(key)
85+
}
86+
P_sum := P.Sum(nil)
87+
P_seq := make([]byte, 0, len(key))
88+
for i = len(key); i > 64; i -= 64 {
89+
P_seq = append(P_seq, P_sum...)
90+
}
91+
P_seq = append(P_seq, P_sum[0:i]...)
92+
93+
S := sha512.New()
94+
for i = 0; i < (16 + int(A_sum[0])); i++ {
95+
S.Write(salt)
96+
}
97+
S_sum := S.Sum(nil)
98+
S_seq := make([]byte, 0, len(salt))
99+
for i = len(salt); i > 64; i -= 64 {
100+
S_seq = append(S_seq, S_sum...)
101+
}
102+
S_seq = append(S_seq, S_sum[0:i]...)
103+
104+
C_sum := A_sum
105+
106+
for i = 0; i < rounds; i++ {
107+
C := sha512.New()
108+
if (i & 1) != 0 {
109+
C.Write(P_seq)
110+
} else {
111+
C.Write(C_sum)
112+
}
113+
if (i % 3) != 0 {
114+
C.Write(S_seq)
115+
}
116+
if (i % 7) != 0 {
117+
C.Write(P_seq)
118+
}
119+
if (i & 1) != 0 {
120+
C.Write(C_sum)
121+
} else {
122+
C.Write(P_seq)
123+
}
124+
125+
C_sum = C.Sum(nil)
126+
}
127+
128+
out := make([]byte, 0, 123)
129+
out = append(out, magicPrefix...)
130+
if isRoundsDef {
131+
out = append(out, []byte("rounds="+strconv.Itoa(rounds)+"$")...)
132+
}
133+
out = append(out, salt...)
134+
out = append(out, '$')
135+
out = append(out, base64_24Bit([]byte{
136+
C_sum[42], C_sum[21], C_sum[0],
137+
C_sum[1], C_sum[43], C_sum[22],
138+
C_sum[23], C_sum[2], C_sum[44],
139+
C_sum[45], C_sum[24], C_sum[3],
140+
C_sum[4], C_sum[46], C_sum[25],
141+
C_sum[26], C_sum[5], C_sum[47],
142+
C_sum[48], C_sum[27], C_sum[6],
143+
C_sum[7], C_sum[49], C_sum[28],
144+
C_sum[29], C_sum[8], C_sum[50],
145+
C_sum[51], C_sum[30], C_sum[9],
146+
C_sum[10], C_sum[52], C_sum[31],
147+
C_sum[32], C_sum[11], C_sum[53],
148+
C_sum[54], C_sum[33], C_sum[12],
149+
C_sum[13], C_sum[55], C_sum[34],
150+
C_sum[35], C_sum[14], C_sum[56],
151+
C_sum[57], C_sum[36], C_sum[15],
152+
C_sum[16], C_sum[58], C_sum[37],
153+
C_sum[38], C_sum[17], C_sum[59],
154+
C_sum[60], C_sum[39], C_sum[18],
155+
C_sum[19], C_sum[61], C_sum[40],
156+
C_sum[41], C_sum[20], C_sum[62],
157+
C_sum[63],
158+
})...)
159+
160+
A.Reset()
161+
Alternate.Reset()
162+
P.Reset()
163+
for i = 0; i < len(A_sum); i++ {
164+
A_sum[i] = 0
165+
}
166+
for i = 0; i < len(AlternateSum); i++ {
167+
AlternateSum[i] = 0
168+
}
169+
for i = 0; i < len(P_seq); i++ {
170+
P_seq[i] = 0
171+
}
172+
173+
return string(out), nil
174+
}
175+
176+
func generateWRounds() []byte {
177+
salt := make([]byte, 16)
178+
_, _ = rand.Read(salt)
179+
180+
magicPrefix := "$6$"
181+
out := make([]byte, len(magicPrefix)+5000)
182+
copy(out, magicPrefix)
183+
copy(out[len(magicPrefix):], base64_24Bit(salt))
184+
return out
185+
}
186+
187+
func base64_24Bit(src []byte) (hash []byte) {
188+
if len(src) == 0 {
189+
return []byte{}
190+
}
191+
alphabet := "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
192+
193+
hashSize := (len(src) * 8) / 6
194+
if (len(src) % 6) != 0 {
195+
hashSize += 1
196+
}
197+
hash = make([]byte, hashSize)
198+
199+
dst := hash
200+
for len(src) > 0 {
201+
switch len(src) {
202+
default:
203+
dst[0] = alphabet[src[0]&0x3f]
204+
dst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]
205+
dst[2] = alphabet[((src[1]>>4)|(src[2]<<4))&0x3f]
206+
dst[3] = alphabet[(src[2]>>2)&0x3f]
207+
src = src[3:]
208+
dst = dst[4:]
209+
case 2:
210+
dst[0] = alphabet[src[0]&0x3f]
211+
dst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]
212+
dst[2] = alphabet[(src[1]>>4)&0x3f]
213+
src = src[2:]
214+
dst = dst[3:]
215+
case 1:
216+
dst[0] = alphabet[src[0]&0x3f]
217+
dst[1] = alphabet[(src[0]>>6)&0x3f]
218+
src = src[1:]
219+
dst = dst[2:]
220+
}
221+
}
222+
223+
return
224+
}

0 commit comments

Comments
 (0)