{$MODE OBJFPC} { -*- delphi -*- } {$INCLUDE settings.inc} unit logindynasty; interface uses sysutils, passwords, hashsettight; type TDynastyRecord = packed record public const MaxUsernameLength = 127; // plus one byte for the length var Username: String[MaxUsernameLength]; Salt: TSalt; PasswordHash: THash; DynastyServer: Cardinal; CurrentScore, TopScore: Double; end; TDynasty = class protected FID: Cardinal; FUsername: UTF8String; FSalt: TSalt; FPasswordHash: THash; FDynastyServer: Cardinal; FCurrentScore, FTopScore: Double; public constructor Create(AID: Cardinal; AUsername: UTF8String; APassword: UTF8String; ADynastyServer: Cardinal); constructor CreateFromRecord(AID: Cardinal; DynastyRecord: TDynastyRecord); function ToRecord(): TDynastyRecord; procedure UpdateUsername(NewUsername: UTF8String); procedure UpdatePassword(NewPassword: UTF8String); function VerifyPassword(Candidate: UTF8String): Boolean; procedure UpdateScore(Score: Double); property ID: Cardinal read FID; property Username: UTF8String read FUsername; property ServerID: Cardinal read FDynastyServer; property CurrentScore: Double read FCurrentScore; property TopScore: Double read FTopScore; end; TDynastyIDHashSet = specialize TTightHashSet; implementation function RawToString(var Source; Length: Cardinal): UTF8String; begin Result := ''; SetLength(Result, Length); Move(Source, Result[1], Length); end; function RawToBytes(var Source; Length: Cardinal): TBytes; begin Result := nil; SetLength(Result, Length); Move(Source, Result[0], Length); end; constructor TDynasty.Create(AID: Cardinal; AUsername: UTF8String; APassword: UTF8String; ADynastyServer: Cardinal); begin inherited Create(); FID := AID; FUsername := AUsername; FDynastyServer := ADynastyServer; UpdatePassword(APassword); end; constructor TDynasty.CreateFromRecord(AID: Cardinal; DynastyRecord: TDynastyRecord); begin inherited Create(); FID := AID; FUsername := DynastyRecord.Username; FSalt := DynastyRecord.Salt; FPasswordHash := DynastyRecord.PasswordHash; FDynastyServer := DynastyRecord.DynastyServer; FCurrentScore := DynastyRecord.CurrentScore; FTopScore := DynastyRecord.TopScore; end; function TDynasty.ToRecord(): TDynastyRecord; begin FillChar(Result.Username, High(Result.Username), 0); // {BOGUS Hint: Function result variable does not seem to be initialized} Result.Username := FUsername; Move(FSalt[0], Result.Salt[0], Length(Result.Salt)); Move(FPasswordHash[0], Result.PasswordHash[0], Length(Result.PasswordHash)); Result.DynastyServer := FDynastyServer; Result.CurrentScore := CurrentScore; Result.TopScore := TopScore; end; procedure TDynasty.UpdateUsername(NewUsername: UTF8String); begin FUsername := NewUsername; end; procedure TDynasty.UpdatePassword(NewPassword: UTF8String); var NewSalt: TSalt; HashedPassword: THash; begin NewSalt := CreateSalt(); ComputeHash(NewSalt, NewPassword, HashedPassword); FSalt := NewSalt; FPasswordHash := HashedPassword; end; function TDynasty.VerifyPassword(Candidate: UTF8String): Boolean; var HashedPassword: THash; begin ComputeHash(FSalt, Candidate, HashedPassword); Assert(Length(HashedPassword) = Length(FPasswordHash)); Assert(Length(HashedPassword) > 0); Result := CompareHashes(HashedPassword, FPasswordHash); end; procedure TDynasty.UpdateScore(Score: Double); begin if (FTopScore < Score) then FTopScore := Score; FCurrentScore := Score; end; end.