Rewrite Steam time to ulongs (#2594)

My latest "research" resulted in learning that under the hood, Steam unix time seconds is bullet-proof not only for year 2038 but for uint range as well. While I do not expect to be alive by 2106, let alone ASF still being operative, it makes sense to base our time on the correct backend implementation regardless.

Small breaking change for people using `GetUnixTime()`.
This commit is contained in:
Łukasz Domeradzki
2022-06-01 21:13:50 +02:00
committed by GitHub
parent 715ed034df
commit 7fe5989f5d
3 changed files with 31 additions and 19 deletions

View File

@@ -94,7 +94,7 @@ public static class Utilities {
}
[PublicAPI]
public static uint GetUnixTime() => (uint) DateTimeOffset.UtcNow.ToUnixTimeSeconds();
public static ulong GetUnixTime() => (ulong) DateTimeOffset.UtcNow.ToUnixTimeSeconds();
[PublicAPI]
public static async void InBackground(Action action, bool longRunning = false) {
@@ -253,6 +253,16 @@ public static class Utilities {
}
}
internal static ulong MathAdd(ulong first, int second) {
if (second >= 0) {
first += (uint) second;
} else {
first -= (uint) -second;
}
return first;
}
internal static bool RelativeDirectoryStartsWith(string directory, params string[] prefixes) {
if (string.IsNullOrEmpty(directory)) {
throw new ArgumentNullException(nameof(directory));

View File

@@ -1678,7 +1678,7 @@ public sealed class ArchiWebHandler : IDisposable {
return result;
}
internal async Task<IDocument?> GetConfirmationsPage(string deviceID, string confirmationHash, uint time) {
internal async Task<IDocument?> GetConfirmationsPage(string deviceID, string confirmationHash, ulong time) {
if (string.IsNullOrEmpty(deviceID)) {
throw new ArgumentNullException(nameof(deviceID));
}
@@ -1803,7 +1803,7 @@ public sealed class ArchiWebHandler : IDisposable {
return response?.Content;
}
internal async Task<uint> GetServerTime() {
internal async Task<ulong> GetServerTime() {
KeyValue? response = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
@@ -1835,7 +1835,7 @@ public sealed class ArchiWebHandler : IDisposable {
return 0;
}
uint result = response["server_time"].AsUnsignedInteger();
ulong result = response["server_time"].AsUnsignedLong();
if (result == 0) {
Bot.ArchiLogger.LogNullError(result);
@@ -1966,7 +1966,7 @@ public sealed class ArchiWebHandler : IDisposable {
return resultInSeconds == 0 ? (byte) 0 : (byte) (resultInSeconds / 86400);
}
internal async Task<bool?> HandleConfirmation(string deviceID, string confirmationHash, uint time, ulong confirmationID, ulong confirmationKey, bool accept) {
internal async Task<bool?> HandleConfirmation(string deviceID, string confirmationHash, ulong time, ulong confirmationID, ulong confirmationKey, bool accept) {
if (string.IsNullOrEmpty(deviceID)) {
throw new ArgumentNullException(nameof(deviceID));
}
@@ -2008,7 +2008,7 @@ public sealed class ArchiWebHandler : IDisposable {
return response?.Content?.Success;
}
internal async Task<bool?> HandleConfirmations(string deviceID, string confirmationHash, uint time, IReadOnlyCollection<Confirmation> confirmations, bool accept) {
internal async Task<bool?> HandleConfirmations(string deviceID, string confirmationHash, ulong time, IReadOnlyCollection<Confirmation> confirmations, bool accept) {
if (string.IsNullOrEmpty(deviceID)) {
throw new ArgumentNullException(nameof(deviceID));
}

View File

@@ -74,7 +74,7 @@ public sealed class MobileAuthenticator : IDisposable {
throw new InvalidOperationException(nameof(Bot));
}
uint time = await GetSteamTime().ConfigureAwait(false);
ulong time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
throw new InvalidOperationException(nameof(time));
@@ -96,7 +96,7 @@ public sealed class MobileAuthenticator : IDisposable {
return null;
}
uint time = await GetSteamTime().ConfigureAwait(false);
ulong time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
throw new InvalidOperationException(nameof(time));
@@ -211,7 +211,7 @@ public sealed class MobileAuthenticator : IDisposable {
return false;
}
uint time = await GetSteamTime().ConfigureAwait(false);
ulong time = await GetSteamTime().ConfigureAwait(false);
if (time == 0) {
throw new InvalidOperationException(nameof(time));
@@ -281,7 +281,7 @@ public sealed class MobileAuthenticator : IDisposable {
}
}
private string? GenerateConfirmationHash(uint time, string? tag = null) {
private string? GenerateConfirmationHash(ulong time, string? tag = null) {
if (time == 0) {
throw new ArgumentOutOfRangeException(nameof(time));
}
@@ -312,7 +312,7 @@ public sealed class MobileAuthenticator : IDisposable {
bufferSize += (byte) Math.Min(32, tag!.Length);
}
byte[] timeArray = BitConverter.GetBytes((ulong) time);
byte[] timeArray = BitConverter.GetBytes(time);
if (BitConverter.IsLittleEndian) {
Array.Reverse(timeArray);
@@ -334,7 +334,7 @@ public sealed class MobileAuthenticator : IDisposable {
return Convert.ToBase64String(hash);
}
private string? GenerateTokenForTime(uint time) {
private string? GenerateTokenForTime(ulong time) {
if (time == 0) {
throw new ArgumentOutOfRangeException(nameof(time));
}
@@ -358,7 +358,7 @@ public sealed class MobileAuthenticator : IDisposable {
return null;
}
byte[] timeArray = BitConverter.GetBytes((ulong) (time / CodeInterval));
byte[] timeArray = BitConverter.GetBytes(time / CodeInterval);
if (BitConverter.IsLittleEndian) {
Array.Reverse(timeArray);
@@ -394,7 +394,7 @@ public sealed class MobileAuthenticator : IDisposable {
);
}
private async Task<uint> GetSteamTime() {
private async Task<ulong> GetSteamTime() {
if (Bot == null) {
throw new InvalidOperationException(nameof(Bot));
}
@@ -402,7 +402,7 @@ public sealed class MobileAuthenticator : IDisposable {
int? steamTimeDifference = SteamTimeDifference;
if (steamTimeDifference.HasValue && (DateTime.UtcNow.Subtract(LastSteamTimeCheck).TotalHours < SteamTimeTTL)) {
return (uint) (Utilities.GetUnixTime() + steamTimeDifference.Value);
return Utilities.MathAdd(Utilities.GetUnixTime(), steamTimeDifference.Value);
}
await TimeSemaphore.WaitAsync().ConfigureAwait(false);
@@ -411,19 +411,21 @@ public sealed class MobileAuthenticator : IDisposable {
steamTimeDifference = SteamTimeDifference;
if (steamTimeDifference.HasValue && (DateTime.UtcNow.Subtract(LastSteamTimeCheck).TotalHours < SteamTimeTTL)) {
return (uint) (Utilities.GetUnixTime() + steamTimeDifference.Value);
return Utilities.MathAdd(Utilities.GetUnixTime(), steamTimeDifference.Value);
}
uint serverTime = await Bot.ArchiWebHandler.GetServerTime().ConfigureAwait(false);
ulong serverTime = await Bot.ArchiWebHandler.GetServerTime().ConfigureAwait(false);
if (serverTime == 0) {
return Utilities.GetUnixTime();
}
SteamTimeDifference = (int) (serverTime - Utilities.GetUnixTime());
// We assume that the difference between times will be within int range, therefore we accept underflow here (for subtraction), and since we cast that result to int afterwards, we also accept overflow for the cast itself
SteamTimeDifference = unchecked((int) (serverTime - Utilities.GetUnixTime()));
LastSteamTimeCheck = DateTime.UtcNow;
return (uint) (Utilities.GetUnixTime() + SteamTimeDifference.Value);
return Utilities.MathAdd(Utilities.GetUnixTime(), SteamTimeDifference.Value);
} finally {
TimeSemaphore.Release();
}