mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 14:31:02 +00:00 
			
		
		
		
	- In Go 1.21 the crypto/sha256 [got a massive improvement](https://go.dev/doc/go1.21#crypto/sha256) by utilizing the SHA instructions for AMD64 CPUs, which sha256-simd already was doing. The performance is now on par and I think it's preferable to use the standard library rather than a package when possible. ``` cpu: AMD Ryzen 5 3600X 6-Core Processor │ simd.txt │ go.txt │ │ sec/op │ sec/op vs base │ Hash/8Bytes-12 63.25n ± 1% 73.38n ± 1% +16.02% (p=0.002 n=6) Hash/64Bytes-12 98.73n ± 1% 105.30n ± 1% +6.65% (p=0.002 n=6) Hash/1K-12 567.2n ± 1% 572.8n ± 1% +0.99% (p=0.002 n=6) Hash/8K-12 4.062µ ± 1% 4.062µ ± 1% ~ (p=0.396 n=6) Hash/1M-12 512.1µ ± 0% 510.6µ ± 1% ~ (p=0.485 n=6) Hash/5M-12 2.556m ± 1% 2.564m ± 0% ~ (p=0.093 n=6) Hash/10M-12 5.112m ± 0% 5.127m ± 0% ~ (p=0.093 n=6) geomean 13.82µ 14.27µ +3.28% │ simd.txt │ go.txt │ │ B/s │ B/s vs base │ Hash/8Bytes-12 120.6Mi ± 1% 104.0Mi ± 1% -13.81% (p=0.002 n=6) Hash/64Bytes-12 618.2Mi ± 1% 579.8Mi ± 1% -6.22% (p=0.002 n=6) Hash/1K-12 1.682Gi ± 1% 1.665Gi ± 1% -0.98% (p=0.002 n=6) Hash/8K-12 1.878Gi ± 1% 1.878Gi ± 1% ~ (p=0.310 n=6) Hash/1M-12 1.907Gi ± 0% 1.913Gi ± 1% ~ (p=0.485 n=6) Hash/5M-12 1.911Gi ± 1% 1.904Gi ± 0% ~ (p=0.093 n=6) Hash/10M-12 1.910Gi ± 0% 1.905Gi ± 0% ~ (p=0.093 n=6) geomean 1.066Gi 1.032Gi -3.18% ``` (cherry picked from commitabd94ff5b5) (cherry picked from commit15e81637ab) Conflicts: go.mod https://codeberg.org/forgejo/forgejo/pulls/1581 (cherry picked from commit325d92917f) Conflicts: modules/context/context_cookie.go https://codeberg.org/forgejo/forgejo/pulls/1617
		
			
				
	
	
		
			112 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2021 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package v1_14 //nolint
 | |
| 
 | |
| import (
 | |
| 	"crypto/sha256"
 | |
| 	"encoding/hex"
 | |
| 
 | |
| 	"golang.org/x/crypto/argon2"
 | |
| 	"golang.org/x/crypto/bcrypt"
 | |
| 	"golang.org/x/crypto/pbkdf2"
 | |
| 	"golang.org/x/crypto/scrypt"
 | |
| 	"xorm.io/builder"
 | |
| 	"xorm.io/xorm"
 | |
| )
 | |
| 
 | |
| func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) {
 | |
| 	const (
 | |
| 		algoBcrypt = "bcrypt"
 | |
| 		algoScrypt = "scrypt"
 | |
| 		algoArgon2 = "argon2"
 | |
| 		algoPbkdf2 = "pbkdf2"
 | |
| 	)
 | |
| 
 | |
| 	type User struct {
 | |
| 		ID                 int64  `xorm:"pk autoincr"`
 | |
| 		Passwd             string `xorm:"NOT NULL"`
 | |
| 		PasswdHashAlgo     string `xorm:"NOT NULL DEFAULT 'argon2'"`
 | |
| 		MustChangePassword bool   `xorm:"NOT NULL DEFAULT false"`
 | |
| 		LoginType          int
 | |
| 		LoginName          string
 | |
| 		Type               int
 | |
| 		Salt               string `xorm:"VARCHAR(10)"`
 | |
| 	}
 | |
| 
 | |
| 	// hashPassword hash password based on algo and salt
 | |
| 	// state 461406070c
 | |
| 	hashPassword := func(passwd, salt, algo string) string {
 | |
| 		var tempPasswd []byte
 | |
| 
 | |
| 		switch algo {
 | |
| 		case algoBcrypt:
 | |
| 			tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
 | |
| 			return string(tempPasswd)
 | |
| 		case algoScrypt:
 | |
| 			tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50)
 | |
| 		case algoArgon2:
 | |
| 			tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50)
 | |
| 		case algoPbkdf2:
 | |
| 			fallthrough
 | |
| 		default:
 | |
| 			tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
 | |
| 		}
 | |
| 
 | |
| 		return hex.EncodeToString(tempPasswd)
 | |
| 	}
 | |
| 
 | |
| 	// ValidatePassword checks if given password matches the one belongs to the user.
 | |
| 	// state 461406070c, changed since it's not necessary to be time constant
 | |
| 	ValidatePassword := func(u *User, passwd string) bool {
 | |
| 		tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)
 | |
| 
 | |
| 		if u.PasswdHashAlgo != algoBcrypt && u.Passwd == tempHash {
 | |
| 			return true
 | |
| 		}
 | |
| 		if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
 | |
| 			return true
 | |
| 		}
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	sess := x.NewSession()
 | |
| 	defer sess.Close()
 | |
| 
 | |
| 	const batchSize = 100
 | |
| 
 | |
| 	for start := 0; ; start += batchSize {
 | |
| 		users := make([]*User, 0, batchSize)
 | |
| 		if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if len(users) == 0 {
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		if err = sess.Begin(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		for _, user := range users {
 | |
| 			if ValidatePassword(user, "") {
 | |
| 				user.Passwd = ""
 | |
| 				user.Salt = ""
 | |
| 				user.PasswdHashAlgo = ""
 | |
| 				if _, err = sess.ID(user.ID).Cols("passwd", "salt", "passwd_hash_algo").Update(user); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if err = sess.Commit(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// delete salt and algo where password is empty
 | |
| 	_, err = sess.Where(builder.Eq{"passwd": ""}.And(builder.Neq{"salt": ""}.Or(builder.Neq{"passwd_hash_algo": ""}))).
 | |
| 		Cols("salt", "passwd_hash_algo").Update(&User{})
 | |
| 
 | |
| 	return err
 | |
| }
 |