38 lines
1.3 KiB
Go
38 lines
1.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// saltedInput mixes the login into the password material before bcrypt hashing, using
|
|
// the login as a per-user salt: a leaked hash can't be replayed against a different
|
|
// login, and pre-hashing keeps the input short regardless of login/password length
|
|
// (bcrypt only considers the first 72 bytes of its input).
|
|
func saltedInput(login, password string) []byte {
|
|
sum := sha256.Sum256([]byte(login + "\x00" + password))
|
|
return []byte(base64.StdEncoding.EncodeToString(sum[:]))
|
|
}
|
|
|
|
// dummyHash is compared against on unknown logins so that a failed lookup takes
|
|
// roughly the same time as a real password comparison, avoiding user enumeration.
|
|
var dummyHash, _ = bcrypt.GenerateFromPassword(saltedInput("dummy-login", "dummy-password"), bcrypt.DefaultCost)
|
|
|
|
func Hash(login, password string) (string, error) {
|
|
hash, err := bcrypt.GenerateFromPassword(saltedInput(login, password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hash), nil
|
|
}
|
|
|
|
func Compare(login, hash, password string) bool {
|
|
return bcrypt.CompareHashAndPassword([]byte(hash), saltedInput(login, password)) == nil
|
|
}
|
|
|
|
func CompareDummy() {
|
|
_ = bcrypt.CompareHashAndPassword(dummyHash, saltedInput("dummy-login", "dummy-password"))
|
|
}
|