diff --git a/src/App/Models/OtpAuth.cs b/src/App/Models/OtpAuth.cs index 01f2e0b5d..971587788 100644 --- a/src/App/Models/OtpAuth.cs +++ b/src/App/Models/OtpAuth.cs @@ -44,6 +44,12 @@ namespace Bit.App.Models } } } + else if (key?.ToLowerInvariant().StartsWith("steam://") ?? false) + { + Steam = true; + Digits = 5; + Secret = key.Substring(8); + } else { Secret = key; @@ -54,5 +60,6 @@ namespace Bit.App.Models public int Digits { get; set; } = 6; public MacAlgorithm Algorithm { get; set; } = MacAlgorithm.HmacSha1; public string Secret { get; set; } + public bool Steam { get; set; } } } diff --git a/src/App/Utilities/Crypto.cs b/src/App/Utilities/Crypto.cs index 3e2b748cc..81dc30fd5 100644 --- a/src/App/Utilities/Crypto.cs +++ b/src/App/Utilities/Crypto.cs @@ -9,6 +9,8 @@ namespace Bit.App.Utilities { public static class Crypto { + private static string SteamChars = "23456789BCDFGHJKMNPQRTVWXY"; + public static CipherString AesCbcEncrypt(byte[] plainBytes, SymmetricCryptoKey key) { var parts = AesCbcEncryptToParts(plainBytes, key); @@ -203,9 +205,23 @@ namespace Bit.App.Utilities var offset = (hash[hash.Length - 1] & 0xf); var binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); - var otp = binary % (int)Math.Pow(10, otpParams.Digits); - return otp.ToString().PadLeft(otpParams.Digits, '0'); + string otp = string.Empty; + if(otpParams.Steam) + { + var fullCode = binary & 0x7fffffff; + for(var i = 0; i < otpParams.Digits; i++) + { + otp += SteamChars[fullCode % SteamChars.Length]; + fullCode = (int)Math.Truncate(fullCode / (double)SteamChars.Length); + } + } + else + { + var rawOtp = binary % (int)Math.Pow(10, otpParams.Digits); + otp = rawOtp.ToString().PadLeft(otpParams.Digits, '0'); + } + return otp; } // ref: https://tools.ietf.org/html/rfc5869