mirror of
https://github.com/bitwarden/mobile
synced 2025-12-26 21:23:46 +00:00
* Account Switching (#1720) * Account switching * WIP * wip * wip * updates to send test logic * fixed Send tests * fixes for theme handling on account switching and re-adding existing account * switch fixes * fixes * fixes * cleanup * vault timeout fixes * account list status enhancements * logout fixes and token handling improvements * merge latest (#1727) * remove duplicate dependency * fix for initial login token storage paradox (#1730) * Fix avatar color update toolbar item issue on iOS for account switching (#1735) * Updated account switching menu UI (#1733) * updated account switching menu UI * additional changes * add key suffix to constant * GetFirstLetters method tweaks * Fix crash on account switching when logging out when having more than user at a time (#1740) * single account migration to multi-account on app update (#1741) * Account Switching Tap to dismiss (#1743) * Added tap to dismiss on the Account switching overlay and improved a bit the code * Fix account switching overlay background transparent on the proper place * Fixed transparent background and the shadow on the account switching overlay * Fix iOS top space on Account switching list overlay after modal (#1746) * Fix top space added to Account switching list overlay after closing modal * Fix top space added to Account switching list overlay after closing modal on lock, login and home views just in case we add modals in the future there as well * Usability: dismiss account list on certain events (#1748) * dismiss account list on certain events * use new FireAndForget method for back button logic * Create and use Account Switching overlay control (#1753) * Added Account switching overlay control and its own ViewModel and refactored accordingly * Fix account switching Accounts list binding update * Implemented dismiss account switching overlay when changing tabs and when selecting the same tab. Also updated the deprecated listener on CustomTabbedRenderer on Android (#1755) * Overriden Equals on AvatarImageSource so it doesn't get set multiple times when it's the same image thus producing blinking on tab chaged (#1756) * Usability improvements for logout on vault timeout (#1781) * accountswitching fixes (#1784) * Fix for invalid PIN lock state when switching accounts (#1792) * fix for pin lock flow * named tuple values and updated async * clear send service cache on account switch (#1796) * Global theme and account removal (#1793) * Global theme and account removal * remove redundant call to hide account list overlay * cleanup and additional tweaks * add try/catch to remove account dialog flow Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
143 lines
4.9 KiB
C#
143 lines
4.9 KiB
C#
using Bit.Core.Abstractions;
|
|
using Bit.Core.Enums;
|
|
using Bit.Core.Utilities;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Bit.Core.Services
|
|
{
|
|
public class TotpService : ITotpService
|
|
{
|
|
private const string SteamChars = "23456789BCDFGHJKMNPQRTVWXY";
|
|
|
|
private readonly IStateService _stateService;
|
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
|
|
|
public TotpService(
|
|
IStateService stateService,
|
|
ICryptoFunctionService cryptoFunctionService)
|
|
{
|
|
_stateService = stateService;
|
|
_cryptoFunctionService = cryptoFunctionService;
|
|
}
|
|
|
|
public async Task<string> GetCodeAsync(string key)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
{
|
|
return null;
|
|
}
|
|
var period = 30;
|
|
var alg = CryptoHashAlgorithm.Sha1;
|
|
var digits = 6;
|
|
var keyB32 = key;
|
|
|
|
var isOtpAuth = key?.ToLowerInvariant().StartsWith("otpauth://") ?? false;
|
|
var isSteamAuth = key?.ToLowerInvariant().StartsWith("steam://") ?? false;
|
|
if (isOtpAuth)
|
|
{
|
|
var qsParams = CoreHelpers.GetQueryParams(key);
|
|
if (qsParams.ContainsKey("digits") && qsParams["digits"] != null &&
|
|
int.TryParse(qsParams["digits"].Trim(), out var digitParam))
|
|
{
|
|
if (digitParam > 10)
|
|
{
|
|
digits = 10;
|
|
}
|
|
else if (digitParam > 0)
|
|
{
|
|
digits = digitParam;
|
|
}
|
|
}
|
|
if (qsParams.ContainsKey("period") && qsParams["period"] != null &&
|
|
int.TryParse(qsParams["period"].Trim(), out var periodParam) && periodParam > 0)
|
|
{
|
|
period = periodParam;
|
|
}
|
|
if (qsParams.ContainsKey("secret") && qsParams["secret"] != null)
|
|
{
|
|
keyB32 = qsParams["secret"];
|
|
}
|
|
if (qsParams.ContainsKey("algorithm") && qsParams["algorithm"] != null)
|
|
{
|
|
var algParam = qsParams["algorithm"].ToLowerInvariant();
|
|
if (algParam == "sha256")
|
|
{
|
|
alg = CryptoHashAlgorithm.Sha256;
|
|
}
|
|
else if (algParam == "sha512")
|
|
{
|
|
alg = CryptoHashAlgorithm.Sha512;
|
|
}
|
|
}
|
|
}
|
|
else if (isSteamAuth)
|
|
{
|
|
digits = 5;
|
|
keyB32 = key.Substring(8);
|
|
}
|
|
|
|
var keyBytes = Base32.FromBase32(keyB32);
|
|
if (keyBytes == null || keyBytes.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
var now = CoreHelpers.EpocUtcNow() / 1000;
|
|
var time = now / period;
|
|
var timeBytes = BitConverter.GetBytes(time);
|
|
if (BitConverter.IsLittleEndian)
|
|
{
|
|
Array.Reverse(timeBytes, 0, timeBytes.Length);
|
|
}
|
|
|
|
var hash = await _cryptoFunctionService.HmacAsync(timeBytes, keyBytes, alg);
|
|
if (hash.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
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);
|
|
|
|
string otp = string.Empty;
|
|
if (isSteamAuth)
|
|
{
|
|
var fullCode = binary & 0x7fffffff;
|
|
for (var i = 0; i < digits; i++)
|
|
{
|
|
otp += SteamChars[fullCode % SteamChars.Length];
|
|
fullCode = (int)Math.Truncate(fullCode / (double)SteamChars.Length);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var rawOtp = binary % (int)Math.Pow(10, digits);
|
|
otp = rawOtp.ToString().PadLeft(digits, '0');
|
|
}
|
|
return otp;
|
|
}
|
|
|
|
public int GetTimeInterval(string key)
|
|
{
|
|
var period = 30;
|
|
if (key != null && key.ToLowerInvariant().StartsWith("otpauth://"))
|
|
{
|
|
var qsParams = CoreHelpers.GetQueryParams(key);
|
|
if (qsParams.ContainsKey("period") && qsParams["period"] != null &&
|
|
int.TryParse(qsParams["period"].Trim(), out var periodParam) && periodParam > 0)
|
|
{
|
|
period = periodParam;
|
|
}
|
|
}
|
|
return period;
|
|
}
|
|
|
|
public async Task<bool> IsAutoCopyEnabledAsync()
|
|
{
|
|
var disabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
|
return !disabled.GetValueOrDefault();
|
|
}
|
|
}
|
|
}
|