1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-26 21:23:46 +00:00
Files
mobile/src/Core/Services/TotpService.cs
Matt Portune 2e8824ce05 Account Switching (#1807)
* 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>
2022-02-23 12:40:17 -05:00

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();
}
}
}