99 "crypto/sha1"
1010 "crypto/sha256"
1111 "crypto/sha512"
12+ "crypto/subtle"
1213 "encoding/binary"
14+ "encoding/hex"
1315 "fmt"
1416 "io"
1517 mrand "math/rand"
@@ -29,7 +31,7 @@ func Pstack() string {
2931 return string (buf [0 :n ])
3032}
3133
32- func CalcPassword (scramble , password []byte ) []byte {
34+ func CalcNativePassword (scramble , password []byte ) []byte {
3335 if len (password ) == 0 {
3436 return nil
3537 }
@@ -39,35 +41,100 @@ func CalcPassword(scramble, password []byte) []byte {
3941 crypt .Write (password )
4042 stage1 := crypt .Sum (nil )
4143
42- // scrambleHash = SHA1(scramble + SHA1(stage1Hash))
43- // inner Hash
44+ // stage2Hash = SHA1(stage1Hash)
4445 crypt .Reset ()
4546 crypt .Write (stage1 )
46- hash := crypt .Sum (nil )
47+ stage2 := crypt .Sum (nil )
4748
48- // outer Hash
49+ // scrambleHash = SHA1(scramble + stage2Hash)
4950 crypt .Reset ()
5051 crypt .Write (scramble )
51- crypt .Write (hash )
52- scramble = crypt .Sum (nil )
52+ crypt .Write (stage2 )
53+ scrambleHash : = crypt .Sum (nil )
5354
5455 // token = scrambleHash XOR stage1Hash
55- for i := range scramble {
56- scramble [i ] ^= stage1 [i ]
56+ return Xor (scrambleHash , stage1 )
57+ }
58+
59+ func Xor (hash1 []byte , hash2 []byte ) []byte {
60+ for i := range hash1 {
61+ hash1 [i ] ^= hash2 [i ]
5762 }
58- return scramble
63+ return hash1
64+ }
65+
66+ // hash_stage1 = xor(reply, sha1(public_seed, hash_stage2))
67+ func Stage1FromReply (scramble []byte , seed []byte , stage2 []byte ) []byte {
68+ crypt := sha1 .New ()
69+ crypt .Write (seed )
70+ crypt .Write (stage2 )
71+ seededHash := crypt .Sum (nil )
72+
73+ return Xor (scramble , seededHash )
74+ }
75+
76+ // FROM vitess.io/vitess/go/mysql/auth_server.go
77+ // DecodePasswordHex decodes the standard format used by MySQL
78+ // for 4.1 style password hashes. It drops the optionally leading * before
79+ // decoding the rest as a hex encoded string.
80+ func DecodePasswordHex (hexEncodedPassword string ) ([]byte , error ) {
81+ if hexEncodedPassword [0 ] == '*' {
82+ hexEncodedPassword = hexEncodedPassword [1 :]
83+ }
84+ return hex .DecodeString (hexEncodedPassword )
85+ }
86+
87+ // EncodePasswordHex encodes to the standard format used by MySQL
88+ // adds the optionally leading * to the hashed password
89+ func EncodePasswordHex (passwordHash []byte ) string {
90+ hexstr := strings .ToUpper (hex .EncodeToString (passwordHash ))
91+ return "*" + hexstr
92+ }
93+
94+ // NativePasswordHash = sha1(sha1(password))
95+ func NativePasswordHash (password []byte ) []byte {
96+ if len (password ) == 0 {
97+ return nil
98+ }
99+
100+ // stage1Hash = SHA1(password)
101+ crypt := sha1 .New ()
102+ crypt .Write (password )
103+ stage1 := crypt .Sum (nil )
104+
105+ // stage2Hash = SHA1(stage1Hash)
106+ crypt .Reset ()
107+ crypt .Write (stage1 )
108+ return crypt .Sum (nil )
109+ }
110+
111+ func CompareNativePassword (reply []byte , stored []byte , seed []byte ) bool {
112+ if len (stored ) == 0 {
113+ return false
114+ }
115+
116+ // hash_stage1 = xor(reply, sha1(public_seed, hash_stage2))
117+ stage1 := Stage1FromReply (reply , seed , stored )
118+ // andidate_hash2 = sha1(hash_stage1)
119+ crypt := sha1 .New ()
120+ crypt .Write (stage1 )
121+ stage2 := crypt .Sum (nil )
122+
123+ // check(candidate_hash2 == hash_stage2)
124+ // use ConstantTimeCompare to mitigate timing based attacks
125+ return subtle .ConstantTimeCompare (stage2 , stored ) == 1
59126}
60127
61128// CalcCachingSha2Password: Hash password using MySQL 8+ method (SHA256)
62- func CalcCachingSha2Password (scramble []byte , password string ) []byte {
129+ func CalcCachingSha2Password (scramble []byte , password [] byte ) []byte {
63130 if len (password ) == 0 {
64131 return nil
65132 }
66133
67134 // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
68135
69136 crypt := sha256 .New ()
70- crypt .Write ([] byte ( password ) )
137+ crypt .Write (password )
71138 message1 := crypt .Sum (nil )
72139
73140 crypt .Reset ()
@@ -135,6 +202,91 @@ func EncryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte,
135202 return rsa .EncryptOAEP (sha1v , rand .Reader , pub , plain , nil )
136203}
137204
205+ const (
206+ SALT_LENGTH = 16
207+ ITERATION_MULTIPLIER = 1000
208+ SHA256_PASSWORD_ITERATIONS = 5
209+ )
210+
211+ // generateUserSalt generate salt of given length for sha256_password hash
212+ func generateUserSalt (length int ) ([]byte , error ) {
213+ // Generate a random salt of the given length
214+ // Implement this function for your project
215+ salt := make ([]byte , length )
216+ _ , err := rand .Read (salt )
217+ if err != nil {
218+ return []byte ("" ), err
219+ }
220+
221+ // Restrict to 7-bit to avoid multi-byte UTF-8
222+ for i := range salt {
223+ salt [i ] = salt [i ] &^ 128
224+ for salt [i ] == 36 || salt [i ] == 0 { // '$' or NUL
225+ newval := make ([]byte , 1 )
226+ _ , err := rand .Read (newval )
227+ if err != nil {
228+ return []byte ("" ), err
229+ }
230+ salt [i ] = newval [0 ] &^ 128
231+ }
232+ }
233+ return salt , nil
234+ }
235+
236+ // hashCrypt256 salt and hash a password the given number of iterations
237+ func hashCrypt256 (source , salt string , iterations uint64 ) (string , error ) {
238+ actualIterations := iterations * ITERATION_MULTIPLIER
239+ hashInput := []byte (source + salt )
240+ var hash [32 ]byte
241+ for i := uint64 (0 ); i < actualIterations ; i ++ {
242+ h := sha256 .New ()
243+ h .Write (hashInput )
244+ hash = sha256 .Sum256 (h .Sum (nil ))
245+ hashInput = hash [:]
246+ }
247+
248+ hashHex := hex .EncodeToString (hash [:])
249+ digest := fmt .Sprintf ("$%d$%s$%s" , iterations , salt , hashHex )
250+ return digest , nil
251+ }
252+
253+ // Check256HashingPassword compares a password to a hash for sha256_password
254+ // rather than trying to recreate just the hash we recreate the full hash
255+ // and use that for comparison
256+ func Check256HashingPassword (pwhash []byte , password string ) (bool , error ) {
257+ pwHashParts := bytes .Split (pwhash , []byte ("$" ))
258+ if len (pwHashParts ) != 4 {
259+ return false , errors .New ("failed to decode hash parts" )
260+ }
261+
262+ iterationsPart := pwHashParts [1 ]
263+ if len (iterationsPart ) == 0 {
264+ return false , errors .New ("iterations part is empty" )
265+ }
266+
267+ iterations , err := strconv .ParseUint (string (iterationsPart ), 10 , 64 )
268+ if err != nil {
269+ return false , errors .New ("failed to decode iterations" )
270+ }
271+ salt := pwHashParts [2 ][:SALT_LENGTH ]
272+
273+ newHash , err := hashCrypt256 (password , string (salt ), iterations )
274+ if err != nil {
275+ return false , err
276+ }
277+
278+ return bytes .Equal (pwhash , []byte (newHash )), nil
279+ }
280+
281+ // NewSha256PasswordHash creates a new password hash for sha256_password
282+ func NewSha256PasswordHash (pwd string ) (string , error ) {
283+ salt , err := generateUserSalt (SALT_LENGTH )
284+ if err != nil {
285+ return "" , err
286+ }
287+ return hashCrypt256 (pwd , string (salt ), SHA256_PASSWORD_ITERATIONS )
288+ }
289+
138290func DecompressMariadbData (data []byte ) ([]byte , error ) {
139291 // algorithm always 0=zlib
140292 // algorithm := (data[pos] & 0x07) >> 4
0 commit comments