mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
53 Commits
feature/ma
...
add-bio-ke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2a91c435 | ||
|
|
4e1361e94a | ||
|
|
d6567fe819 | ||
|
|
bf749d39de | ||
|
|
2522bbc60f | ||
|
|
b620a9c09f | ||
|
|
903f099134 | ||
|
|
fd5ef49811 | ||
|
|
78004dbdb9 | ||
|
|
e820408a64 | ||
|
|
c595b1626e | ||
|
|
270a395d9f | ||
|
|
4fa8d2ba28 | ||
|
|
e076c9fe04 | ||
|
|
ee0dcd23f5 | ||
|
|
1e8ed1b5ce | ||
|
|
7fb89fa1a5 | ||
|
|
b1eb263fef | ||
|
|
61aac20555 | ||
|
|
3e87d74061 | ||
|
|
89a9185b20 | ||
|
|
e323e196c0 | ||
|
|
c793260689 | ||
|
|
c2ddbb7eff | ||
|
|
bb5a7383a8 | ||
|
|
de5113ede7 | ||
|
|
ba6d260565 | ||
|
|
7562c688c5 | ||
|
|
10574a7117 | ||
|
|
a2f1ca583a | ||
|
|
813ac841c6 | ||
|
|
0da3d25955 | ||
|
|
f8c9cde2ed | ||
|
|
a3183857b9 | ||
|
|
bedbca841d | ||
|
|
0ff314f076 | ||
|
|
c9a7c29190 | ||
|
|
546bf8dcb1 | ||
|
|
7fdc5597fc | ||
|
|
7c664f58b3 | ||
|
|
bdfe806846 | ||
|
|
5ed567ab90 | ||
|
|
cd4f44e6f6 | ||
|
|
d58f0b281b | ||
|
|
5ba3fac0c0 | ||
|
|
1e30524985 | ||
|
|
515decb4c9 | ||
|
|
bf28d373e9 | ||
|
|
69d38d4d75 | ||
|
|
c1619536aa | ||
|
|
079e02e4e5 | ||
|
|
15d3da607b | ||
|
|
b5cf9fd79d |
@@ -156,9 +156,9 @@ namespace Bit.Droid
|
|||||||
messagingService, broadcasterService);
|
messagingService, broadcasterService);
|
||||||
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
||||||
platformUtilsService, new LazyResolve<IEventService>());
|
platformUtilsService, new LazyResolve<IEventService>());
|
||||||
var biometricService = new BiometricService(stateService);
|
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
|
var biometricService = new BiometricService(stateService, cryptoService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
||||||
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);
|
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Security.Keystore;
|
using Android.Security.Keystore;
|
||||||
|
using Bit.App.Services;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Java.Security;
|
using Java.Security;
|
||||||
@@ -9,10 +10,8 @@ using Javax.Crypto;
|
|||||||
|
|
||||||
namespace Bit.Droid.Services
|
namespace Bit.Droid.Services
|
||||||
{
|
{
|
||||||
public class BiometricService : IBiometricService
|
public class BiometricService : BaseBiometricService
|
||||||
{
|
{
|
||||||
private readonly IStateService _stateService;
|
|
||||||
|
|
||||||
private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
|
private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
|
||||||
|
|
||||||
private const string KeyStoreName = "AndroidKeyStore";
|
private const string KeyStoreName = "AndroidKeyStore";
|
||||||
@@ -24,14 +23,14 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
private readonly KeyStore _keystore;
|
private readonly KeyStore _keystore;
|
||||||
|
|
||||||
public BiometricService(IStateService stateService)
|
public BiometricService(IStateService stateService, ICryptoService cryptoService)
|
||||||
|
: base(stateService, cryptoService)
|
||||||
{
|
{
|
||||||
_stateService = stateService;
|
|
||||||
_keystore = KeyStore.GetInstance(KeyStoreName);
|
_keystore = KeyStore.GetInstance(KeyStoreName);
|
||||||
_keystore.Load(null);
|
_keystore.Load(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
public override async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
@@ -41,7 +40,7 @@ namespace Bit.Droid.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
public override async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
<Grid
|
<Grid
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
IsVisible="{Binding PinLock}"
|
IsVisible="{Binding PinEnabled}"
|
||||||
Padding="0, 10, 0, 0">
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="_passwordGrid"
|
x:Name="_passwordGrid"
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding PinEnabled, Converter={StaticResource inverseBool}}"
|
||||||
Padding="0, 10, 0, 0">
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_vm?.PinLock ?? false)
|
if (_vm?.PinEnabled ?? false)
|
||||||
{
|
{
|
||||||
return _pin;
|
return _pin;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task PromptBiometricAfterResumeAsync()
|
public async Task PromptBiometricAfterResumeAsync()
|
||||||
{
|
{
|
||||||
if (_vm.BiometricLock)
|
if (_vm.BiometricEnabled)
|
||||||
{
|
{
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
if (!_promptedAfterResume)
|
if (!_promptedAfterResume)
|
||||||
@@ -91,13 +91,13 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
||||||
|
|
||||||
if (!_vm.BiometricLock)
|
if (!_vm.BiometricEnabled)
|
||||||
{
|
{
|
||||||
RequestFocus(SecretEntry);
|
RequestFocus(SecretEntry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_vm.UsingKeyConnector && !_vm.PinLock)
|
if (_vm.UsingKeyConnector && !_vm.PinEnabled)
|
||||||
{
|
{
|
||||||
_passwordGrid.IsVisible = false;
|
_passwordGrid.IsVisible = false;
|
||||||
_unlockButton.IsVisible = false;
|
_unlockButton.IsVisible = false;
|
||||||
|
|||||||
@@ -38,16 +38,15 @@ namespace Bit.App.Pages
|
|||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
private string _pin;
|
private string _pin;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _pinLock;
|
private PinLockType _pinStatus;
|
||||||
private bool _biometricLock;
|
private bool _pinEnabled;
|
||||||
|
private bool _biometricEnabled;
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _biometricButtonVisible;
|
private bool _biometricButtonVisible;
|
||||||
private bool _usingKeyConnector;
|
private bool _usingKeyConnector;
|
||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
private bool _isPinProtected;
|
|
||||||
private bool _isPinProtectedWithKey;
|
|
||||||
|
|
||||||
public LockPageViewModel()
|
public LockPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -100,10 +99,10 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PinLock
|
public bool PinEnabled
|
||||||
{
|
{
|
||||||
get => _pinLock;
|
get => _pinEnabled;
|
||||||
set => SetProperty(ref _pinLock, value);
|
set => SetProperty(ref _pinEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UsingKeyConnector
|
public bool UsingKeyConnector
|
||||||
@@ -111,10 +110,10 @@ namespace Bit.App.Pages
|
|||||||
get => _usingKeyConnector;
|
get => _usingKeyConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BiometricLock
|
public bool BiometricEnabled
|
||||||
{
|
{
|
||||||
get => _biometricLock;
|
get => _biometricEnabled;
|
||||||
set => SetProperty(ref _biometricLock, value);
|
set => SetProperty(ref _biometricEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BiometricIntegrityValid
|
public bool BiometricIntegrityValid
|
||||||
@@ -162,14 +161,18 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
|
||||||
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
|
||||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockType.Persistent;
|
||||||
|
|
||||||
|
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _biometricService.CanUseBiometricsUnlockAsync();
|
||||||
|
|
||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
if (_usingKeyConnector && !(BiometricLock || PinLock))
|
if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))
|
||||||
{
|
{
|
||||||
await _vaultTimeoutService.LogOutAsync();
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
return;
|
return;
|
||||||
@@ -188,7 +191,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
var webVaultHostname = CoreHelpers.GetHostname(webVault);
|
var webVaultHostname = CoreHelpers.GetHostname(webVault);
|
||||||
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
|
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
|
||||||
if (PinLock)
|
if (PinEnabled)
|
||||||
{
|
{
|
||||||
PageTitle = AppResources.VerifyPIN;
|
PageTitle = AppResources.VerifyPIN;
|
||||||
LockedVerifyText = AppResources.VaultLockedPIN;
|
LockedVerifyText = AppResources.VaultLockedPIN;
|
||||||
@@ -207,7 +210,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BiometricLock)
|
if (BiometricEnabled)
|
||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
@@ -229,14 +232,14 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
if (PinLock && string.IsNullOrWhiteSpace(Pin))
|
if (PinEnabled && string.IsNullOrWhiteSpace(Pin))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!PinLock && string.IsNullOrWhiteSpace(MasterPassword))
|
if (!PinEnabled && string.IsNullOrWhiteSpace(MasterPassword))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
@@ -247,34 +250,54 @@ namespace Bit.App.Pages
|
|||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
|
|
||||||
if (PinLock)
|
if (PinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockType.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockType.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockType.Transient,
|
||||||
|
Pin,
|
||||||
|
_email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
failed = decPin != Pin;
|
|
||||||
if (!failed)
|
|
||||||
{
|
|
||||||
Pin = string.Empty;
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
failed = false;
|
Pin,
|
||||||
|
_email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != Pin;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetUserKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -295,19 +318,21 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync();
|
||||||
var passwordValid = false;
|
var passwordValid = false;
|
||||||
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
|
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
|
||||||
|
|
||||||
if (storedKeyHash != null)
|
if (storedKeyHash != null)
|
||||||
{
|
{
|
||||||
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
// Offline unlock possible
|
||||||
|
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, masterKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Online unlock required
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
var keyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization);
|
||||||
var request = new PasswordVerificationRequest();
|
var request = new PasswordVerificationRequest();
|
||||||
request.MasterPasswordHash = keyHash;
|
request.MasterPasswordHash = keyHash;
|
||||||
|
|
||||||
@@ -316,8 +341,8 @@ namespace Bit.App.Pages
|
|||||||
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
|
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
|
||||||
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
|
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
|
||||||
passwordValid = true;
|
passwordValid = true;
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
var localKeyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
await _cryptoService.SetMasterKeyHashAsync(localKeyHash);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -327,15 +352,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
|
||||||
{
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
|
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
|
||||||
{
|
{
|
||||||
// Save the ForcePasswordResetReason to force a password reset after unlock
|
// Save the ForcePasswordResetReason to force a password reset after unlock
|
||||||
@@ -345,10 +361,13 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetUserKeyAndContinueAsync(userKey);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
if (BiometricLock & !BiometricIntegrityValid)
|
if (BiometricEnabled & !BiometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync();
|
await _biometricService.SetupBiometricAsync();
|
||||||
}
|
}
|
||||||
@@ -425,7 +444,7 @@ namespace Bit.App.Pages
|
|||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
var secret = PinLock ? Pin : MasterPassword;
|
var secret = PinEnabled ? Pin : MasterPassword;
|
||||||
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
|
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,26 +452,27 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
BiometricButtonVisible = BiometricIntegrityValid;
|
BiometricButtonVisible = BiometricIntegrityValid;
|
||||||
if (!BiometricLock || !BiometricIntegrityValid)
|
if (!BiometricEnabled || !BiometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
PinLock ? AppResources.PIN : AppResources.MasterPassword,
|
PinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
|
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
await DoContinueAsync();
|
var userKey = await _cryptoService.GetBiometricUnlockKeyAsync();
|
||||||
|
await SetUserKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
|
private async Task SetUserKeyAndContinueAsync(UserKey key)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(key);
|
||||||
}
|
}
|
||||||
await DoContinueAsync();
|
await DoContinueAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,25 +177,25 @@ namespace Bit.App.Pages
|
|||||||
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||||
Email = Email.Trim().ToLower();
|
Email = Email.Trim().ToLower();
|
||||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig);
|
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
|
||||||
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
|
||||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||||
var request = new RegisterRequest
|
var request = new RegisterRequest
|
||||||
{
|
{
|
||||||
Email = Email,
|
Email = Email,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
MasterPasswordHash = hashedPassword,
|
MasterPasswordHash = hashedPassword,
|
||||||
MasterPasswordHint = Hint,
|
MasterPasswordHint = Hint,
|
||||||
Key = encKey.Item2.EncryptedString,
|
Key = newProtectedUserKey.EncryptedString,
|
||||||
Kdf = kdfConfig.Type,
|
Kdf = kdfConfig.Type,
|
||||||
KdfIterations = kdfConfig.Iterations,
|
KdfIterations = kdfConfig.Iterations,
|
||||||
KdfMemory = kdfConfig.Memory,
|
KdfMemory = kdfConfig.Memory,
|
||||||
KdfParallelism = kdfConfig.Parallelism,
|
KdfParallelism = kdfConfig.Parallelism,
|
||||||
Keys = new KeysRequest
|
Keys = new KeysRequest
|
||||||
{
|
{
|
||||||
PublicKey = keys.Item1,
|
PublicKey = newPublicKey,
|
||||||
EncryptedPrivateKey = keys.Item2.EncryptedString
|
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||||
},
|
},
|
||||||
CaptchaResponse = _captchaToken,
|
CaptchaResponse = _captchaToken,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
Organization = await _keyConnectorService.GetManagingOrganization();
|
Organization = await _keyConnectorService.GetManagingOrganizationAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MigrateAccount()
|
public async Task MigrateAccount()
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
await _keyConnectorService.MigrateUser();
|
await _keyConnectorService.MigrateUserAsync();
|
||||||
await _syncService.FullSyncAsync(true);
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|||||||
@@ -165,26 +165,17 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
|
||||||
|
|
||||||
Tuple<SymmetricCryptoKey, EncString> encKey;
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
|
||||||
if (existingEncKey == null)
|
|
||||||
{
|
|
||||||
encKey = await _cryptoService.MakeEncKeyAsync(key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
encKey = await _cryptoService.RemakeEncKeyAsync(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||||
var request = new SetPasswordRequest
|
var request = new SetPasswordRequest
|
||||||
{
|
{
|
||||||
MasterPasswordHash = masterPasswordHash,
|
MasterPasswordHash = masterPasswordHash,
|
||||||
Key = encKey.Item2.EncryptedString,
|
Key = newProtectedUserKey.EncryptedString,
|
||||||
MasterPasswordHint = Hint,
|
MasterPasswordHint = Hint,
|
||||||
Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256),
|
Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256),
|
||||||
KdfIterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Pbkdf2Iterations),
|
KdfIterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Pbkdf2Iterations),
|
||||||
@@ -193,8 +184,8 @@ namespace Bit.App.Pages
|
|||||||
OrgIdentifier = OrgIdentifier,
|
OrgIdentifier = OrgIdentifier,
|
||||||
Keys = new KeysRequest
|
Keys = new KeysRequest
|
||||||
{
|
{
|
||||||
PublicKey = keys.Item1,
|
PublicKey = newPublicKey,
|
||||||
EncryptedPrivateKey = keys.Item2.EncryptedString
|
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -204,19 +195,20 @@ namespace Bit.App.Pages
|
|||||||
// Set Password and relevant information
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(newUserKey);
|
||||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
|
||||||
|
await _cryptoService.SetUserPrivateKeyAsync(newProtectedPrivateKey.EncryptedString);
|
||||||
|
|
||||||
if (ResetPasswordAutoEnroll)
|
if (ResetPasswordAutoEnroll)
|
||||||
{
|
{
|
||||||
// Grab Organization Keys
|
// Grab Organization Keys
|
||||||
var response = await _apiService.GetOrganizationKeysAsync(OrgId);
|
var response = await _apiService.GetOrganizationKeysAsync(OrgId);
|
||||||
var publicKey = CoreHelpers.Base64UrlDecode(response.PublicKey);
|
var publicKey = CoreHelpers.Base64UrlDecode(response.PublicKey);
|
||||||
// Grab user's Encryption Key and encrypt with Org Public Key
|
// Grab User Key and encrypt with Org Public Key
|
||||||
var userEncKey = await _cryptoService.GetEncKeyAsync();
|
var userKey = await _cryptoService.GetUserKeyAsync();
|
||||||
var encryptedKey = await _cryptoService.RsaEncryptAsync(userEncKey.Key, publicKey);
|
var encryptedKey = await _cryptoService.RsaEncryptAsync(userKey.Key, publicKey);
|
||||||
// Request
|
// Request
|
||||||
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
|
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,12 +93,12 @@ namespace Bit.App.Pages
|
|||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new master key and hash new password
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey);
|
||||||
|
|
||||||
// Create new encKey for the User
|
// Encrypt user key with new master key
|
||||||
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
|
||||||
// Initiate API action
|
// Initiate API action
|
||||||
try
|
try
|
||||||
@@ -108,10 +108,10 @@ namespace Bit.App.Pages
|
|||||||
switch (_reason)
|
switch (_reason)
|
||||||
{
|
{
|
||||||
case ForcePasswordResetReason.AdminForcePasswordReset:
|
case ForcePasswordResetReason.AdminForcePasswordReset:
|
||||||
await UpdateTempPasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
|
await UpdateTempPasswordAsync(masterPasswordHash, newProtectedUserKey.EncryptedString);
|
||||||
break;
|
break;
|
||||||
case ForcePasswordResetReason.WeakMasterPasswordOnLogin:
|
case ForcePasswordResetReason.WeakMasterPasswordOnLogin:
|
||||||
await UpdatePasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
|
await UpdatePasswordAsync(masterPasswordHash, newProtectedUserKey.EncryptedString);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
@@ -155,7 +155,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task UpdatePasswordAsync(string newMasterPasswordHash, string newEncKey)
|
private async Task UpdatePasswordAsync(string newMasterPasswordHash, string newEncKey)
|
||||||
{
|
{
|
||||||
var currentPasswordHash = await _cryptoService.HashPasswordAsync(CurrentMasterPassword, null);
|
var currentPasswordHash = await _cryptoService.HashMasterKeyAsync(CurrentMasterPassword, null);
|
||||||
|
|
||||||
var request = new PasswordRequest
|
var request = new PasswordRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||||
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||||
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnector();
|
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
|
|
||||||
if (UseOTPVerification)
|
if (UseOTPVerification)
|
||||||
{
|
{
|
||||||
@@ -165,7 +165,7 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
|
var verificationType = await _keyConnectorService.GetUsesKeyConnectorAsync()
|
||||||
? VerificationType.OTP
|
? VerificationType.OTP
|
||||||
: VerificationType.MasterPassword;
|
: VerificationType.MasterPassword;
|
||||||
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ namespace Bit.App.Pages
|
|||||||
t.Value != null).ToList();
|
t.Value != null).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
var pinSet = await _vaultTimeoutService.GetPinLockTypeAsync();
|
||||||
_pin = pinSet.Item1 || pinSet.Item2;
|
_pin = pinSet != PinLockType.Disabled;
|
||||||
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
||||||
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||||
!await _keyConnectorService.GetUsesKeyConnector();
|
!await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
_reportLoggingEnabled = await _loggerService.IsEnabled();
|
||||||
_approvePasswordlessLoginRequests = await _stateService.GetApprovePasswordlessLoginsAsync();
|
_approvePasswordlessLoginRequests = await _stateService.GetApprovePasswordlessLoginsAsync();
|
||||||
_shouldConnectToWatch = await _stateService.GetShouldConnectToWatchAsync();
|
_shouldConnectToWatch = await _stateService.GetShouldConnectToWatchAsync();
|
||||||
@@ -323,6 +323,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (oldTimeout != newTimeout)
|
if (oldTimeout != newTimeout)
|
||||||
{
|
{
|
||||||
|
await _cryptoService.RefreshKeysAsync();
|
||||||
await Device.InvokeOnMainThreadAsync(BuildList);
|
await Device.InvokeOnMainThreadAsync(BuildList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +429,7 @@ namespace Bit.App.Pages
|
|||||||
if (!string.IsNullOrWhiteSpace(pin))
|
if (!string.IsNullOrWhiteSpace(pin))
|
||||||
{
|
{
|
||||||
var masterPassOnRestart = false;
|
var masterPassOnRestart = false;
|
||||||
if (!await _keyConnectorService.GetUsesKeyConnector())
|
if (!await _keyConnectorService.GetUsesKeyConnectorAsync())
|
||||||
{
|
{
|
||||||
masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
|
masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
|
||||||
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
|
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
|
||||||
@@ -437,19 +438,20 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
|
var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig);
|
||||||
var key = await _cryptoService.GetKeyAsync();
|
var userKey = await _cryptoService.GetUserKeyAsync();
|
||||||
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
var protectedPinKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey);
|
||||||
|
|
||||||
|
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||||
|
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||||
|
|
||||||
if (masterPassOnRestart)
|
if (masterPassOnRestart)
|
||||||
{
|
{
|
||||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(protectedPinKey);
|
||||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(pinProtectedKey);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
await _stateService.SetPinKeyEncryptedUserKeyAsync(protectedPinKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -459,7 +461,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (!_pin)
|
if (!_pin)
|
||||||
{
|
{
|
||||||
await _cryptoService.ClearPinProtectedKeyAsync();
|
|
||||||
await _vaultTimeoutService.ClearAsync();
|
await _vaultTimeoutService.ClearAsync();
|
||||||
}
|
}
|
||||||
BuildList();
|
BuildList();
|
||||||
@@ -491,7 +492,7 @@ namespace Bit.App.Pages
|
|||||||
await _stateService.SetBiometricUnlockAsync(null);
|
await _stateService.SetBiometricUnlockAsync(null);
|
||||||
}
|
}
|
||||||
await _stateService.SetBiometricLockedAsync(false);
|
await _stateService.SetBiometricLockedAsync(false);
|
||||||
await _cryptoService.ToggleKeyAsync();
|
await _cryptoService.RefreshKeysAsync();
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
await UpdateVaultButtonTitleAsync();
|
await UpdateVaultButtonTitleAsync();
|
||||||
if (await _keyConnectorService.UserNeedsMigration())
|
if (await _keyConnectorService.UserNeedsMigrationAsync())
|
||||||
{
|
{
|
||||||
_messagingService.Send("convertAccountToKeyConnector");
|
_messagingService.Send("convertAccountToKeyConnector");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace Bit.App.Pages
|
|||||||
_cipherDomain = await _cipherService.GetAsync(CipherId);
|
_cipherDomain = await _cipherService.GetAsync(CipherId);
|
||||||
Cipher = await _cipherDomain.DecryptAsync();
|
Cipher = await _cipherDomain.DecryptAsync();
|
||||||
LoadAttachments();
|
LoadAttachments();
|
||||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
_hasUpdatedKey = await _cryptoService.HasUserKeyAsync();
|
||||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||||
if (!_canAccessAttachments)
|
if (!_canAccessAttachments)
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Hide password reprompt option if using key connector
|
// Hide password reprompt option if using key connector
|
||||||
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnector();
|
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
|
|||||||
25
src/App/Services/BaseBiometricService.cs
Normal file
25
src/App/Services/BaseBiometricService.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
|
||||||
|
namespace Bit.App.Services
|
||||||
|
{
|
||||||
|
public abstract class BaseBiometricService : IBiometricService
|
||||||
|
{
|
||||||
|
protected readonly IStateService _stateService;
|
||||||
|
protected readonly ICryptoService _cryptoService;
|
||||||
|
|
||||||
|
protected BaseBiometricService(IStateService stateService, ICryptoService cryptoService)
|
||||||
|
{
|
||||||
|
_stateService = stateService;
|
||||||
|
_cryptoService = cryptoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanUseBiometricsUnlockAsync()
|
||||||
|
{
|
||||||
|
return await _cryptoService.HasEncryptedUserKeyAsync() || await _stateService.GetKeyEncryptedAsync() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
|
||||||
|
public abstract Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ namespace Bit.App.Services
|
|||||||
public async Task<bool> Enabled()
|
public async Task<bool> Enabled()
|
||||||
{
|
{
|
||||||
var keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
var keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
return !await keyConnectorService.GetUsesKeyConnector();
|
return !await keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ namespace Bit.App.Utilities.AccountManagement
|
|||||||
|
|
||||||
private async Task AddAccountAsync()
|
private async Task AddAccountAsync()
|
||||||
{
|
{
|
||||||
|
await AppHelpers.ClearServiceCacheAsync();
|
||||||
await Device.InvokeOnMainThreadAsync(() =>
|
await Device.InvokeOnMainThreadAsync(() =>
|
||||||
{
|
{
|
||||||
Options.HideAccountSwitcher = false;
|
Options.HideAccountSwitcher = false;
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Bit.App.Utilities
|
|||||||
|
|
||||||
public async Task ValidateAndExecuteAsync()
|
public async Task ValidateAndExecuteAsync()
|
||||||
{
|
{
|
||||||
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
|
var verificationType = await _keyConnectorService.GetUsesKeyConnectorAsync()
|
||||||
? VerificationType.OTP
|
? VerificationType.OTP
|
||||||
: VerificationType.MasterPassword;
|
: VerificationType.MasterPassword;
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ namespace Bit.App.Utilities
|
|||||||
}
|
}
|
||||||
|
|
||||||
var parameters = GetParameters();
|
var parameters = GetParameters();
|
||||||
parameters.Secret = await _cryptoService.HashPasswordAsync(password, null);
|
parameters.Secret = await _cryptoService.HashMasterKeyAsync(password, null);
|
||||||
parameters.VerificationType = VerificationType.MasterPassword;
|
parameters.VerificationType = VerificationType.MasterPassword;
|
||||||
await ExecuteAsync(parameters);
|
await ExecuteAsync(parameters);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
|
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
|
||||||
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
||||||
OrganizationUserResetPasswordEnrollmentRequest request);
|
OrganizationUserResetPasswordEnrollmentRequest request);
|
||||||
Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl);
|
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl);
|
||||||
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||||
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
||||||
Task PostConvertToKeyConnector();
|
Task PostConvertToKeyConnector();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace Bit.Core.Abstractions
|
|||||||
{
|
{
|
||||||
public interface IBiometricService
|
public interface IBiometricService
|
||||||
{
|
{
|
||||||
|
Task<bool> CanUseBiometricsUnlockAsync();
|
||||||
Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null);
|
Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null);
|
||||||
Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
|
Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,49 +9,56 @@ namespace Bit.Core.Abstractions
|
|||||||
{
|
{
|
||||||
public interface ICryptoService
|
public interface ICryptoService
|
||||||
{
|
{
|
||||||
Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
|
|
||||||
Task ClearKeyAsync(string userId = null);
|
|
||||||
Task ClearKeyHashAsync(string userId = null);
|
|
||||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
|
||||||
Task ClearKeysAsync(string userId = null);
|
|
||||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
|
||||||
Task ClearPinProtectedKeyAsync(string userId = null);
|
|
||||||
void ClearCache();
|
void ClearCache();
|
||||||
|
Task RefreshKeysAsync();
|
||||||
|
Task SetUserKeyAsync(UserKey userKey, string userId = null);
|
||||||
|
Task<UserKey> GetUserKeyAsync(string userId = null);
|
||||||
|
Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null);
|
||||||
|
Task<bool> HasUserKeyAsync(string userId = null);
|
||||||
|
Task<bool> HasEncryptedUserKeyAsync(string userId = null);
|
||||||
|
Task<UserKey> MakeUserKeyAsync();
|
||||||
|
Task ClearUserKeyAsync(string userId = null);
|
||||||
|
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||||
|
Task<UserKey> GetAutoUnlockKeyAsync(string userId = null);
|
||||||
|
Task<bool> HasAutoUnlockKeyAsync(string userId = null);
|
||||||
|
Task<UserKey> GetBiometricUnlockKeyAsync(string userId = null);
|
||||||
|
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
|
||||||
|
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||||
|
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||||
|
Task ClearMasterKeyAsync(string userId = null);
|
||||||
|
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey);
|
||||||
|
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
|
||||||
|
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key);
|
||||||
|
Task<string> HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||||
|
Task SetMasterKeyHashAsync(string keyHash);
|
||||||
|
Task<string> GetMasterKeyHashAsync();
|
||||||
|
Task ClearMasterKeyHashAsync(string userId = null);
|
||||||
|
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key);
|
||||||
|
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
||||||
|
Task<OrgKey> GetOrgKeyAsync(string orgId);
|
||||||
|
Task<Dictionary<string, OrgKey>> GetOrgKeysAsync();
|
||||||
|
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||||
|
Task<byte[]> GetUserPublicKeyAsync();
|
||||||
|
Task SetUserPrivateKeyAsync(string encPrivateKey);
|
||||||
|
Task<byte[]> GetUserPrivateKeyAsync();
|
||||||
|
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||||
|
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||||
|
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||||
|
Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config);
|
||||||
|
Task ClearPinKeysAsync(string userId = null);
|
||||||
|
Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null);
|
||||||
|
Task<MasterKey> DecryptMasterKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedMasterKey = null);
|
||||||
|
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||||
|
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||||
|
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||||
|
Task<int> RandomNumberAsync(int min, int max);
|
||||||
|
Task<string> RandomStringAsync(int length);
|
||||||
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
||||||
Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null);
|
Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null);
|
||||||
Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null);
|
Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null);
|
||||||
Task<EncString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
Task<EncString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||||
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
||||||
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||||
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
|
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
|
||||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
|
||||||
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
|
|
||||||
Task<string> GetKeyHashAsync();
|
|
||||||
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
|
|
||||||
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
|
|
||||||
Task<byte[]> GetPrivateKeyAsync();
|
|
||||||
Task<byte[]> GetPublicKeyAsync();
|
|
||||||
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
|
|
||||||
Task<bool> HasEncKeyAsync();
|
|
||||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
|
||||||
Task<bool> HasKeyAsync(string userId = null);
|
|
||||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
|
||||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config);
|
|
||||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null);
|
|
||||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
|
||||||
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config);
|
|
||||||
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
|
||||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
|
||||||
Task<int> RandomNumberAsync(int min, int max);
|
|
||||||
Task<string> RandomStringAsync(int length);
|
|
||||||
Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key);
|
|
||||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
|
||||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
|
||||||
Task SetEncKeyAsync(string encKey);
|
|
||||||
Task SetEncPrivateKeyAsync(string encPrivateKey);
|
|
||||||
Task SetKeyAsync(SymmetricCryptoKey key);
|
|
||||||
Task SetKeyHashAsync(string keyHash);
|
|
||||||
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
|
||||||
Task ToggleKeyAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ namespace Bit.Core.Abstractions
|
|||||||
{
|
{
|
||||||
public interface IKeyConnectorService
|
public interface IKeyConnectorService
|
||||||
{
|
{
|
||||||
Task SetUsesKeyConnector(bool usesKeyConnector);
|
Task SetUsesKeyConnectorAsync(bool usesKeyConnector);
|
||||||
Task<bool> GetUsesKeyConnector();
|
Task<bool> GetUsesKeyConnectorAsync();
|
||||||
Task<bool> UserNeedsMigration();
|
Task<bool> UserNeedsMigrationAsync();
|
||||||
Task MigrateUser();
|
Task MigrateUserAsync();
|
||||||
Task GetAndSetKey(string url);
|
Task GetAndSetMasterKeyAsync(string url);
|
||||||
Task<Organization> GetManagingOrganization();
|
Task<Organization> GetManagingOrganizationAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ namespace Bit.Core.Abstractions
|
|||||||
public interface IStateService
|
public interface IStateService
|
||||||
{
|
{
|
||||||
List<AccountView> AccountViews { get; }
|
List<AccountView> AccountViews { get; }
|
||||||
|
Task<UserKey> GetUserKeyAsync(string userId = null);
|
||||||
|
Task SetUserKeyAsync(UserKey value, string userId = null);
|
||||||
|
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||||
|
Task SetMasterKeyAsync(MasterKey value, string userId = null);
|
||||||
|
Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null);
|
||||||
|
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||||
|
Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null);
|
||||||
|
Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null);
|
||||||
Task<string> GetActiveUserIdAsync();
|
Task<string> GetActiveUserIdAsync();
|
||||||
Task<string> GetActiveUserEmailAsync();
|
Task<string> GetActiveUserEmailAsync();
|
||||||
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
|
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
|
||||||
@@ -27,6 +35,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync();
|
Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync();
|
||||||
Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value);
|
Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value);
|
||||||
Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null);
|
Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null);
|
||||||
|
Task<UserKey> GetUserKeyBiometricUnlockAsync(string userId = null);
|
||||||
|
Task SetUserKeyBiometricUnlockAsync(UserKey value, string userId = null);
|
||||||
Task<bool?> GetBiometricUnlockAsync(string userId = null);
|
Task<bool?> GetBiometricUnlockAsync(string userId = null);
|
||||||
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
||||||
Task<bool> GetBiometricLockedAsync(string userId = null);
|
Task<bool> GetBiometricLockedAsync(string userId = null);
|
||||||
@@ -36,22 +46,16 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||||
Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||||
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
|
||||||
Task<string> GetProtectedPinAsync(string userId = null);
|
Task<string> GetProtectedPinAsync(string userId = null);
|
||||||
|
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
||||||
|
Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null);
|
||||||
|
Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null);
|
||||||
|
Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null);
|
||||||
|
Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null);
|
||||||
Task SetProtectedPinAsync(string value, string userId = null);
|
Task SetProtectedPinAsync(string value, string userId = null);
|
||||||
Task<string> GetPinProtectedAsync(string userId = null);
|
|
||||||
Task SetPinProtectedAsync(string value, string userId = null);
|
|
||||||
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
|
|
||||||
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
|
|
||||||
Task SetKdfConfigurationAsync(KdfConfig config, string userId = null);
|
Task SetKdfConfigurationAsync(KdfConfig config, string userId = null);
|
||||||
Task<string> GetKeyEncryptedAsync(string userId = null);
|
|
||||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
|
||||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
|
||||||
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
|
|
||||||
Task<string> GetKeyHashAsync(string userId = null);
|
Task<string> GetKeyHashAsync(string userId = null);
|
||||||
Task SetKeyHashAsync(string value, string userId = null);
|
Task SetKeyHashAsync(string value, string userId = null);
|
||||||
Task<string> GetEncKeyEncryptedAsync(string userId = null);
|
|
||||||
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
|
||||||
Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null);
|
Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null);
|
||||||
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
||||||
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
||||||
@@ -176,5 +180,24 @@ namespace Bit.Core.Abstractions
|
|||||||
void SetLocale(string locale);
|
void SetLocale(string locale);
|
||||||
ConfigResponse GetConfigs();
|
ConfigResponse GetConfigs();
|
||||||
void SetConfigs(ConfigResponse value);
|
void SetConfigs(ConfigResponse value);
|
||||||
|
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
Task<string> GetPinProtectedAsync(string userId = null);
|
||||||
|
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
Task SetPinProtectedAsync(string value, string userId = null);
|
||||||
|
[Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")]
|
||||||
|
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
|
||||||
|
[Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")]
|
||||||
|
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
|
||||||
|
[Obsolete("Use GetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
Task<string> GetEncKeyEncryptedAsync(string userId = null);
|
||||||
|
[Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
||||||
|
[Obsolete("Left for migration purposes")]
|
||||||
|
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||||
|
|
||||||
|
[Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")]
|
||||||
|
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||||
|
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
||||||
|
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Core.Abstractions
|
namespace Bit.Core.Abstractions
|
||||||
{
|
{
|
||||||
@@ -16,7 +17,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<bool> ShouldLockAsync(string userId = null);
|
Task<bool> ShouldLockAsync(string userId = null);
|
||||||
Task<bool> IsLoggedOutByTimeoutAsync(string userId = null);
|
Task<bool> IsLoggedOutByTimeoutAsync(string userId = null);
|
||||||
Task<bool> ShouldLogOutByTimeoutAsync(string userId = null);
|
Task<bool> ShouldLogOutByTimeoutAsync(string userId = null);
|
||||||
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
|
Task<PinLockType> GetPinLockTypeAsync(string userId = null);
|
||||||
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
||||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
||||||
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Bit.Core
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core
|
||||||
{
|
{
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
@@ -79,6 +81,9 @@
|
|||||||
|
|
||||||
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
||||||
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
||||||
|
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
||||||
|
public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}";
|
||||||
|
public static string UserKeyBiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
||||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||||
public static string FoldersKey(string userId) => $"folders_{userId}";
|
public static string FoldersKey(string userId) => $"folders_{userId}";
|
||||||
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
||||||
@@ -87,12 +92,10 @@
|
|||||||
public static string NeverDomainsKey(string userId) => $"neverDomains_{userId}";
|
public static string NeverDomainsKey(string userId) => $"neverDomains_{userId}";
|
||||||
public static string SendsKey(string userId) => $"sends_{userId}";
|
public static string SendsKey(string userId) => $"sends_{userId}";
|
||||||
public static string PoliciesKey(string userId) => $"policies_{userId}";
|
public static string PoliciesKey(string userId) => $"policies_{userId}";
|
||||||
public static string KeyKey(string userId) => $"key_{userId}";
|
|
||||||
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
||||||
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
||||||
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
|
||||||
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
||||||
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
public static string PinKeyEncryptedUserKeyKey(string userId) => $"pinKeyEncryptedUserKey_{userId}";
|
||||||
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
||||||
public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
|
public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
|
||||||
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";
|
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";
|
||||||
@@ -121,5 +124,11 @@
|
|||||||
public static string PushCurrentTokenKey(string userId) => $"pushCurrentToken_{userId}";
|
public static string PushCurrentTokenKey(string userId) => $"pushCurrentToken_{userId}";
|
||||||
public static string ShouldConnectToWatchKey(string userId) => $"shouldConnectToWatch_{userId}";
|
public static string ShouldConnectToWatchKey(string userId) => $"shouldConnectToWatch_{userId}";
|
||||||
public static string ScreenCaptureAllowedKey(string userId) => $"screenCaptureAllowed_{userId}";
|
public static string ScreenCaptureAllowedKey(string userId) => $"screenCaptureAllowed_{userId}";
|
||||||
|
[Obsolete]
|
||||||
|
public static string KeyKey(string userId) => $"key_{userId}";
|
||||||
|
[Obsolete]
|
||||||
|
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
||||||
|
[Obsolete]
|
||||||
|
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,9 +117,14 @@ namespace Bit.Core.Models.Domain
|
|||||||
|
|
||||||
public class AccountVolatileData
|
public class AccountVolatileData
|
||||||
{
|
{
|
||||||
public SymmetricCryptoKey Key;
|
public UserKey UserKey;
|
||||||
public EncString PinProtectedKey;
|
public MasterKey MasterKey;
|
||||||
|
public EncString PinKeyEncryptedUserKeyEphemeral;
|
||||||
public bool? BiometricLocked;
|
public bool? BiometricLocked;
|
||||||
|
[Obsolete("Jul 6 2023: Key has been deprecated. We will use the User Key in the future. It remains here for migration during app upgrade.")]
|
||||||
|
public SymmetricCryptoKey Key;
|
||||||
|
[Obsolete("Jul 6 2023: PinProtectedKey has been deprecated in favor of UserKeyPinEphemeral. It remains here for migration during app upgrade.")]
|
||||||
|
public EncString PinProtectedKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,4 +74,32 @@ namespace Bit.Core.Models.Domain
|
|||||||
public string EncKeyB64 { get; set; }
|
public string EncKeyB64 { get; set; }
|
||||||
public string MacKeyB64 { get; set; }
|
public string MacKeyB64 { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UserKey : SymmetricCryptoKey
|
||||||
|
{
|
||||||
|
public UserKey(byte[] key, EncryptionType? encType = null)
|
||||||
|
: base(key, encType)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MasterKey : SymmetricCryptoKey
|
||||||
|
{
|
||||||
|
public MasterKey(byte[] key, EncryptionType? encType = null)
|
||||||
|
: base(key, encType)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PinKey : SymmetricCryptoKey
|
||||||
|
{
|
||||||
|
public PinKey(byte[] key, EncryptionType? encType = null)
|
||||||
|
: base(key, encType)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrgKey : SymmetricCryptoKey
|
||||||
|
{
|
||||||
|
public OrgKey(byte[] key, EncryptionType? encType = null)
|
||||||
|
: base(key, encType)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
#region Key Connector
|
#region Key Connector
|
||||||
|
|
||||||
public async Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl)
|
public async Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl)
|
||||||
{
|
{
|
||||||
using (var requestMessage = new HttpRequestMessage())
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly bool _setCryptoKeys;
|
private readonly bool _setCryptoKeys;
|
||||||
|
|
||||||
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
||||||
private SymmetricCryptoKey _key;
|
private MasterKey _masterKey;
|
||||||
|
|
||||||
private string _authedUserId;
|
private string _authedUserId;
|
||||||
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
||||||
@@ -46,8 +46,6 @@ namespace Bit.Core.Services
|
|||||||
II18nService i18nService,
|
II18nService i18nService,
|
||||||
IPlatformUtilsService platformUtilsService,
|
IPlatformUtilsService platformUtilsService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IVaultTimeoutService vaultTimeoutService,
|
|
||||||
IKeyConnectorService keyConnectorService,
|
|
||||||
IPasswordGenerationService passwordGenerationService,
|
IPasswordGenerationService passwordGenerationService,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
bool setCryptoKeys = true)
|
bool setCryptoKeys = true)
|
||||||
@@ -61,7 +59,6 @@ namespace Bit.Core.Services
|
|||||||
_i18nService = i18nService;
|
_i18nService = i18nService;
|
||||||
_platformUtilsService = platformUtilsService;
|
_platformUtilsService = platformUtilsService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_keyConnectorService = keyConnectorService;
|
|
||||||
_passwordGenerationService = passwordGenerationService;
|
_passwordGenerationService = passwordGenerationService;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
_setCryptoKeys = setCryptoKeys;
|
_setCryptoKeys = setCryptoKeys;
|
||||||
@@ -145,8 +142,8 @@ namespace Bit.Core.Services
|
|||||||
SelectedTwoFactorProviderType = null;
|
SelectedTwoFactorProviderType = null;
|
||||||
_2faForcePasswordResetReason = null;
|
_2faForcePasswordResetReason = null;
|
||||||
var key = await MakePreloginKeyAsync(masterPassword, email);
|
var key = await MakePreloginKeyAsync(masterPassword, email);
|
||||||
var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key);
|
var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key);
|
||||||
var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization);
|
var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
var result = await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null, captchaToken);
|
var result = await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null, captchaToken);
|
||||||
|
|
||||||
if (await RequirePasswordChangeAsync(email, masterPassword))
|
if (await RequirePasswordChangeAsync(email, masterPassword))
|
||||||
@@ -199,7 +196,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
var decKey = await _cryptoService.RsaDecryptAsync(userKeyCiphered, decryptionKey);
|
var decKey = await _cryptoService.RsaDecryptAsync(userKeyCiphered, decryptionKey);
|
||||||
var decPasswordHash = await _cryptoService.RsaDecryptAsync(localHashedPasswordCiphered, decryptionKey);
|
var decPasswordHash = await _cryptoService.RsaDecryptAsync(localHashedPasswordCiphered, decryptionKey);
|
||||||
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decPasswordHash), null, null, null, new SymmetricCryptoKey(decKey), null, null,
|
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decPasswordHash), null, null, null, new MasterKey(decKey), null, null,
|
||||||
null, null, authRequestId: authRequestId);
|
null, null, authRequestId: authRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +213,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
CaptchaToken = captchaToken;
|
CaptchaToken = captchaToken;
|
||||||
}
|
}
|
||||||
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _key,
|
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _masterKey,
|
||||||
twoFactorProvider, twoFactorToken, remember, CaptchaToken, authRequestId: AuthRequestId);
|
twoFactorProvider, twoFactorToken, remember, CaptchaToken, authRequestId: AuthRequestId);
|
||||||
|
|
||||||
// If we successfully authenticated and we have a saved _2faForcePasswordResetReason reason from LogInAsync()
|
// If we successfully authenticated and we have a saved _2faForcePasswordResetReason reason from LogInAsync()
|
||||||
@@ -236,8 +233,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
SelectedTwoFactorProviderType = null;
|
SelectedTwoFactorProviderType = null;
|
||||||
var key = await MakePreloginKeyAsync(masterPassword, email);
|
var key = await MakePreloginKeyAsync(masterPassword, email);
|
||||||
var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key);
|
var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key);
|
||||||
var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization);
|
var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, twoFactorProvider,
|
return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, twoFactorProvider,
|
||||||
twoFactorToken, remember);
|
twoFactorToken, remember);
|
||||||
}
|
}
|
||||||
@@ -337,7 +334,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private async Task<SymmetricCryptoKey> MakePreloginKeyAsync(string masterPassword, string email)
|
private async Task<MasterKey> MakePreloginKeyAsync(string masterPassword, string email)
|
||||||
{
|
{
|
||||||
email = email.Trim().ToLower();
|
email = email.Trim().ToLower();
|
||||||
KdfConfig kdfConfig = KdfConfig.Default;
|
KdfConfig kdfConfig = KdfConfig.Default;
|
||||||
@@ -356,11 +353,11 @@ namespace Bit.Core.Services
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await _cryptoService.MakeKeyAsync(masterPassword, email, kdfConfig);
|
return await _cryptoService.MakeMasterKeyAsync(masterPassword, email, kdfConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||||
string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key,
|
string code, string codeVerifier, string redirectUrl, MasterKey masterKey,
|
||||||
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
||||||
string captchaToken = null, string orgId = null, string authRequestId = null)
|
string captchaToken = null, string orgId = null, string authRequestId = null)
|
||||||
{
|
{
|
||||||
@@ -426,7 +423,7 @@ namespace Bit.Core.Services
|
|||||||
Code = code;
|
Code = code;
|
||||||
CodeVerifier = codeVerifier;
|
CodeVerifier = codeVerifier;
|
||||||
SsoRedirectUrl = redirectUrl;
|
SsoRedirectUrl = redirectUrl;
|
||||||
_key = _setCryptoKeys ? key : null;
|
_masterKey = _setCryptoKeys ? masterKey : null;
|
||||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||||
@@ -470,24 +467,27 @@ namespace Bit.Core.Services
|
|||||||
_messagingService.Send("accountAdded");
|
_messagingService.Send("accountAdded");
|
||||||
if (_setCryptoKeys)
|
if (_setCryptoKeys)
|
||||||
{
|
{
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
await _cryptoService.SetKeyAsync(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localHashedPassword != null)
|
if (localHashedPassword != null)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyHashAsync(localHashedPassword);
|
await _cryptoService.SetMasterKeyHashAsync(localHashedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code == null || tokenResponse.Key != null)
|
if (code == null || tokenResponse.Key != null)
|
||||||
{
|
{
|
||||||
if (tokenResponse.KeyConnectorUrl != null)
|
if (tokenResponse.KeyConnectorUrl != null)
|
||||||
{
|
{
|
||||||
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
|
await _keyConnectorService.GetAndSetMasterKeyAsync(tokenResponse.KeyConnectorUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
||||||
|
|
||||||
|
if (masterKey != null)
|
||||||
|
{
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
|
}
|
||||||
|
|
||||||
// User doesn't have a key pair yet (old account), let's generate one for them.
|
// User doesn't have a key pair yet (old account), let's generate one for them.
|
||||||
if (tokenResponse.PrivateKey == null)
|
if (tokenResponse.PrivateKey == null)
|
||||||
@@ -505,19 +505,26 @@ namespace Bit.Core.Services
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
await _cryptoService.SetUserPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||||
}
|
}
|
||||||
else if (tokenResponse.KeyConnectorUrl != null)
|
else if (tokenResponse.KeyConnectorUrl != null)
|
||||||
{
|
{
|
||||||
// SSO Key Connector Onboarding
|
// SSO Key Connector Onboarding
|
||||||
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||||
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
|
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(
|
||||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
|
Convert.ToBase64String(password),
|
||||||
await _cryptoService.SetKeyAsync(k);
|
_tokenService.GetEmail(),
|
||||||
|
tokenResponse.KdfConfig);
|
||||||
|
|
||||||
var encKey = await _cryptoService.MakeEncKeyAsync(k);
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
|
||||||
var keyPair = await _cryptoService.MakeKeyPairAsync();
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
|
|
||||||
|
await _cryptoService.SetUserKeyAsync(newUserKey);
|
||||||
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||||
|
|
||||||
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -530,11 +537,11 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
var keys = new KeysRequest
|
var keys = new KeysRequest
|
||||||
{
|
{
|
||||||
PublicKey = keyPair.Item1,
|
PublicKey = newPublicKey,
|
||||||
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||||
};
|
};
|
||||||
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
encKey.Item2.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
newProtectedPrivateKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
||||||
);
|
);
|
||||||
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||||
}
|
}
|
||||||
@@ -549,7 +556,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
private void ClearState()
|
private void ClearState()
|
||||||
{
|
{
|
||||||
_key = null;
|
_masterKey = null;
|
||||||
Email = null;
|
Email = null;
|
||||||
CaptchaToken = null;
|
CaptchaToken = null;
|
||||||
MasterPasswordHash = null;
|
MasterPasswordHash = null;
|
||||||
@@ -590,7 +597,7 @@ namespace Bit.Core.Services
|
|||||||
public async Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved)
|
public async Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved)
|
||||||
{
|
{
|
||||||
var publicKey = CoreHelpers.Base64UrlDecode(pubKey);
|
var publicKey = CoreHelpers.Base64UrlDecode(pubKey);
|
||||||
var masterKey = await _cryptoService.GetKeyAsync();
|
var masterKey = await _cryptoService.GetMasterKeyAsync();
|
||||||
var encryptedKey = await _cryptoService.RsaEncryptAsync(masterKey.EncKey, publicKey);
|
var encryptedKey = await _cryptoService.RsaEncryptAsync(masterKey.EncKey, publicKey);
|
||||||
var encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(await _stateService.GetKeyHashAsync()), publicKey);
|
var encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(await _stateService.GetKeyHashAsync()), publicKey);
|
||||||
var deviceId = await _appIdService.GetAppIdAsync();
|
var deviceId = await _appIdService.GetAppIdAsync();
|
||||||
|
|||||||
@@ -249,8 +249,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var hashKey = await _cryptoService.HasKeyAsync();
|
if (!await _cryptoService.HasUserKeyAsync())
|
||||||
if (!hashKey)
|
|
||||||
{
|
{
|
||||||
throw new Exception("No key.");
|
throw new Exception("No key.");
|
||||||
}
|
}
|
||||||
@@ -557,9 +556,9 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)
|
public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)
|
||||||
{
|
{
|
||||||
var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId);
|
var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(cipher.OrganizationId);
|
||||||
var encFileName = await _cryptoService.EncryptAsync(filename, orgKey);
|
|
||||||
var (attachmentKey, orgEncAttachmentKey) = await _cryptoService.MakeEncKeyAsync(orgKey);
|
var encFileName = await _cryptoService.EncryptAsync(filename, encKey);
|
||||||
var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey);
|
var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey);
|
||||||
|
|
||||||
CipherResponse response;
|
CipherResponse response;
|
||||||
@@ -567,7 +566,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
var request = new AttachmentRequest
|
var request = new AttachmentRequest
|
||||||
{
|
{
|
||||||
Key = orgEncAttachmentKey.EncryptedString,
|
Key = protectedAttachmentKey.EncryptedString,
|
||||||
FileName = encFileName.EncryptedString,
|
FileName = encFileName.EncryptedString,
|
||||||
FileSize = encFileData.Buffer.Length,
|
FileSize = encFileData.Buffer.Length,
|
||||||
};
|
};
|
||||||
@@ -578,7 +577,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
catch (ApiException e) when (e.Error.StatusCode == System.Net.HttpStatusCode.NotFound || e.Error.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
|
catch (ApiException e) when (e.Error.StatusCode == System.Net.HttpStatusCode.NotFound || e.Error.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
|
||||||
{
|
{
|
||||||
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, orgEncAttachmentKey);
|
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, protectedAttachmentKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = await _stateService.GetActiveUserIdAsync();
|
var userId = await _stateService.GetActiveUserIdAsync();
|
||||||
@@ -796,6 +795,14 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
|
private async Task<Tuple<SymmetricCryptoKey, EncString, SymmetricCryptoKey>> MakeAttachmentKeyAsync(string organizationId)
|
||||||
|
{
|
||||||
|
var encryptionKey = await _cryptoService.GetOrgKeyAsync(organizationId)
|
||||||
|
?? (SymmetricCryptoKey)await _cryptoService.GetUserKeyWithLegacySupportAsync();
|
||||||
|
var (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(encryptionKey);
|
||||||
|
return new Tuple<SymmetricCryptoKey, EncString, SymmetricCryptoKey>(attachmentKey, protectedAttachmentKey, encryptionKey);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ShareAttachmentWithServerAsync(AttachmentView attachmentView, string cipherId,
|
private async Task ShareAttachmentWithServerAsync(AttachmentView attachmentView, string cipherId,
|
||||||
string organizationId)
|
string organizationId)
|
||||||
{
|
{
|
||||||
@@ -807,14 +814,16 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync();
|
var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync();
|
||||||
var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null);
|
var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null);
|
||||||
var key = await _cryptoService.GetOrgKeyAsync(organizationId);
|
|
||||||
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, key);
|
var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId);
|
||||||
var dataEncKey = await _cryptoService.MakeEncKeyAsync(key);
|
|
||||||
var encData = await _cryptoService.EncryptToBytesAsync(decBytes, dataEncKey.Item1);
|
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, encKey);
|
||||||
|
var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey);
|
||||||
|
|
||||||
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);
|
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);
|
||||||
var fd = new MultipartFormDataContent(boundary);
|
var fd = new MultipartFormDataContent(boundary);
|
||||||
fd.Add(new StringContent(dataEncKey.Item2.EncryptedString), "key");
|
fd.Add(new StringContent(protectedAttachmentKey.EncryptedString), "key");
|
||||||
fd.Add(new StreamContent(new MemoryStream(encData.Buffer)), "data", encFileName.EncryptedString);
|
fd.Add(new StreamContent(new MemoryStream(encFileData.Buffer)), "data", encFileName.EncryptedString);
|
||||||
await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId);
|
await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
return _decryptedCollectionCache;
|
return _decryptedCollectionCache;
|
||||||
}
|
}
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
throw new Exception("No key.");
|
throw new Exception("No key.");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
return _decryptedFolderCache;
|
return _decryptedFolderCache;
|
||||||
}
|
}
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
throw new Exception("No key.");
|
throw new Exception("No key.");
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ namespace Bit.Core.Services
|
|||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GetAndSetKey(string url)
|
public async Task GetAndSetMasterKeyAsync(string url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var userKeyResponse = await _apiService.GetUserKeyFromKeyConnector(url);
|
var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnectorAsync(url);
|
||||||
var keyArr = Convert.FromBase64String(userKeyResponse.Key);
|
var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key);
|
||||||
var k = new SymmetricCryptoKey(keyArr);
|
var masterKey = new MasterKey(masterKeyArr);
|
||||||
await _cryptoService.SetKeyAsync(k);
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -39,17 +39,17 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetUsesKeyConnector(bool usesKeyConnector)
|
public async Task SetUsesKeyConnectorAsync(bool usesKeyConnector)
|
||||||
{
|
{
|
||||||
await _stateService.SetUsesKeyConnectorAsync(usesKeyConnector);
|
await _stateService.SetUsesKeyConnectorAsync(usesKeyConnector);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> GetUsesKeyConnector()
|
public async Task<bool> GetUsesKeyConnectorAsync()
|
||||||
{
|
{
|
||||||
return await _stateService.GetUsesKeyConnectorAsync();
|
return await _stateService.GetUsesKeyConnectorAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Organization> GetManagingOrganization()
|
public async Task<Organization> GetManagingOrganizationAsync()
|
||||||
{
|
{
|
||||||
var orgs = await _organizationService.GetAllAsync();
|
var orgs = await _organizationService.GetAllAsync();
|
||||||
return orgs.Find(o =>
|
return orgs.Find(o =>
|
||||||
@@ -57,14 +57,14 @@ namespace Bit.Core.Services
|
|||||||
!o.IsAdmin);
|
!o.IsAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MigrateUser()
|
public async Task MigrateUserAsync()
|
||||||
{
|
{
|
||||||
var organization = await GetManagingOrganization();
|
var organization = await GetManagingOrganizationAsync();
|
||||||
var key = await _cryptoService.GetKeyAsync();
|
var masterKey = await _cryptoService.GetMasterKeyAsync();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(key.EncKeyB64);
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.EncKeyB64);
|
||||||
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -75,11 +75,11 @@ namespace Bit.Core.Services
|
|||||||
await _apiService.PostConvertToKeyConnector();
|
await _apiService.PostConvertToKeyConnector();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UserNeedsMigration()
|
public async Task<bool> UserNeedsMigrationAsync()
|
||||||
{
|
{
|
||||||
var loggedInUsingSso = await _tokenService.GetIsExternal();
|
var loggedInUsingSso = await _tokenService.GetIsExternal();
|
||||||
var requiredByOrganization = await GetManagingOrganization() != null;
|
var requiredByOrganization = await GetManagingOrganizationAsync() != null;
|
||||||
var userIsNotUsingKeyConnector = !await GetUsesKeyConnector();
|
var userIsNotUsingKeyConnector = !await GetUsesKeyConnectorAsync();
|
||||||
|
|
||||||
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
|
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<List<GeneratedPasswordHistory>> GetHistoryAsync()
|
public async Task<List<GeneratedPasswordHistory>> GetHistoryAsync()
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
return new List<GeneratedPasswordHistory>();
|
return new List<GeneratedPasswordHistory>();
|
||||||
@@ -262,7 +262,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken))
|
public async Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ namespace Bit.Core.Services
|
|||||||
return _decryptedSendsCache;
|
return _decryptedSendsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
throw new Exception("No Key.");
|
throw new Exception("No Key.");
|
||||||
|
|||||||
@@ -241,6 +241,19 @@ namespace Bit.Core.Services
|
|||||||
))?.Settings?.EnvironmentUrls;
|
))?.Settings?.EnvironmentUrls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> GetUserKeyBiometricUnlockAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var keyB64 = await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyBiometricUnlockKey, userId), true);
|
||||||
|
return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserKeyBiometricUnlockAsync(UserKey value, string userId = null)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyBiometricUnlockKey, userId), value?.KeyB64, true);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool?> GetBiometricUnlockAsync(string userId = null)
|
public async Task<bool?> GetBiometricUnlockAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
@@ -302,12 +315,70 @@ namespace Bit.Core.Services
|
|||||||
true, reconciledOptions);
|
true, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> GetUserKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return (await GetAccountAsync(
|
||||||
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||||
|
))?.VolatileData?.UserKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserKeyAsync(UserKey value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultInMemoryOptionsAsync());
|
||||||
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
|
account.VolatileData.UserKey = value;
|
||||||
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MasterKey> GetMasterKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return (await GetAccountAsync(
|
||||||
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||||
|
))?.VolatileData?.MasterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetMasterKeyAsync(MasterKey value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultInMemoryOptionsAsync());
|
||||||
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
|
account.VolatileData.MasterKey = value;
|
||||||
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var keyB64 = await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), true);
|
||||||
|
return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value?.KeyB64, true);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||||
{
|
{
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
userId = await GetActiveUserIdAsync();
|
userId = await GetActiveUserIdAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await IsAuthenticatedAsync(userId))
|
if (!await IsAuthenticatedAsync(userId))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -353,36 +424,36 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions);
|
await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetPinProtectedAsync(string userId = null)
|
public async Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var key = await _storageMediatorService.GetAsync<string>(
|
||||||
await GetDefaultStorageOptionsAsync());
|
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), false);
|
||||||
return await GetValueAsync<string>(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions);
|
return key != null ? new EncString(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetPinProtectedAsync(string value, string userId = null)
|
public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
await _storageMediatorService.SaveAsync(
|
||||||
await GetDefaultStorageOptionsAsync());
|
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), value?.EncryptedString, false);
|
||||||
await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EncString> GetPinProtectedKeyAsync(string userId = null)
|
public async Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null)
|
||||||
{
|
{
|
||||||
return (await GetAccountAsync(
|
return (await GetAccountAsync(
|
||||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||||
))?.VolatileData?.PinProtectedKey;
|
))?.VolatileData?.PinKeyEncryptedUserKeyEphemeral;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetPinProtectedKeyAsync(EncString value, string userId = null)
|
public async Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
await GetDefaultInMemoryOptionsAsync());
|
await GetDefaultInMemoryOptionsAsync());
|
||||||
var account = await GetAccountAsync(reconciledOptions);
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
account.VolatileData.PinProtectedKey = value;
|
account.VolatileData.PinKeyEncryptedUserKeyEphemeral = value;
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null)
|
public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
@@ -395,35 +466,6 @@ namespace Bit.Core.Services
|
|||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetKeyEncryptedAsync(string userId = null)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultSecureStorageOptionsAsync());
|
|
||||||
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetKeyEncryptedAsync(string value, string userId)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultSecureStorageOptionsAsync());
|
|
||||||
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
|
||||||
{
|
|
||||||
return (await GetAccountAsync(
|
|
||||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
|
||||||
))?.VolatileData?.Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultInMemoryOptionsAsync());
|
|
||||||
var account = await GetAccountAsync(reconciledOptions);
|
|
||||||
account.VolatileData.Key = value;
|
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetKeyHashAsync(string userId = null)
|
public async Task<string> GetKeyHashAsync(string userId = null)
|
||||||
{
|
{
|
||||||
@@ -439,19 +481,6 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.KeyHashKey(reconciledOptions.UserId), value, reconciledOptions);
|
await SetValueAsync(Constants.KeyHashKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultStorageOptionsAsync());
|
|
||||||
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetEncKeyEncryptedAsync(string value, string userId)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultStorageOptionsAsync());
|
|
||||||
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null)
|
public async Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null)
|
||||||
{
|
{
|
||||||
@@ -1447,28 +1476,31 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Non-state storage
|
// Non-state storage
|
||||||
await SetProtectedPinAsync(null, userId);
|
await Task.WhenAll(
|
||||||
await SetPinProtectedAsync(null, userId);
|
SetUserKeyAutoUnlockAsync(null, userId),
|
||||||
await SetKeyEncryptedAsync(null, userId);
|
SetUserKeyBiometricUnlockAsync(null, userId),
|
||||||
await SetKeyHashAsync(null, userId);
|
SetProtectedPinAsync(null, userId),
|
||||||
await SetEncKeyEncryptedAsync(null, userId);
|
SetKeyHashAsync(null, userId),
|
||||||
await SetOrgKeysEncryptedAsync(null, userId);
|
SetOrgKeysEncryptedAsync(null, userId),
|
||||||
await SetPrivateKeyEncryptedAsync(null, userId);
|
SetPrivateKeyEncryptedAsync(null, userId),
|
||||||
await SetLastActiveTimeAsync(null, userId);
|
SetLastActiveTimeAsync(null, userId),
|
||||||
await SetPreviousPageInfoAsync(null, userId);
|
SetPreviousPageInfoAsync(null, userId),
|
||||||
await SetInvalidUnlockAttemptsAsync(null, userId);
|
SetInvalidUnlockAttemptsAsync(null, userId),
|
||||||
await SetLocalDataAsync(null, userId);
|
SetLocalDataAsync(null, userId),
|
||||||
await SetEncryptedCiphersAsync(null, userId);
|
SetEncryptedCiphersAsync(null, userId),
|
||||||
await SetEncryptedCollectionsAsync(null, userId);
|
SetEncryptedCollectionsAsync(null, userId),
|
||||||
await SetLastSyncAsync(null, userId);
|
SetLastSyncAsync(null, userId),
|
||||||
await SetEncryptedFoldersAsync(null, userId);
|
SetEncryptedFoldersAsync(null, userId),
|
||||||
await SetEncryptedPoliciesAsync(null, userId);
|
SetEncryptedPoliciesAsync(null, userId),
|
||||||
await SetUsesKeyConnectorAsync(null, userId);
|
SetUsesKeyConnectorAsync(null, userId),
|
||||||
await SetOrganizationsAsync(null, userId);
|
SetOrganizationsAsync(null, userId),
|
||||||
await SetEncryptedPasswordGenerationHistoryAsync(null, userId);
|
SetEncryptedPasswordGenerationHistoryAsync(null, userId),
|
||||||
await SetEncryptedSendsAsync(null, userId);
|
SetEncryptedSendsAsync(null, userId),
|
||||||
await SetSettingsAsync(null, userId);
|
SetSettingsAsync(null, userId),
|
||||||
await SetApprovePasswordlessLoginsAsync(null, userId);
|
SetApprovePasswordlessLoginsAsync(null, userId),
|
||||||
|
SetEncKeyEncryptedAsync(null, userId),
|
||||||
|
SetKeyEncryptedAsync(null, userId),
|
||||||
|
SetPinProtectedAsync(null, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScaffoldNewAccountAsync(Account account)
|
private async Task ScaffoldNewAccountAsync(Account account)
|
||||||
@@ -1656,5 +1688,79 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.LastUserShouldConnectToWatchKey,
|
await SetValueAsync(Constants.LastUserShouldConnectToWatchKey,
|
||||||
shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync());
|
shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
public async Task<string> GetPinProtectedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
return await GetValueAsync<string>(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead")]
|
||||||
|
public async Task SetPinProtectedAsync(string value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")]
|
||||||
|
public async Task<EncString> GetPinProtectedKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return (await GetAccountAsync(
|
||||||
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||||
|
))?.VolatileData?.PinProtectedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead")]
|
||||||
|
public async Task SetPinProtectedKeyAsync(EncString value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultInMemoryOptionsAsync());
|
||||||
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
|
account.VolatileData.PinProtectedKey = value;
|
||||||
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
public async Task SetEncKeyEncryptedAsync(string value, string userId)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Left for migration purposes")]
|
||||||
|
public async Task SetKeyEncryptedAsync(string value, string userId)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultSecureStorageOptionsAsync());
|
||||||
|
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")]
|
||||||
|
public async Task<string> GetKeyEncryptedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultSecureStorageOptionsAsync());
|
||||||
|
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
||||||
|
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return (await GetAccountAsync(
|
||||||
|
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||||
|
))?.VolatileData?.Key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,8 +327,8 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _cryptoService.SetEncKeyAsync(response.Key);
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key);
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(response.PrivateKey);
|
await _cryptoService.SetUserPrivateKeyAsync(response.PrivateKey);
|
||||||
await _cryptoService.SetOrgKeysAsync(response.Organizations);
|
await _cryptoService.SetOrgKeysAsync(response.Organizations);
|
||||||
await _stateService.SetSecurityStampAsync(response.SecurityStamp);
|
await _stateService.SetSecurityStampAsync(response.SecurityStamp);
|
||||||
var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o));
|
var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o));
|
||||||
@@ -337,7 +337,7 @@ namespace Bit.Core.Services
|
|||||||
await _stateService.SetNameAsync(response.Name);
|
await _stateService.SetNameAsync(response.Name);
|
||||||
await _stateService.SetPersonalPremiumAsync(response.Premium);
|
await _stateService.SetPersonalPremiumAsync(response.Premium);
|
||||||
await _stateService.SetAvatarColorAsync(response.AvatarColor);
|
await _stateService.SetAvatarColorAsync(response.AvatarColor);
|
||||||
await _keyConnectorService.SetUsesKeyConnector(response.UsesKeyConnector);
|
await _keyConnectorService.SetUsesKeyConnectorAsync(response.UsesKeyConnector);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
|
public enum PinLockType
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Persistent,
|
||||||
|
Transient
|
||||||
|
}
|
||||||
|
|
||||||
public class VaultTimeoutService : IVaultTimeoutService
|
public class VaultTimeoutService : IVaultTimeoutService
|
||||||
{
|
{
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
@@ -54,15 +59,26 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<bool> IsLockedAsync(string userId = null)
|
public async Task<bool> IsLockedAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync(userId);
|
var biometricSet = await IsBiometricLockSetAsync(userId);
|
||||||
if (hasKey)
|
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
||||||
{
|
{
|
||||||
var biometricSet = await IsBiometricLockSetAsync(userId);
|
return true;
|
||||||
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
}
|
||||||
|
|
||||||
|
if (!await _cryptoService.HasUserKeyAsync(userId))
|
||||||
|
{
|
||||||
|
if (await _cryptoService.HasAutoUnlockKeyAsync(userId))
|
||||||
|
{
|
||||||
|
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check again to verify auto key was set
|
||||||
|
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
|
||||||
return !hasKey;
|
return !hasKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,13 +179,15 @@ namespace Bit.Core.Services
|
|||||||
userId = await _stateService.GetActiveUserIdAsync();
|
userId = await _stateService.GetActiveUserIdAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await _keyConnectorService.GetUsesKeyConnector())
|
if (await _keyConnectorService.GetUsesKeyConnectorAsync())
|
||||||
{
|
{
|
||||||
var (isPinProtected, isPinProtectedWithKey) = await IsPinLockSetAsync(userId);
|
var pinStatus = await GetPinLockTypeAsync(userId);
|
||||||
var pinLock = (isPinProtected && await _stateService.GetPinProtectedKeyAsync(userId) != null) ||
|
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
|
||||||
isPinProtectedWithKey;
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
|
||||||
|
pinStatus == PinLockType.Persistent;
|
||||||
|
|
||||||
if (!pinLock && !await IsBiometricLockSetAsync())
|
if (!pinEnabled && !await IsBiometricLockSetAsync())
|
||||||
{
|
{
|
||||||
await LogOutAsync(userInitiated, userId);
|
await LogOutAsync(userInitiated, userId);
|
||||||
return;
|
return;
|
||||||
@@ -187,10 +205,11 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Task.WhenAll(
|
await Task.WhenAll(
|
||||||
_cryptoService.ClearKeyAsync(userId),
|
_cryptoService.ClearUserKeyAsync(userId),
|
||||||
|
_cryptoService.ClearMasterKeyAsync(userId),
|
||||||
|
_stateService.SetUserKeyAutoUnlockAsync(null, userId),
|
||||||
_cryptoService.ClearOrgKeysAsync(true, userId),
|
_cryptoService.ClearOrgKeysAsync(true, userId),
|
||||||
_cryptoService.ClearKeyPairAsync(true, userId),
|
_cryptoService.ClearKeyPairAsync(true, userId));
|
||||||
_cryptoService.ClearEncKeyAsync(true, userId));
|
|
||||||
|
|
||||||
if (isActiveAccount)
|
if (isActiveAccount)
|
||||||
{
|
{
|
||||||
@@ -214,15 +233,27 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
await _stateService.SetVaultTimeoutAsync(timeout);
|
await _stateService.SetVaultTimeoutAsync(timeout);
|
||||||
await _stateService.SetVaultTimeoutActionAsync(action);
|
await _stateService.SetVaultTimeoutActionAsync(action);
|
||||||
await _cryptoService.ToggleKeyAsync();
|
await _cryptoService.RefreshKeysAsync();
|
||||||
await _tokenService.ToggleTokensAsync();
|
await _tokenService.ToggleTokensAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null)
|
public async Task<PinLockType> GetPinLockTypeAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync(userId);
|
// we can't depend on only the protected pin being set because old
|
||||||
var pinProtectedKey = await _stateService.GetPinProtectedAsync(userId);
|
// versions only used it for MP on Restart
|
||||||
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null;
|
||||||
|
var hasUserKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null;
|
||||||
|
var hasOldUserKeyPin = await _stateService.GetPinProtectedAsync(userId) != null;
|
||||||
|
|
||||||
|
if (hasUserKeyPin || hasOldUserKeyPin)
|
||||||
|
{
|
||||||
|
return PinLockType.Persistent;
|
||||||
|
}
|
||||||
|
else if (isPinEnabled && !hasUserKeyPin && !hasOldUserKeyPin)
|
||||||
|
{
|
||||||
|
return PinLockType.Transient;
|
||||||
|
}
|
||||||
|
return PinLockType.Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
|
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
|
||||||
@@ -233,8 +264,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task ClearAsync(string userId = null)
|
public async Task ClearAsync(string userId = null)
|
||||||
{
|
{
|
||||||
await _stateService.SetPinProtectedKeyAsync(null, userId);
|
await _cryptoService.ClearPinKeysAsync(userId);
|
||||||
await _stateService.SetProtectedPinAsync(null, userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int?> GetVaultTimeout(string userId = null)
|
public async Task<int?> GetVaultTimeout(string userId = null)
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ namespace Bit.Core.Utilities
|
|||||||
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
|
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
|
||||||
var totpService = new TotpService(cryptoFunctionService);
|
var totpService = new TotpService(cryptoFunctionService);
|
||||||
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
|
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
|
||||||
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
|
tokenService, appIdService, i18nService, platformUtilsService, messagingService,
|
||||||
keyConnectorService, passwordGenerationService, policyService);
|
passwordGenerationService, policyService);
|
||||||
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
||||||
var auditService = new AuditService(cryptoFunctionService, apiService);
|
var auditService = new AuditService(cryptoFunctionService, apiService);
|
||||||
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.iOS.Core.Utilities;
|
using Bit.iOS.Core.Utilities;
|
||||||
using Bit.iOS.Core.Views;
|
using Bit.iOS.Core.Views;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Controllers
|
namespace Bit.iOS.Core.Controllers
|
||||||
@@ -28,10 +30,9 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private IBiometricService _biometricService;
|
private IBiometricService _biometricService;
|
||||||
private IKeyConnectorService _keyConnectorService;
|
private IKeyConnectorService _keyConnectorService;
|
||||||
private IAccountsManager _accountManager;
|
private IAccountsManager _accountManager;
|
||||||
private bool _isPinProtected;
|
private PinLockType _pinStatus;
|
||||||
private bool _isPinProtectedWithKey;
|
private bool _pinEnabled;
|
||||||
private bool _pinLock;
|
private bool _biometricEnabled;
|
||||||
private bool _biometricLock;
|
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _passwordReprompt = false;
|
private bool _passwordReprompt = false;
|
||||||
private bool _usesKeyConnector;
|
private bool _usesKeyConnector;
|
||||||
@@ -85,7 +86,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract UITableView TableView { get; }
|
public abstract UITableView TableView { get; }
|
||||||
|
|
||||||
public override async void ViewDidLoad()
|
public override async void ViewDidLoad()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
@@ -103,25 +104,28 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
||||||
{
|
{
|
||||||
_passwordReprompt = true;
|
_passwordReprompt = true;
|
||||||
_isPinProtected = false;
|
_pinStatus = PinLockType.Disabled;
|
||||||
_isPinProtectedWithKey = false;
|
_pinEnabled = false;
|
||||||
_pinLock = false;
|
_biometricEnabled = false;
|
||||||
_biometricLock = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
|
||||||
_pinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
await _cryptoService.HasKeyAsync();
|
_pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockType.Persistent;
|
||||||
|
|
||||||
|
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
|
||||||
|
&& await _cryptoService.HasEncryptedUserKeyAsync();
|
||||||
_biometricIntegrityValid =
|
_biometricIntegrityValid =
|
||||||
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
BaseNavItem.Title = AppResources.VerifyPIN;
|
BaseNavItem.Title = AppResources.VerifyPIN;
|
||||||
}
|
}
|
||||||
@@ -150,7 +154,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
if (!_biometricUnlockOnly)
|
if (!_biometricUnlockOnly)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.Label.Text = _pinLock ? AppResources.PIN : AppResources.MasterPassword;
|
MasterPasswordCell.Label.Text = _pinEnabled ? AppResources.PIN : AppResources.MasterPassword;
|
||||||
MasterPasswordCell.TextField.SecureTextEntry = true;
|
MasterPasswordCell.TextField.SecureTextEntry = true;
|
||||||
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
||||||
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
||||||
@@ -158,7 +162,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
CheckPasswordAsync().FireAndForget();
|
CheckPasswordAsync().FireAndForget();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
||||||
}
|
}
|
||||||
@@ -177,7 +181,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
|
|
||||||
if (_biometricLock)
|
if (_biometricEnabled)
|
||||||
{
|
{
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
@@ -198,18 +202,18 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
if (_usesKeyConnector)
|
if (_usesKeyConnector)
|
||||||
{
|
{
|
||||||
if (!(_pinLock || _biometricLock) ||
|
if (!(_pinEnabled || _biometricEnabled) ||
|
||||||
(_biometricLock && !_biometricIntegrityValid))
|
(_biometricEnabled && !_biometricIntegrityValid))
|
||||||
{
|
{
|
||||||
PromptSSO();
|
PromptSSO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_biometricLock || !_biometricIntegrityValid)
|
else if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.BecomeFirstResponder();
|
MasterPasswordCell.TextField.BecomeFirstResponder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CheckPasswordAsync()
|
protected async Task CheckPasswordAsync()
|
||||||
{
|
{
|
||||||
if (_checkingPassword)
|
if (_checkingPassword)
|
||||||
@@ -224,7 +228,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired,
|
string.Format(AppResources.ValidationFieldRequired,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
return;
|
return;
|
||||||
@@ -246,33 +250,53 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockType.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockType.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockType.Transient,
|
||||||
|
inputtedValue,
|
||||||
|
email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
failed = decPin != inputtedValue;
|
|
||||||
if (!failed)
|
|
||||||
{
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
kdfConfig);
|
inputtedValue,
|
||||||
failed = false;
|
email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != inputtedValue;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -286,33 +310,27 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedPasswordHash == null)
|
||||||
{
|
{
|
||||||
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
||||||
if (key2.KeyB64 == oldKey)
|
if (masterKey.KeyB64 == oldKey)
|
||||||
{
|
{
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
await _cryptoService.SetMasterKeyHashAsync(localPasswordHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey);
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
|
||||||
{
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
|
||||||
kdfConfig);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
|
||||||
}
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetKeyAndContinueAsync(userKey, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -339,12 +357,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
if (!_biometricLock || !_biometricIntegrityValid)
|
if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
@@ -371,12 +389,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
PresentViewController(loginController, true, null);
|
PresentViewController(loginController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key, bool masterPassword = false)
|
private async Task SetKeyAndContinueAsync(UserKey userKey, bool masterPassword = false)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
}
|
}
|
||||||
DoContinue(masterPassword);
|
DoContinue(masterPassword);
|
||||||
}
|
}
|
||||||
@@ -396,7 +414,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private async Task EnableBiometricsIfNeeded()
|
private async Task EnableBiometricsIfNeeded()
|
||||||
{
|
{
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricEnabled & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
@@ -405,7 +423,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private void InvalidValue()
|
private void InvalidValue()
|
||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(null, _pinLock ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
string.Format(null, _pinEnabled ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
||||||
AppResources.Ok, (a) =>
|
AppResources.Ok, (a) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -490,7 +508,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!controller._biometricUnlockOnly && controller._biometricLock) ||
|
return (!controller._biometricUnlockOnly && controller._biometricEnabled) ||
|
||||||
controller._passwordReprompt
|
controller._passwordReprompt
|
||||||
? 2
|
? 2
|
||||||
: 1;
|
: 1;
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using UIKit;
|
|
||||||
using Foundation;
|
|
||||||
using Bit.iOS.Core.Views;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.iOS.Core.Utilities;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core.Models.Domain;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.App.Pages;
|
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Pages;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.iOS.Core.Utilities;
|
||||||
|
using Bit.iOS.Core.Views;
|
||||||
|
using Foundation;
|
||||||
|
using UIKit;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Controllers
|
namespace Bit.iOS.Core.Controllers
|
||||||
@@ -29,10 +30,9 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private IPlatformUtilsService _platformUtilsService;
|
private IPlatformUtilsService _platformUtilsService;
|
||||||
private IBiometricService _biometricService;
|
private IBiometricService _biometricService;
|
||||||
private IKeyConnectorService _keyConnectorService;
|
private IKeyConnectorService _keyConnectorService;
|
||||||
private bool _isPinProtected;
|
private PinLockType _pinStatus;
|
||||||
private bool _isPinProtectedWithKey;
|
private bool _pinEnabled;
|
||||||
private bool _pinLock;
|
private bool _biometricEnabled;
|
||||||
private bool _biometricLock;
|
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _passwordReprompt = false;
|
private bool _passwordReprompt = false;
|
||||||
private bool _usesKeyConnector;
|
private bool _usesKeyConnector;
|
||||||
@@ -96,25 +96,28 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
||||||
{
|
{
|
||||||
_passwordReprompt = true;
|
_passwordReprompt = true;
|
||||||
_isPinProtected = false;
|
_pinStatus = PinLockType.Disabled;
|
||||||
_isPinProtectedWithKey = false;
|
_pinEnabled = false;
|
||||||
_pinLock = false;
|
_biometricEnabled = false;
|
||||||
_biometricLock = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
|
||||||
_pinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
await _cryptoService.HasKeyAsync();
|
_pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockType.Persistent;
|
||||||
|
|
||||||
|
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
|
||||||
|
&& await _cryptoService.HasEncryptedUserKeyAsync();
|
||||||
_biometricIntegrityValid =
|
_biometricIntegrityValid =
|
||||||
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
BaseNavItem.Title = AppResources.VerifyPIN;
|
BaseNavItem.Title = AppResources.VerifyPIN;
|
||||||
}
|
}
|
||||||
@@ -126,7 +129,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
{
|
{
|
||||||
BaseNavItem.Title = AppResources.VerifyMasterPassword;
|
BaseNavItem.Title = AppResources.VerifyMasterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseCancelButton.Title = AppResources.Cancel;
|
BaseCancelButton.Title = AppResources.Cancel;
|
||||||
|
|
||||||
if (_biometricUnlockOnly)
|
if (_biometricUnlockOnly)
|
||||||
@@ -143,7 +146,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
if (!_biometricUnlockOnly)
|
if (!_biometricUnlockOnly)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.Label.Text = _pinLock ? AppResources.PIN : AppResources.MasterPassword;
|
MasterPasswordCell.Label.Text = _pinEnabled ? AppResources.PIN : AppResources.MasterPassword;
|
||||||
MasterPasswordCell.TextField.SecureTextEntry = true;
|
MasterPasswordCell.TextField.SecureTextEntry = true;
|
||||||
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
||||||
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
||||||
@@ -151,7 +154,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
CheckPasswordAsync().GetAwaiter().GetResult();
|
CheckPasswordAsync().GetAwaiter().GetResult();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
|
|
||||||
if (_biometricLock)
|
if (_biometricEnabled)
|
||||||
{
|
{
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
@@ -186,13 +189,13 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
if (_usesKeyConnector)
|
if (_usesKeyConnector)
|
||||||
{
|
{
|
||||||
if (!(_pinLock || _biometricLock) ||
|
if (!(_pinEnabled || _biometricEnabled) ||
|
||||||
(_biometricLock && !_biometricIntegrityValid))
|
(_biometricEnabled && !_biometricIntegrityValid))
|
||||||
{
|
{
|
||||||
PromptSSO();
|
PromptSSO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_biometricLock || !_biometricIntegrityValid)
|
else if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.BecomeFirstResponder();
|
MasterPasswordCell.TextField.BecomeFirstResponder();
|
||||||
}
|
}
|
||||||
@@ -204,7 +207,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired,
|
string.Format(AppResources.ValidationFieldRequired,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
return;
|
return;
|
||||||
@@ -214,33 +217,53 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var inputtedValue = MasterPasswordCell.TextField.Text;
|
var inputtedValue = MasterPasswordCell.TextField.Text;
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockType.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockType.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockType.Transient,
|
||||||
|
inputtedValue,
|
||||||
|
email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
failed = decPin != inputtedValue;
|
|
||||||
if (!failed)
|
|
||||||
{
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
kdfConfig);
|
inputtedValue,
|
||||||
failed = false;
|
email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != inputtedValue;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -260,33 +283,27 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedPasswordHash == null)
|
||||||
{
|
{
|
||||||
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
||||||
if (key2.KeyB64 == oldKey)
|
if (masterKey.KeyB64 == oldKey)
|
||||||
{
|
{
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
await _cryptoService.SetMasterKeyHashAsync(localPasswordHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey);
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
|
||||||
{
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
|
||||||
kdfConfig);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
|
||||||
}
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetKeyAndContinueAsync(userKey, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -303,12 +320,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
if (!_biometricLock || !_biometricIntegrityValid)
|
if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
@@ -335,12 +352,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
PresentViewController(loginController, true, null);
|
PresentViewController(loginController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key, bool masterPassword = false)
|
private async Task SetKeyAndContinueAsync(UserKey userKey, bool masterPassword = false)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
}
|
}
|
||||||
DoContinue(masterPassword);
|
DoContinue(masterPassword);
|
||||||
}
|
}
|
||||||
@@ -360,7 +377,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private async Task EnableBiometricsIfNeeded()
|
private async Task EnableBiometricsIfNeeded()
|
||||||
{
|
{
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricEnabled & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
@@ -369,7 +386,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private void InvalidValue()
|
private void InvalidValue()
|
||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(null, _pinLock ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
string.Format(null, _pinEnabled ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
||||||
AppResources.Ok, (a) =>
|
AppResources.Ok, (a) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -378,7 +395,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
});
|
});
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogOutAsync()
|
private async Task LogOutAsync()
|
||||||
{
|
{
|
||||||
await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync());
|
await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync());
|
||||||
@@ -444,7 +461,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public override nint NumberOfSections(UITableView tableView)
|
public override nint NumberOfSections(UITableView tableView)
|
||||||
{
|
{
|
||||||
return (!_controller._biometricUnlockOnly && _controller._biometricLock) ||
|
return (!_controller._biometricUnlockOnly && _controller._biometricEnabled) ||
|
||||||
_controller._passwordReprompt
|
_controller._passwordReprompt
|
||||||
? 2
|
? 2
|
||||||
: 1;
|
: 1;
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Services;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using LocalAuthentication;
|
using LocalAuthentication;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Services
|
namespace Bit.iOS.Core.Services
|
||||||
{
|
{
|
||||||
public class BiometricService : IBiometricService
|
public class BiometricService : BaseBiometricService
|
||||||
{
|
{
|
||||||
private IStateService _stateService;
|
public BiometricService(IStateService stateService, ICryptoService cryptoService)
|
||||||
|
: base(stateService, cryptoService)
|
||||||
public BiometricService(IStateService stateService)
|
|
||||||
{
|
{
|
||||||
_stateService = stateService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
public override async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
if (bioIntegritySrcKey == null)
|
if (bioIntegritySrcKey == null)
|
||||||
{
|
{
|
||||||
@@ -30,7 +29,7 @@ namespace Bit.iOS.Core.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
public override async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
var state = GetState();
|
var state = GetState();
|
||||||
if (state == null)
|
if (state == null)
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ namespace Bit.iOS.Core.Utilities
|
|||||||
var clipboardService = new ClipboardService(stateService);
|
var clipboardService = new ClipboardService(stateService);
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||||
messagingService, broadcasterService);
|
messagingService, broadcasterService);
|
||||||
var biometricService = new BiometricService(stateService);
|
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
|
var biometricService = new BiometricService(stateService, cryptoService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
||||||
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);
|
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace Bit.Core.Test.Services
|
|||||||
.Returns(encFileName);
|
.Returns(encFileName);
|
||||||
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
||||||
.Returns(data);
|
.Returns(data);
|
||||||
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
||||||
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
||||||
.Returns(uploadDataResponse);
|
.Returns(uploadDataResponse);
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ namespace Bit.Core.Test.Services
|
|||||||
.Returns(new EncString(fileName));
|
.Returns(new EncString(fileName));
|
||||||
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
||||||
.Returns(data);
|
.Returns(data);
|
||||||
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
||||||
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
||||||
.Throws(new ApiException(new ErrorResponse { StatusCode = statusCode }));
|
.Throws(new ApiException(new ErrorResponse { StatusCode = statusCode }));
|
||||||
sutProvider.GetDependency<IApiService>().PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any<MultipartFormDataContent>())
|
sutProvider.GetDependency<IApiService>().PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any<MultipartFormDataContent>())
|
||||||
@@ -70,7 +70,7 @@ namespace Bit.Core.Test.Services
|
|||||||
.Returns(new EncString(fileName));
|
.Returns(new EncString(fileName));
|
||||||
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
|
||||||
.Returns(data);
|
.Returns(data);
|
||||||
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>())
|
sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>())
|
||||||
.Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
.Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
|
||||||
var expectedException = new ApiException(new ErrorResponse { StatusCode = HttpStatusCode.BadRequest });
|
var expectedException = new ApiException(new ErrorResponse { StatusCode = HttpStatusCode.BadRequest });
|
||||||
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ namespace Bit.Core.Test.Services
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var sendDataDict = sendDatas.ToDictionary(d => d.Id, d => d);
|
var sendDataDict = sendDatas.ToDictionary(d => d.Id, d => d);
|
||||||
sutProvider.GetDependency<ICryptoService>().HasKeyAsync().Returns(true);
|
sutProvider.GetDependency<ICryptoService>().HasUserKeyAsync().Returns(true);
|
||||||
ServiceContainer.Register("cryptoService", sutProvider.GetDependency<ICryptoService>());
|
ServiceContainer.Register("cryptoService", sutProvider.GetDependency<ICryptoService>());
|
||||||
sutProvider.GetDependency<II18nService>().StringComparer.Returns(StringComparer.CurrentCulture);
|
sutProvider.GetDependency<II18nService>().StringComparer.Returns(StringComparer.CurrentCulture);
|
||||||
sutProvider.GetDependency<IStateService>().GetActiveUserIdAsync().Returns(userId);
|
sutProvider.GetDependency<IStateService>().GetActiveUserIdAsync().Returns(userId);
|
||||||
|
|||||||
Reference in New Issue
Block a user