Use PHC hashes for password storage
This commit is contained in:
		
							parent
							
								
									788e75c4c1
								
							
						
					
					
						commit
						8e949f837b
					
				@ -338,6 +338,9 @@ func (rl *rushlink) authenticateUser(w http.ResponseWriter, r *http.Request, sho
 | 
			
		||||
		// Authentication failed, return a 401 Unauthorized response
 | 
			
		||||
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
 | 
			
		||||
		w.WriteHeader(http.StatusUnauthorized)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("authentication failure: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return user
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
@ -54,37 +55,64 @@ func Authenticate(db *gorm.DB, username string, password string) (*User, error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Compare the hashed password with the provided password
 | 
			
		||||
	if !comparePassword(user.Password, password) {
 | 
			
		||||
	valid, err := comparePassword(user.Password, password)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !valid {
 | 
			
		||||
		return nil, errors.New("invalid password")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &user, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	pwdSaltSize = 16
 | 
			
		||||
	pwdHashSize = 32
 | 
			
		||||
	pwdParams   = "m=65536,t=2,p=1"
 | 
			
		||||
	pwdAlgo     = "argon2id"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func HashPassword(password string) (string, error) {
 | 
			
		||||
	// Generate a salt for the password hash
 | 
			
		||||
	salt := make([]byte, 16)
 | 
			
		||||
	salt := make([]byte, pwdSaltSize)
 | 
			
		||||
	if _, err := rand.Read(salt); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the password using argon2id
 | 
			
		||||
	hash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64)
 | 
			
		||||
	hash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, pwdHashSize)
 | 
			
		||||
 | 
			
		||||
	// Encode the salt and hash as a string
 | 
			
		||||
	return string(salt) + string(hash), nil
 | 
			
		||||
	// Encode the salt and hash as a string in PHC format
 | 
			
		||||
	encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
 | 
			
		||||
	encodedHash := base64.RawStdEncoding.EncodeToString(hash)
 | 
			
		||||
	return fmt.Sprintf("$%s$%s$%s$%s", pwdAlgo, pwdParams, encodedSalt, encodedHash), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func comparePassword(hashedPassword string, password string) bool {
 | 
			
		||||
	// Decode the salt and hash from the hashed password string
 | 
			
		||||
	salt := []byte(hashedPassword)[:16]
 | 
			
		||||
	hash := []byte(hashedPassword)[16:]
 | 
			
		||||
func comparePassword(hashedPassword string, password string) (bool, error) {
 | 
			
		||||
	// Extract the salt and hash from the hashed password string
 | 
			
		||||
	fields := strings.Split(hashedPassword, "$")[1:]
 | 
			
		||||
	if len(fields) != 4 || fields[0] != pwdAlgo || fields[1] != pwdParams {
 | 
			
		||||
		return false, errors.New("invalid password format in db")
 | 
			
		||||
	}
 | 
			
		||||
	encodedSalt, encodedHash := fields[2], fields[3]
 | 
			
		||||
 | 
			
		||||
	// Hash the password using the same salt and parameters
 | 
			
		||||
	computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64)
 | 
			
		||||
	// Decode the salt and hash from base64
 | 
			
		||||
	salt, err := base64.RawStdEncoding.DecodeString(encodedSalt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	hash, err := base64.RawStdEncoding.DecodeString(encodedHash)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hash the password using the extracted salt and parameters
 | 
			
		||||
	computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, pwdHashSize)
 | 
			
		||||
 | 
			
		||||
	// Compare the computed hash with the stored hash
 | 
			
		||||
	return bytes.Equal(hash, computedHash)
 | 
			
		||||
	// todo constant time?
 | 
			
		||||
	return bytes.Equal(hash, computedHash), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteUser deletes a user with the specified username from the database.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user