diff --git a/internal/db/user.go b/internal/db/user.go index d1de281..54dcf1b 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -69,11 +69,21 @@ func Authenticate(db *gorm.DB, username string, password string) (*User, error) const ( pwdSaltSize = 16 pwdHashSize = 32 - pwdParams = "m=65536,t=2,p=1" + + // chosen from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + pwdMemory = 64 * 1024 + pwdThreads = 1 + pwdIterations = 2 + pwdParams = "m=65536,t=2,p=1,v=19" + pwdVersion = "v=19" pwdAlgo = "argon2id" ) func HashPassword(password string) (string, error) { + if argon2.Version != 19 { + // go has no static asserts + panic("Unexpected argon2 version") + } // Generate a salt for the password hash salt := make([]byte, pwdSaltSize) if _, err := rand.Read(salt); err != nil { @@ -81,22 +91,22 @@ func HashPassword(password string) (string, error) { } // Hash the password using argon2id - hash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, pwdHashSize) + hash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize) // 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 + return fmt.Sprintf("$%s$%s$%s$%s$%s", pwdAlgo, pwdVersion, pwdParams, encodedSalt, encodedHash), nil } var errInvalidDBPasswordFormat = errors.New("invalid password format in db") func comparePassword(hashedPassword string, password string) (bool, error) { // Extract the salt and hash from the hashed password string fields := strings.Split(hashedPassword, "$") - if len(fields) != 5 || fields[1] != pwdAlgo || fields[2] != pwdParams { + if len(fields) != 6 || fields[1] != pwdAlgo || fields[2] != pwdVersion || fields[3] != pwdParams { return false, errInvalidDBPasswordFormat } - encodedSalt, encodedHash := fields[3], fields[4] + encodedSalt, encodedHash := fields[4], fields[5] // Decode the salt and hash from base64 salt, err := base64.RawStdEncoding.DecodeString(encodedSalt) @@ -109,7 +119,7 @@ func comparePassword(hashedPassword string, password string) (bool, error) { } // Hash the password using the extracted salt and parameters - computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, pwdHashSize) + computedHash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize) // Compare the computed hash with the stored hash return subtle.ConstantTimeCompare(hash, computedHash) == 1, nil