mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Server fixes: - voice_leave: broadcast voice_leave even on DB error so peers don't see ghost users (H1) - migrate: record migration inside transaction for atomicity (H2) - password: use init() with panic for dummyHash to catch bcrypt init failures (M1) - password: replace //nolint:errcheck with explicit _ = discard (M2) - handlers: clarify edit permission comment, fix error message wording (M3) - auth_handler: distinguish duplicate username (400) from DB error (500) (M4) Client fixes: - media: revert YouTube oEmbed to browser fetch — no need to disable cert verification (C1) - messages.store: fix prependMessages cap to keep newest messages, not oldest (H5) - ChannelSidebar: ref-count globalDragAc to prevent multi-instance teardown race (H6) - attachments: replace console.error with project logger (M6) - ws: clarify lastSeq reset comment to match actual behavior (M5)
73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
const (
|
|
bcryptCost = 12
|
|
minPassLen = 8
|
|
maxPassLen = 72 // bcrypt silently truncates beyond 72 bytes
|
|
)
|
|
|
|
// ErrPasswordTooShort is returned when the password is below the minimum length.
|
|
var ErrPasswordTooShort = errors.New("password must be at least 8 characters")
|
|
|
|
// ErrPasswordTooLong is returned when the password exceeds bcrypt's 72-byte limit.
|
|
var ErrPasswordTooLong = errors.New("password must not exceed 72 characters")
|
|
|
|
// HashPassword returns a bcrypt hash of password using cost 12.
|
|
func HashPassword(password string) (string, error) {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hash), nil
|
|
}
|
|
|
|
// dummyHash is a pre-computed bcrypt hash used to prevent timing side-channels
|
|
// when the user does not exist. Comparing against this dummy ensures that
|
|
// CheckPassword takes roughly constant time regardless of whether a valid hash
|
|
// was supplied.
|
|
var dummyHash []byte
|
|
|
|
func init() {
|
|
h, err := bcrypt.GenerateFromPassword([]byte("dummy-timing-pad"), bcryptCost)
|
|
if err != nil {
|
|
panic("auth: failed to generate dummy bcrypt hash: " + err.Error())
|
|
}
|
|
dummyHash = h
|
|
}
|
|
|
|
// CheckPassword reports whether password matches hash. Returns false on any
|
|
// error, including an empty or malformed hash. When hash is empty (user does
|
|
// not exist), a dummy bcrypt comparison is performed to prevent timing-based
|
|
// username enumeration.
|
|
func CheckPassword(hash, password string) bool {
|
|
if hash == "" {
|
|
// Perform a dummy comparison so the response time is indistinguishable
|
|
// from a real check, preventing timing-based username enumeration.
|
|
// The error is intentionally discarded: we always return false here.
|
|
// The comparison is performed only to consume time and prevent
|
|
// timing-based username enumeration.
|
|
_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(password))
|
|
return false
|
|
}
|
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
// ValidatePasswordStrength returns an error if password fails strength
|
|
// requirements: minimum 8 characters, maximum 72 characters.
|
|
func ValidatePasswordStrength(password string) error {
|
|
if len(password) < minPassLen {
|
|
return ErrPasswordTooShort
|
|
}
|
|
if len(password) > maxPassLen {
|
|
return ErrPasswordTooLong
|
|
}
|
|
return nil
|
|
}
|