From 29cb094430acea7676d096f1dd54a01a9232815d Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 24 Nov 2025 12:11:46 +0200 Subject: [PATCH] Optimize generating totp codes (#3512) --- .../Steam/Security/MobileAuthenticator.cs | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs index 5df32ad75..20d70abb9 100644 --- a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs +++ b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs @@ -23,6 +23,7 @@ using System; using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -101,53 +102,33 @@ public sealed class MobileAuthenticator : IDisposable { throw new InvalidOperationException(nameof(SharedSecret)); } - byte[] sharedSecret; + Span sharedSecret = stackalloc byte[32]; - try { - sharedSecret = Convert.FromBase64String(SharedSecret); - } catch (FormatException e) { - Bot.ArchiLogger.LogGenericException(e); + if (!Convert.TryFromBase64String(SharedSecret, sharedSecret, out int bytesWritten)) { Bot.ArchiLogger.LogGenericError(Strings.FormatErrorIsInvalid(nameof(SharedSecret))); return null; } - byte[] timeArray = BitConverter.GetBytes(time / CodeInterval); + sharedSecret = sharedSecret[..bytesWritten]; - if (BitConverter.IsLittleEndian) { - Array.Reverse(timeArray); - } + Span timeArray = stackalloc byte[sizeof(long)]; + BinaryPrimitives.WriteInt64BigEndian(timeArray, (long) (time / CodeInterval)); + + Span hash = stackalloc byte[HMACSHA1.HashSizeInBytes]; #pragma warning disable CA5350 // This is actually a fair warning, but there is nothing we can do about Steam using weak cryptographic algorithms - byte[] hash = HMACSHA1.HashData(sharedSecret, timeArray); + _ = HMACSHA1.HashData(sharedSecret, timeArray, hash); #pragma warning restore CA5350 // This is actually a fair warning, but there is nothing we can do about Steam using weak cryptographic algorithms // The last 4 bits of the mac say where the code starts int start = hash[^1] & 0x0f; - uint fullCode; - // Extract those 4 bytes - byte[] bytes = ArrayPool.Shared.Rent(4); + Span bytes = hash[start..(start + 4)]; - try { - Array.Copy(hash, start, bytes, 0, 4); - - Span span; - - if (BitConverter.IsLittleEndian) { - Array.Reverse(bytes); - - span = bytes.AsSpan()[^4..]; - } else { - span = bytes.AsSpan()[..4]; - } - - // Build the alphanumeric code - fullCode = BitConverter.ToUInt32(span) & 0x7fffffff; - } finally { - ArrayPool.Shared.Return(bytes); - } + // Build the alphanumeric code + uint fullCode = BinaryPrimitives.ReadUInt32BigEndian(bytes) & 0x7fffffff; return string.Create( CodeDigits, fullCode, static (buffer, state) => {