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
 | 
							// Authentication failed, return a 401 Unauthorized response
 | 
				
			||||||
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
 | 
							w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
 | 
				
			||||||
		w.WriteHeader(http.StatusUnauthorized)
 | 
							w.WriteHeader(http.StatusUnauthorized)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Printf("authentication failure: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return user
 | 
						return user
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/google/uuid"
 | 
						"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
 | 
						// 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 nil, errors.New("invalid password")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &user, nil
 | 
						return &user, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						pwdSaltSize = 16
 | 
				
			||||||
 | 
						pwdHashSize = 32
 | 
				
			||||||
 | 
						pwdParams   = "m=65536,t=2,p=1"
 | 
				
			||||||
 | 
						pwdAlgo     = "argon2id"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func HashPassword(password string) (string, error) {
 | 
					func HashPassword(password string) (string, error) {
 | 
				
			||||||
	// Generate a salt for the password hash
 | 
						// Generate a salt for the password hash
 | 
				
			||||||
	salt := make([]byte, 16)
 | 
						salt := make([]byte, pwdSaltSize)
 | 
				
			||||||
	if _, err := rand.Read(salt); err != nil {
 | 
						if _, err := rand.Read(salt); err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Hash the password using argon2id
 | 
						// 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
 | 
						// Encode the salt and hash as a string in PHC format
 | 
				
			||||||
	return string(salt) + string(hash), nil
 | 
						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 {
 | 
					func comparePassword(hashedPassword string, password string) (bool, error) {
 | 
				
			||||||
	// Decode the salt and hash from the hashed password string
 | 
						// Extract the salt and hash from the hashed password string
 | 
				
			||||||
	salt := []byte(hashedPassword)[:16]
 | 
						fields := strings.Split(hashedPassword, "$")[1:]
 | 
				
			||||||
	hash := []byte(hashedPassword)[16:]
 | 
						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
 | 
						// Decode the salt and hash from base64
 | 
				
			||||||
	computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64)
 | 
						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
 | 
						// 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.
 | 
					// DeleteUser deletes a user with the specified username from the database.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user