// Checks if the password matches the hash // // If the cost of the given hash is below the cost we currently use, the 2nd return value will contain a new and stronger hash. // If the 2nd return value is present, you must update the hash for the password to it or you're missing out on the security benefits and wasting CPU cycles. // If the given hash is already strong enough, the 2nd argument will be nil. func (self *Hasher) Validate(password []byte, hash []byte) (bool, []byte, error) { err := bcrypt.CompareHashAndPassword(hash, password) if err != nil { // password and hash do not match return false, nil, nil } else { // password matches the hash costOfHash, err := bcrypt.Cost(hash) if err != nil || costOfHash < self.currentCost { // if unable to determine the cost (err != nil), treat it the same as an outdated hash newHash, err := self.Hash(password) if err != nil { return true, nil, err } else { return true, newHash, nil } } else { // the hash is valid and is sufficiently strong return true, nil, nil } } }
func (c *config) IsCurrent(encoded []byte) (isCurrent bool, err error) { cost, err := bcrypt.Cost(encoded) if err == nil { isCurrent = cost >= c.Cost } return }
// checkPass verifies that the given user exists and that the password matches. func checkPass(username, pass string, ip net.Addr) (user *world.User, err error) { if world.FindPlayer(username) != nil { return nil, ErrDupe } hash, err := db.Password(username) if err != nil { return nil, err } passb := []byte(pass) if hash == nil { // User does not exist. Fake out the time we would otherwise take to run // the hash. Ignore the error, we really only care about sucking up // some CPU cycles here. _ = bcrypt.CompareHashAndPassword(fakehash, passb) return nil, ErrAuth } err = bcrypt.CompareHashAndPassword(hash, passb) if err == bcrypt.ErrMismatchedHashAndPassword { return nil, ErrAuth } if err != nil { return nil, err } cost, err := bcrypt.Cost(hash) if err != nil { return nil, err } // Handle bcrypt cost change, rehash with new cost. if cost != bcryptCost { hash, err = bcrypt.GenerateFromPassword(passb, bcryptCost) if err != nil { return nil, err } } // Login successful, update info. if err := db.SaveUser(username, ip, hash); err != nil { return nil, err } return &world.User{Username: username, IP: ip}, nil }
// Login returns a non-nil player and an empty error string OR a nil player // and an error to show to the user. func Login(addr string, packet *LoginPacket) (*Player, string) { login := strings.TrimSpace(packet.Login) if login == "" { return nil, "A username is required." } pass := []byte(packet.Pass) if len(pass) <= 2 { return nil, "A password is required." } filename := loginToFilename(login) loginLock.Lock() defer loginLock.Unlock() loginAttempts[addr]++ if loginAttempts[addr] == 5 { loginAttempts[addr] += 60 } if loginAttempts[addr] > 5 { return nil, fmt.Sprintf("Too many login attempts. Come back in %d minutes.", loginAttempts[addr]-5) } f, err := os.Open(filename) if err != nil { hashedPass, err := bcrypt.GenerateFromPassword(pass, CryptoCost) if err != nil { panic(err) } p := &Player{ Hero: *GenerateHero(rand.New(rand.NewSource(rand.Int63()))), characterCreation: true, tileX: 127, tileY: 127, login: login, password: hashedPass, firstAddr: addr, registered: time.Now().UTC(), lastAddr: addr, lastLogin: time.Now().UTC(), } world.InitObject(p) for _, e := range p.Hero.equipped { e.wearer = p } savePlayer(p) onlinePlayers[login] = p return p, "" } defer f.Close() g, err := gzip.NewReader(f) if err != nil { panic(err) } defer g.Close() var data interface{} err = gob.NewDecoder(g).Decode(&data) if err != nil { panic(err) } p := world.LoadConvert(data).(*Player) if bcrypt.CompareHashAndPassword(p.password, pass) != nil { return nil, "Username or password incorrect." } loginAttempts[addr]-- if other, ok := onlinePlayers[p.login]; ok { savePlayer(other) other.Kick("Logged in from a different location.") _, err = f.Seek(0, 0) if err != nil { panic(err) } g, err = gzip.NewReader(f) if err != nil { panic(err) } defer g.Close() data = nil err = gob.NewDecoder(g).Decode(&data) if err != nil { panic(err) } p = world.LoadConvert(data).(*Player) } cost, err := bcrypt.Cost(p.password) if err != nil { panic(err) } if cost != CryptoCost { p.password, err = bcrypt.GenerateFromPassword(pass, CryptoCost) if err != nil { panic(err) } savePlayer(p) } onlinePlayers[p.login] = p return p, "" }