1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

33 Commits

Author SHA1 Message Date
Federico Maccaroni
fc2a91c435 PM-3194 Fix biometrics button to be shown on upgrade when no UserKey is present yet 2023-08-07 11:48:21 -03:00
Jacob Fink
4e1361e94a renaming bio key and fix build 2023-08-07 09:59:23 -04:00
Jacob Fink
d6567fe819 Merge branch 'auth/pm-2713/drop-master-key-dependency' into add-bio-key-migration 2023-08-07 09:57:53 -04:00
Federico Maccaroni
bf749d39de PM-2713 Fix auto-migrating EncKeyEncrypted into MasterKey encrypted UserKey when requesting DecryptUserKeyWithMasterKeyAsync is called 2023-08-04 18:39:16 -03:00
Jacob Fink
2522bbc60f Get UserKey from bio state on unlock 2023-08-04 16:34:27 -04:00
Jacob Fink
b620a9c09f add state for biometric key and accept UserKey instead of string for auto key 2023-08-04 16:20:39 -04:00
Jacob Fink
903f099134 [PM-2713] rename ephemeral pin key 2023-08-04 15:58:04 -04:00
Jacob Fink
fd5ef49811 [PM-2713] add async to key connector service methods 2023-08-04 13:38:04 -04:00
Jacob Fink
78004dbdb9 [PM-2713] clear service cache when adding new account 2023-08-03 16:28:04 -04:00
Jacob Fink
e820408a64 [PM-2713] make method async again
- returning null from a task thats not async throws
2023-08-03 15:44:41 -04:00
Jacob Fink
c595b1626e fix formatting 2023-08-03 14:34:50 -04:00
Jacob Fink
270a395d9f [PM-2713] set enc user key during kc onboarding 2023-08-03 13:41:19 -04:00
Jacob Fink
4fa8d2ba28 [PM-2713] set user key on set password page 2023-08-03 13:30:45 -04:00
Jacob Fink
e076c9fe04 [PM-2713] add auto unlock key to mobile 2023-08-03 10:49:55 -04:00
Jacob Fink
ee0dcd23f5 rename account keys to be more descriptive 2023-08-01 20:08:41 -04:00
Jacob Fink
1e8ed1b5ce [PM-2713] replace generic with inherited class 2023-08-01 10:47:02 -04:00
Jacob Fink
7fb89fa1a5 [PM-2713] consolidate attachment key creation
- also fix ios files missed during symbol rename
2023-08-01 09:30:00 -04:00
Jacob Fink
b1eb263fef [PM-2713] combine makeDataEncKey methods 2023-08-01 08:54:19 -04:00
Jacob Fink
61aac20555 [PM-2713] rename state methods 2023-08-01 08:46:02 -04:00
Jacob Fink
3e87d74061 [PM-2713] revert feedback for build 2023-07-31 17:09:47 -04:00
Jacob Fink
89a9185b20 [PM-2713] rename get pin lock type method 2023-07-31 16:49:41 -04:00
Jacob Fink
e323e196c0 [PM-2713] PR feedback 2023-07-31 15:31:46 -04:00
Jacob Fink
c793260689 [PM-2713] pr feedback 2023-07-31 13:13:29 -04:00
Jacob Fink
c2ddbb7eff [PM-2713] rename toggle method, don't reset enc user key 2023-07-31 12:57:50 -04:00
Jacob Fink
bb5a7383a8 [PM-2713] don't pass user key as param when encrypting 2023-07-31 12:42:56 -04:00
Jacob Fink
de5113ede7 [PM-2713] rename PinLockEnum to PinLockType 2023-07-27 16:26:23 -04:00
Jacob Fink
ba6d260565 [PM-2713] rename PrivateKey methods to UserPrivateKey on crypto service 2023-07-27 16:24:31 -04:00
Jacob Fink
7562c688c5 [PM-2713] deconstruct new key pair 2023-07-27 16:20:33 -04:00
Jacob Fink
10574a7117 [PM-2713] remove broken casting 2023-07-20 16:17:30 -04:00
Jacob Fink
a2f1ca583a [PM-2713] remove extra comment 2023-07-20 16:14:40 -04:00
Jacob Fink
813ac841c6 [PM-2713] fix casting issues and pin 2023-07-20 13:57:32 -04:00
Jacob Fink
0da3d25955 [PM-2713] rename password hash to master key hash 2023-07-20 12:30:02 -04:00
Jacob Fink
f8c9cde2ed [PM-2713] optimize async code in crypto service 2023-07-20 11:17:57 -04:00
37 changed files with 596 additions and 462 deletions

View File

@@ -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);

View File

@@ -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)
{ {

View File

@@ -38,7 +38,7 @@ namespace Bit.App.Pages
private string _masterPassword; private string _masterPassword;
private string _pin; private string _pin;
private bool _showPassword; private bool _showPassword;
private PinLockEnum _pinStatus; private PinLockType _pinStatus;
private bool _pinEnabled; private bool _pinEnabled;
private bool _biometricEnabled; private bool _biometricEnabled;
private bool _biometricIntegrityValid = true; private bool _biometricIntegrityValid = true;
@@ -161,17 +161,17 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync(); ?? await _stateService.GetPinProtectedKeyAsync();
PinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
_pinStatus == PinLockEnum.Persistent; _pinStatus == PinLockType.Persistent;
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync(); 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 && !(BiometricEnabled || PinEnabled)) if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))
{ {
await _vaultTimeoutService.LogOutAsync(); await _vaultTimeoutService.LogOutAsync();
@@ -257,15 +257,15 @@ namespace Bit.App.Pages
{ {
EncString userKeyPin = null; EncString userKeyPin = null;
EncString oldPinProtected = null; EncString oldPinProtected = null;
if (_pinStatus == PinLockEnum.Persistent) if (_pinStatus == PinLockType.Persistent)
{ {
userKeyPin = await _stateService.GetUserKeyPinAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
} }
else if (_pinStatus == PinLockEnum.Transient) else if (_pinStatus == PinLockType.Transient)
{ {
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
} }
@@ -273,7 +273,7 @@ namespace Bit.App.Pages
if (oldPinProtected != null) if (oldPinProtected != null)
{ {
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockEnum.Transient, _pinStatus == PinLockType.Transient,
Pin, Pin,
_email, _email,
kdfConfig, kdfConfig,
@@ -297,7 +297,7 @@ namespace Bit.App.Pages
{ {
Pin = string.Empty; Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(userKey); await SetUserKeyAndContinueAsync(userKey);
} }
} }
catch catch
@@ -319,20 +319,20 @@ namespace Bit.App.Pages
else else
{ {
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig); var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetPasswordHashAsync(); var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync();
var passwordValid = false; var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null; MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null) if (storedKeyHash != null)
{ {
// Offline unlock possible // Offline unlock possible
passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey); passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, masterKey);
} }
else else
{ {
// Online unlock required // Online unlock required
await _deviceActionService.ShowLoadingAsync(AppResources.Loading); await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, 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;
@@ -341,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, masterKey, HashPurpose.LocalAuthorization); var localKeyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
await _cryptoService.SetPasswordHashAsync(localKeyHash); await _cryptoService.SetMasterKeyHashAsync(localKeyHash);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -364,7 +364,7 @@ namespace Bit.App.Pages
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey); var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
await _cryptoService.SetMasterKeyAsync(masterKey); await _cryptoService.SetMasterKeyAsync(masterKey);
await SetKeyAndContinueAsync(userKey); await SetUserKeyAndContinueAsync(userKey);
// Re-enable biometrics // Re-enable biometrics
if (BiometricEnabled & !BiometricIntegrityValid) if (BiometricEnabled & !BiometricIntegrityValid)
@@ -462,11 +462,12 @@ namespace Bit.App.Pages
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(UserKey key) private async Task SetUserKeyAndContinueAsync(UserKey key)
{ {
var hasKey = await _cryptoService.HasUserKeyAsync(); var hasKey = await _cryptoService.HasUserKeyAsync();
if (!hasKey) if (!hasKey)

View File

@@ -178,11 +178,8 @@ namespace Bit.App.Pages
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 newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync( var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
newMasterKey, var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
await _cryptoService.MakeUserKeyAsync()
);
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new RegisterRequest var request = new RegisterRequest
{ {

View File

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

View File

@@ -166,13 +166,12 @@ 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 newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization); var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization); var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey, var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
var keys = await _cryptoService.MakeKeyPairAsync(newUserKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new SetPasswordRequest var request = new SetPasswordRequest
{ {
MasterPasswordHash = masterPasswordHash, MasterPasswordHash = masterPasswordHash,
@@ -185,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
} }
}; };
@@ -196,10 +195,11 @@ 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.SetUserKeyAsync(newUserKey);
await _cryptoService.SetMasterKeyAsync(newMasterKey); await _cryptoService.SetMasterKeyAsync(newMasterKey);
await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash); await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString); await _cryptoService.SetUserPrivateKeyAsync(newProtectedPrivateKey.EncryptedString);
if (ResetPasswordAutoEnroll) if (ResetPasswordAutoEnroll)
{ {

View File

@@ -95,7 +95,7 @@ namespace Bit.App.Pages
// Create new master key and hash new password // Create new master key and hash new password
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig); var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey); var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey);
// Encrypt user key with new master key // Encrypt user key with new master key
var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey); var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey);
@@ -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
{ {

View File

@@ -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))

View File

@@ -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 != PinLockEnum.Disabled; _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,
@@ -439,18 +440,18 @@ namespace Bit.App.Pages
var email = await _stateService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig); var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig);
var userKey = await _cryptoService.GetUserKeyAsync(); var userKey = await _cryptoService.GetUserKeyAsync();
var pinProtectedKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey); var protectedPinKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey);
var encPin = await _cryptoService.EncryptAsync(pin); var encPin = await _cryptoService.EncryptAsync(pin);
await _stateService.SetProtectedPinAsync(encPin.EncryptedString); await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
if (masterPassOnRestart) if (masterPassOnRestart)
{ {
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey); await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(protectedPinKey);
} }
else else
{ {
await _stateService.SetUserKeyPinAsync(pinProtectedKey); await _stateService.SetPinKeyEncryptedUserKeyAsync(protectedPinKey);
} }
} }
else else
@@ -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.ToggleKeysAsync(); await _cryptoService.RefreshKeysAsync();
BuildList(); BuildList();
} }

View File

@@ -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");
} }

View File

@@ -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()

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

View File

@@ -38,13 +38,13 @@ namespace Bit.App.Services
return false; return false;
}; };
return await _cryptoService.CompareAndUpdatePasswordHashAsync(password, null); return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
} }
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();
} }
} }
} }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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> GetMasterKeyFromKeyConnector(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();

View File

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

View File

@@ -10,7 +10,7 @@ namespace Bit.Core.Abstractions
public interface ICryptoService public interface ICryptoService
{ {
void ClearCache(); void ClearCache();
Task ToggleKeysAsync(); Task RefreshKeysAsync();
Task SetUserKeyAsync(UserKey userKey, string userId = null); Task SetUserKeyAsync(UserKey userKey, string userId = null);
Task<UserKey> GetUserKeyAsync(string userId = null); Task<UserKey> GetUserKeyAsync(string userId = null);
Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null); Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null);
@@ -19,26 +19,28 @@ namespace Bit.Core.Abstractions
Task<UserKey> MakeUserKeyAsync(); Task<UserKey> MakeUserKeyAsync();
Task ClearUserKeyAsync(string userId = null); Task ClearUserKeyAsync(string userId = null);
Task SetMasterKeyEncryptedUserKeyAsync(string value, 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 SetMasterKeyAsync(MasterKey masterKey, string userId = null);
Task<MasterKey> GetMasterKeyAsync(string userId = null); Task<MasterKey> GetMasterKeyAsync(string userId = null);
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig); Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
Task ClearMasterKeyAsync(string userId = null); Task ClearMasterKeyAsync(string userId = null);
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null); Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey);
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(UserKey key); Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(OrgKey key); Task<string> HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task SetMasterKeyHashAsync(string keyHash);
Task SetPasswordHashAsync(string keyHash); Task<string> GetMasterKeyHashAsync();
Task<string> GetPasswordHashAsync(); Task ClearMasterKeyHashAsync(string userId = null);
Task ClearPasswordHashAsync(string userId = null); Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key);
Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key);
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs); Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
Task<OrgKey> GetOrgKeyAsync(string orgId); Task<OrgKey> GetOrgKeyAsync(string orgId);
Task<Dictionary<string, OrgKey>> GetOrgKeysAsync(); Task<Dictionary<string, OrgKey>> GetOrgKeysAsync();
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null); Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
Task<byte[]> GetPublicKeyAsync(); Task<byte[]> GetUserPublicKeyAsync();
Task SetPrivateKeyAsync(string encPrivateKey); Task SetUserPrivateKeyAsync(string encPrivateKey);
Task<byte[]> GetPrivateKeyAsync(); Task<byte[]> GetUserPrivateKeyAsync();
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null); Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null); Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null); Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);

View File

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

View File

@@ -17,8 +17,10 @@ namespace Bit.Core.Abstractions
Task SetUserKeyAsync(UserKey value, string userId = null); Task SetUserKeyAsync(UserKey value, string userId = null);
Task<MasterKey> GetMasterKeyAsync(string userId = null); Task<MasterKey> GetMasterKeyAsync(string userId = null);
Task SetMasterKeyAsync(MasterKey value, string userId = null); Task SetMasterKeyAsync(MasterKey value, string userId = null);
Task<string> GetUserKeyMasterKeyAsync(string userId = null); Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null);
Task SetUserKeyMasterKeyAsync(string value, 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);
@@ -33,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);
@@ -44,19 +48,11 @@ namespace Bit.Core.Abstractions
Task<bool> CanAccessPremiumAsync(string userId = null); Task<bool> CanAccessPremiumAsync(string userId = null);
Task<string> GetProtectedPinAsync(string userId = null); Task<string> GetProtectedPinAsync(string userId = null);
Task SetPersonalPremiumAsync(bool value, string userId = null); Task SetPersonalPremiumAsync(bool value, string userId = null);
Task<EncString> GetUserKeyPinAsync(string userId = null); Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null);
Task SetUserKeyPinAsync(EncString value, string userId = null); Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null);
Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null); Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null);
Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null); Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null);
Task SetProtectedPinAsync(string value, string userId = null); Task SetProtectedPinAsync(string value, string userId = null);
[Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")]
Task<string> GetPinProtectedAsync(string userId = null);
[Obsolete("Use SetUserKeyPinAsync instead")]
Task SetPinProtectedAsync(string value, string userId = null);
[Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")]
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
[Obsolete("Use SetUserKeyPinEphemeralAsync instead")]
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
Task SetKdfConfigurationAsync(KdfConfig config, string userId = null); Task SetKdfConfigurationAsync(KdfConfig config, 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);
@@ -184,17 +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 GetUserKeyMasterKey instead")] [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); Task<string> GetEncKeyEncryptedAsync(string userId = null);
[Obsolete("Use SetUserKeyMasterKey instead")] [Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
Task SetEncKeyEncryptedAsync(string value, string userId = null); Task SetEncKeyEncryptedAsync(string value, string userId = null);
[Obsolete] [Obsolete("Left for migration purposes")]
Task<string> GetKeyEncryptedAsync(string userId = null);
[Obsolete]
Task SetKeyEncryptedAsync(string value, string userId = null); Task SetKeyEncryptedAsync(string value, string userId = null);
[Obsolete("Use GetMasterKey instead")]
[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); Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
[Obsolete("Use GetMasterKey instead")]
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
} }
} }

View File

@@ -17,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<PinLockEnum> 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);

View File

@@ -81,7 +81,9 @@ namespace Bit.Core
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 UserKeyKey(string userId) => $"userKey_{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}";
@@ -93,7 +95,7 @@ namespace Bit.Core
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 KeyHashKey(string userId) => $"keyHash_{userId}"; public static string KeyHashKey(string userId) => $"keyHash_{userId}";
public static string UserKeyPinKey(string userId) => $"userKeyPin_{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}";

View File

@@ -119,7 +119,7 @@ namespace Bit.Core.Models.Domain
{ {
public UserKey UserKey; public UserKey UserKey;
public MasterKey MasterKey; public MasterKey MasterKey;
public EncString UserKeyPinEphemeral; 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.")] [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; public SymmetricCryptoKey Key;

View File

@@ -485,7 +485,7 @@ namespace Bit.Core.Services
#region Key Connector #region Key Connector
public async Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnector(string keyConnectorUrl) public async Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl)
{ {
using (var requestMessage = new HttpRequestMessage()) using (var requestMessage = new HttpRequestMessage())
{ {

View File

@@ -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))
@@ -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);
} }
@@ -473,14 +470,14 @@ namespace Bit.Core.Services
if (localHashedPassword != null) if (localHashedPassword != null)
{ {
await _cryptoService.SetPasswordHashAsync(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.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
@@ -508,21 +505,25 @@ namespace Bit.Core.Services
catch { } catch { }
} }
await _cryptoService.SetPrivateKeyAsync(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 newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64); Convert.ToBase64String(password),
await _cryptoService.SetMasterKeyAsync(newMasterKey); _tokenService.GetEmail(),
tokenResponse.KdfConfig);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync( var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
newMasterKey,
await _cryptoService.MakeUserKeyAsync()); var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
await _cryptoService.SetUserKeyAsync(newUserKey); await _cryptoService.SetUserKeyAsync(newUserKey);
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
try try

View File

@@ -249,8 +249,7 @@ namespace Bit.Core.Services
{ {
try try
{ {
var hashKey = await _cryptoService.HasUserKeyAsync(); if (!await _cryptoService.HasUserKeyAsync())
if (!hashKey)
{ {
throw new Exception("No key."); throw new Exception("No key.");
} }
@@ -557,20 +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)
{ {
SymmetricCryptoKey attachmentKey; var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(cipher.OrganizationId);
EncString protectedAttachmentKey;
var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId);
if (orgKey != null)
{
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
}
else
{
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
}
var encFileName = await _cryptoService.EncryptAsync(filename, 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;
@@ -807,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)
{ {
@@ -819,20 +815,9 @@ 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);
SymmetricCryptoKey attachmentKey; var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId);
EncString protectedAttachmentKey;
var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId);
if (orgKey != null)
{
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
}
else
{
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
}
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, orgKey); var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, encKey);
var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey); var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey);
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks); var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);

View File

@@ -20,7 +20,7 @@ namespace Bit.Core.Services
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private SymmetricCryptoKey _legacyEtmKey; private SymmetricCryptoKey _legacyEtmKey;
private string _passwordHash; private string _masterKeyHash;
private byte[] _publicKey; private byte[] _publicKey;
private byte[] _privateKey; private byte[] _privateKey;
private Dictionary<string, OrgKey> _orgKeys; private Dictionary<string, OrgKey> _orgKeys;
@@ -37,42 +37,28 @@ namespace Bit.Core.Services
public void ClearCache() public void ClearCache()
{ {
_legacyEtmKey = null; _legacyEtmKey = null;
_passwordHash = null; _masterKeyHash = null;
_publicKey = null; _publicKey = null;
_privateKey = null; _privateKey = null;
_orgKeys = null; _orgKeys = null;
} }
public async Task ToggleKeysAsync() public async Task RefreshKeysAsync()
{ {
// refresh or clear the pin key // Refresh or clear additional keys such as
// pin and auto unlock keys
await SetUserKeyAsync(await GetUserKeyAsync()); await SetUserKeyAsync(await GetUserKeyAsync());
// refresh or clear the encrypted user key
var encUserKey = await _stateService.GetUserKeyMasterKeyAsync();
await _stateService.SetUserKeyMasterKeyAsync(null);
await _stateService.SetUserKeyMasterKeyAsync(encUserKey);
} }
public async Task SetUserKeyAsync(UserKey userKey, string userId = null) public async Task SetUserKeyAsync(UserKey userKey, string userId = null)
{ {
await _stateService.SetUserKeyAsync(userKey, userId); await _stateService.SetUserKeyAsync(userKey, userId);
await StoreAdditionalKeysAsync(userKey, userId);
// Refresh the Pin Key if the user has a Pin set
if (await _stateService.GetProtectedPinAsync(userId) != null)
{
await StorePinKey(userKey, userId);
}
else
{
await _stateService.SetUserKeyPinAsync(null, userId);
await _stateService.SetUserKeyPinEphemeralAsync(null, userId);
}
} }
public async Task<UserKey> GetUserKeyAsync(string userId = null) public Task<UserKey> GetUserKeyAsync(string userId = null)
{ {
return await _stateService.GetUserKeyAsync(userId); return _stateService.GetUserKeyAsync(userId);
} }
public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null) public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null)
@@ -85,7 +71,7 @@ namespace Bit.Core.Services
// Legacy support: encryption used to be done with the master key (derived from master password). // Legacy support: encryption used to be done with the master key (derived from master password).
// Users who have not migrated will have a null user key and must use the master key instead. // Users who have not migrated will have a null user key and must use the master key instead.
return (SymmetricCryptoKey)await GetMasterKeyAsync() as UserKey; return new UserKey((await GetMasterKeyAsync()).Key);
} }
public async Task<bool> HasUserKeyAsync(string userId = null) public async Task<bool> HasUserKeyAsync(string userId = null)
@@ -95,7 +81,7 @@ namespace Bit.Core.Services
public async Task<bool> HasEncryptedUserKeyAsync(string userId = null) public async Task<bool> HasEncryptedUserKeyAsync(string userId = null)
{ {
return await _stateService.GetUserKeyMasterKeyAsync(userId) != null; return await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId) != null;
} }
public async Task<UserKey> MakeUserKeyAsync() public async Task<UserKey> MakeUserKeyAsync()
@@ -103,20 +89,36 @@ namespace Bit.Core.Services
return new UserKey(await _cryptoFunctionService.RandomBytesAsync(64)); return new UserKey(await _cryptoFunctionService.RandomBytesAsync(64));
} }
public async Task ClearUserKeyAsync(string userId = null) public Task ClearUserKeyAsync(string userId = null)
{ {
await _stateService.SetUserKeyAsync(null, userId); return _stateService.SetUserKeyAsync(null, userId);
} }
public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) public Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
{ {
await _stateService.SetUserKeyMasterKeyAsync(value, userId); return _stateService.SetMasterKeyEncryptedUserKeyAsync(value, userId);
} }
public async Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) public async Task<UserKey> GetAutoUnlockKeyAsync(string userId = null)
{ {
await _stateService.SetMasterKeyAsync(masterKey, userId); await MigrateAutoAndBioKeysIfNeededAsync(userId);
return await _stateService.GetUserKeyAutoUnlockAsync(userId);
}
public async Task<bool> HasAutoUnlockKeyAsync(string userId = null)
{
return await GetAutoUnlockKeyAsync(userId) != null;
}
public async Task<UserKey> GetBiometricUnlockKeyAsync(string userId = null)
{
await MigrateAutoAndBioKeysIfNeededAsync(userId);
return await _stateService.GetUserKeyBiometricUnlockAsync(userId);
}
public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null)
{
return _stateService.SetMasterKeyAsync(masterKey, userId);
} }
public async Task<MasterKey> GetMasterKeyAsync(string userId = null) public async Task<MasterKey> GetMasterKeyAsync(string userId = null)
@@ -125,7 +127,7 @@ namespace Bit.Core.Services
if (masterKey == null) if (masterKey == null)
{ {
// Migration support // Migration support
masterKey = await _stateService.GetKeyDecryptedAsync(userId) as MasterKey; masterKey = new MasterKey((await _stateService.GetKeyDecryptedAsync(userId)).Key);
if (masterKey != null) if (masterKey != null)
{ {
await SetMasterKeyAsync(masterKey, userId); await SetMasterKeyAsync(masterKey, userId);
@@ -134,20 +136,20 @@ namespace Bit.Core.Services
return masterKey; return masterKey;
} }
public async Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig) public Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig)
{ {
return await MakeKeyAsync(password, email, kdfConfig, keyBytes => new MasterKey(keyBytes)); return MakeKeyAsync(password, email, kdfConfig, keyBytes => new MasterKey(keyBytes));
} }
public async Task ClearMasterKeyAsync(string userId = null) public Task ClearMasterKeyAsync(string userId = null)
{ {
await _stateService.SetMasterKeyAsync(null, userId); return _stateService.SetMasterKeyAsync(null, userId);
} }
public async Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null) public async Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey)
{ {
userKey ??= await GetUserKeyAsync(); var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync();
return await BuildProtectedSymmetricKey<UserKey>(masterKey, userKey.Key); return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
} }
public async Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null) public async Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null)
@@ -160,11 +162,27 @@ namespace Bit.Core.Services
if (encUserKey == null) if (encUserKey == null)
{ {
var userKeyMasterKey = await _stateService.GetUserKeyMasterKeyAsync(userId); var userKeyMasterKey = await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId);
if (userKeyMasterKey == null)
if (userKeyMasterKey is null)
{ {
throw new Exception("No encrypted user key found"); // Migrate old key
var oldEncUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
if (oldEncUserKey is null)
{
throw new Exception("No encrypted user key nor old encKeyEncrypted found");
}
var userKey = await DecryptUserKeyWithMasterKeyAsync(
masterKey,
new EncString(oldEncUserKey),
userId
);
await SetMasterKeyEncryptedUserKeyAsync(oldEncUserKey, userId);
return userKey;
} }
encUserKey = new EncString(userKeyMasterKey); encUserKey = new EncString(userKeyMasterKey);
} }
@@ -175,12 +193,12 @@ namespace Bit.Core.Services
} }
else if (encUserKey.EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64) else if (encUserKey.EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64)
{ {
var newKey = await StretchKeyAsync(masterKey); var newKey = await StretchKeyAsync(masterKey, keyBytes => new MasterKey(keyBytes));
decUserKey = await DecryptToBytesAsync(encUserKey, newKey); decUserKey = await DecryptToBytesAsync(encUserKey, newKey);
} }
else else
{ {
throw new Exception("Unsupported encKey type."); throw new Exception($"Unsupported encrypted user key type: {encUserKey.EncryptionType}");
} }
if (decUserKey == null) if (decUserKey == null)
@@ -190,86 +208,82 @@ namespace Bit.Core.Services
return new UserKey(decUserKey); return new UserKey(decUserKey);
} }
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(UserKey key) public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key)
{ {
if (key == null) if (key is null)
{ {
throw new Exception("No user key provided"); throw new ArgumentNullException(nameof(key));
}
if (!(key is UserKey) && !(key is OrgKey))
{
throw new ArgumentException($"Data encryption keys must be of type UserKey or OrgKey. {key.GetType().FullName} unsupported.");
} }
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64);
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey); return await BuildProtectedSymmetricKeyAsync(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes));
} }
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(OrgKey key) public async Task<string> HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization)
{ {
if (key == null) if (password is null)
{ {
throw new Exception("No org key provided"); throw new ArgumentNullException(nameof(password));
} }
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); if (masterKey is null)
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey); {
} masterKey = await GetMasterKeyAsync();
// TODO(Jake): Uses Master Key if (masterKey is null)
public async Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) {
{ throw new ArgumentNullException(nameof(masterKey));
if (key == null) }
{
key = await GetMasterKeyAsync();
} }
if (password == null || key == null) var hash = await _cryptoFunctionService.Pbkdf2Async(masterKey.Key, password, CryptoHashAlgorithm.Sha256, (int)hashPurpose);
{
throw new Exception("Invalid parameters.");
}
var iterations = hashPurpose == HashPurpose.LocalAuthorization ? 2 : 1;
var hash = await _cryptoFunctionService.Pbkdf2Async(key.Key, password, CryptoHashAlgorithm.Sha256, iterations);
return Convert.ToBase64String(hash); return Convert.ToBase64String(hash);
} }
public async Task SetPasswordHashAsync(string keyHash) public Task SetMasterKeyHashAsync(string keyHash)
{ {
_passwordHash = keyHash; _masterKeyHash = keyHash;
await _stateService.SetKeyHashAsync(keyHash); return _stateService.SetKeyHashAsync(keyHash);
} }
public async Task<string> GetPasswordHashAsync() public async Task<string> GetMasterKeyHashAsync()
{ {
if (_passwordHash != null) if (_masterKeyHash != null)
{ {
return _passwordHash; return _masterKeyHash;
} }
var passwordHash = await _stateService.GetKeyHashAsync(); var passwordHash = await _stateService.GetKeyHashAsync();
if (passwordHash != null) if (passwordHash != null)
{ {
_passwordHash = passwordHash; _masterKeyHash = passwordHash;
} }
return _passwordHash; return _masterKeyHash;
} }
public async Task ClearPasswordHashAsync(string userId = null) public Task ClearMasterKeyHashAsync(string userId = null)
{ {
_passwordHash = null; _masterKeyHash = null;
await _stateService.SetKeyHashAsync(null, userId); return _stateService.SetKeyHashAsync(null, userId);
} }
// TODO(Jake): Uses Master Key public async Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key)
public async Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key)
{ {
var storedPasswordHash = await GetPasswordHashAsync(); var storedPasswordHash = await GetMasterKeyHashAsync();
if (masterPassword != null && storedPasswordHash != null) if (masterPassword != null && storedPasswordHash != null)
{ {
var localPasswordHash = await HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); var localPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization);
if (localPasswordHash != null && storedPasswordHash == localPasswordHash) if (localPasswordHash != null && storedPasswordHash == localPasswordHash)
{ {
return true; return true;
} }
var serverPasswordHash = await HashPasswordAsync(masterPassword, key, HashPurpose.ServerAuthorization); var serverPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.ServerAuthorization);
if (serverPasswordHash != null & storedPasswordHash == serverPasswordHash) if (serverPasswordHash != null && storedPasswordHash == serverPasswordHash)
{ {
await SetPasswordHashAsync(localPasswordHash); await SetMasterKeyHashAsync(localPasswordHash);
return true; return true;
} }
} }
@@ -277,11 +291,11 @@ namespace Bit.Core.Services
return false; return false;
} }
public async Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs) public Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs)
{ {
var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key); var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key);
_orgKeys = null; _orgKeys = null;
await _stateService.SetOrgKeysEncryptedAsync(orgKeys); return _stateService.SetOrgKeysEncryptedAsync(orgKeys);
} }
public async Task<OrgKey> GetOrgKeyAsync(string orgId) public async Task<OrgKey> GetOrgKeyAsync(string orgId)
@@ -351,13 +365,13 @@ namespace Bit.Core.Services
} }
} }
public async Task<byte[]> GetPublicKeyAsync() public async Task<byte[]> GetUserPublicKeyAsync()
{ {
if (_publicKey != null) if (_publicKey != null)
{ {
return _publicKey; return _publicKey;
} }
var privateKey = await GetPrivateKeyAsync(); var privateKey = await GetUserPrivateKeyAsync();
if (privateKey == null) if (privateKey == null)
{ {
return null; return null;
@@ -366,7 +380,7 @@ namespace Bit.Core.Services
return _publicKey; return _publicKey;
} }
public async Task SetPrivateKeyAsync(string encPrivateKey) public async Task SetUserPrivateKeyAsync(string encPrivateKey)
{ {
if (encPrivateKey == null) if (encPrivateKey == null)
{ {
@@ -376,7 +390,7 @@ namespace Bit.Core.Services
_privateKey = null; _privateKey = null;
} }
public async Task<byte[]> GetPrivateKeyAsync() public async Task<byte[]> GetUserPrivateKeyAsync()
{ {
if (_privateKey != null) if (_privateKey != null)
{ {
@@ -395,7 +409,7 @@ namespace Bit.Core.Services
{ {
if (publicKey == null) if (publicKey == null)
{ {
publicKey = await GetPublicKeyAsync(); publicKey = await GetUserPublicKeyAsync();
} }
if (publicKey == null) if (publicKey == null)
{ {
@@ -426,28 +440,29 @@ namespace Bit.Core.Services
public async Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config) public async Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config)
{ {
var pinKey = await MakeKeyAsync(pin, salt, config, keyBytes => new PinKey(keyBytes)); var pinKey = await MakeKeyAsync(pin, salt, config, keyBytes => new PinKey(keyBytes));
return await StretchKeyAsync(pinKey) as PinKey; return await StretchKeyAsync(pinKey, keyBytes => new PinKey(keyBytes));
} }
public async Task ClearPinKeysAsync(string userId = null) public Task ClearPinKeysAsync(string userId = null)
{ {
await _stateService.SetUserKeyPinAsync(null, userId); return Task.WhenAll(
await _stateService.SetUserKeyPinEphemeralAsync(null, userId); _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId),
await _stateService.SetProtectedPinAsync(null, userId); _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId),
await clearDeprecatedPinKeysAsync(userId); _stateService.SetProtectedPinAsync(null, userId),
ClearDeprecatedPinKeysAsync(userId));
} }
public async Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null) public async Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null)
{ {
pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync(); pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyAsync();
pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync(); pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
if (pinProtectedUserKey == null) if (pinProtectedUserKey == null)
{ {
throw new Exception("No PIN protected user key found."); throw new Exception("No PIN protected user key found.");
} }
var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig); var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey); var userKeyBytes = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
return new UserKey(userKey); return new UserKey(userKeyBytes);
} }
// Only for migration purposes // Only for migration purposes
@@ -481,7 +496,7 @@ namespace Bit.Core.Services
{ {
if (publicKey == null) if (publicKey == null)
{ {
publicKey = await GetPublicKeyAsync(); publicKey = await GetUserPublicKeyAsync();
} }
if (publicKey == null) if (publicKey == null)
{ {
@@ -521,7 +536,7 @@ namespace Bit.Core.Services
if (privateKey is null) if (privateKey is null)
{ {
privateKey = await GetPrivateKeyAsync(); privateKey = await GetUserPrivateKeyAsync();
} }
if (privateKey == null) if (privateKey == null)
@@ -618,17 +633,17 @@ namespace Bit.Core.Services
return await AesDecryptToBytesAsync(encType, ctBytes, ivBytes, macBytes, key); return await AesDecryptToBytesAsync(encType, ctBytes, ivBytes, macBytes, key);
} }
public async Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null) public Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null)
{ {
var iv = Convert.FromBase64String(encString.Iv); var iv = Convert.FromBase64String(encString.Iv);
var data = Convert.FromBase64String(encString.Data); var data = Convert.FromBase64String(encString.Data);
var mac = !string.IsNullOrWhiteSpace(encString.Mac) ? Convert.FromBase64String(encString.Mac) : null; var mac = !string.IsNullOrWhiteSpace(encString.Mac) ? Convert.FromBase64String(encString.Mac) : null;
return await AesDecryptToBytesAsync(encString.EncryptionType, data, iv, mac, key); return AesDecryptToBytesAsync(encString.EncryptionType, data, iv, mac, key);
} }
public async Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null) public Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null)
{ {
return await AesDecryptToUtf8Async(encString.EncryptionType, encString.Data, return AesDecryptToUtf8Async(encString.EncryptionType, encString.Data,
encString.Iv, encString.Mac, key); encString.Iv, encString.Mac, key);
} }
@@ -675,7 +690,41 @@ namespace Bit.Core.Services
// --HELPER METHODS-- // --HELPER METHODS--
private async Task StorePinKey(UserKey userKey, string userId = null) private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null)
{
// Set, refresh, or clear the pin key
if (await _stateService.GetProtectedPinAsync(userId) != null)
{
await UpdatePinKeyAsync(userKey, userId);
}
else
{
await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId);
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId);
}
// Set, refresh, or clear the auto unlock key
if (await _stateService.GetVaultTimeoutAsync(userId) == null)
{
await _stateService.SetUserKeyAutoUnlockAsync(userKey, userId);
}
else
{
await _stateService.SetUserKeyAutoUnlockAsync(null, userId);
}
// Set, refresh, or clear the biometric unlock key
if ((await _stateService.GetBiometricUnlockAsync(userId)).GetValueOrDefault())
{
await _stateService.SetUserKeyBiometricUnlockAsync(userKey, userId);
}
else
{
await _stateService.SetUserKeyBiometricUnlockAsync(null, userId);
}
}
private async Task UpdatePinKeyAsync(UserKey userKey, string userId = null)
{ {
var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId))); var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId)));
var pinKey = await MakePinKeyAsync( var pinKey = await MakePinKeyAsync(
@@ -685,13 +734,12 @@ namespace Bit.Core.Services
); );
var encPin = await EncryptAsync(userKey.Key, pinKey); var encPin = await EncryptAsync(userKey.Key, pinKey);
// TODO(Jake): Does this logic make sense? Should we save something in state to indicate the preference? if (await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null)
if (await _stateService.GetUserKeyPinAsync(userId) != null)
{ {
await _stateService.SetUserKeyPinAsync(encPin, userId); await _stateService.SetPinKeyEncryptedUserKeyAsync(encPin, userId);
return; return;
} }
await _stateService.SetUserKeyPinEphemeralAsync(encPin, userId); await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(encPin, userId);
} }
private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key) private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key)
@@ -821,14 +869,16 @@ namespace Bit.Core.Services
return key; return key;
} }
private async Task<SymmetricCryptoKey> StretchKeyAsync(SymmetricCryptoKey key) // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack
private async Task<TKey> StretchKeyAsync<TKey>(SymmetricCryptoKey key, Func<byte[], TKey> keyCreator)
where TKey : SymmetricCryptoKey
{ {
var newKey = new byte[64]; var newKey = new byte[64];
var enc = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("enc"), 32, HkdfAlgorithm.Sha256); var enc = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("enc"), 32, HkdfAlgorithm.Sha256);
Buffer.BlockCopy(enc, 0, newKey, 0, 32); Buffer.BlockCopy(enc, 0, newKey, 0, 32);
var mac = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("mac"), 32, HkdfAlgorithm.Sha256); var mac = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("mac"), 32, HkdfAlgorithm.Sha256);
Buffer.BlockCopy(mac, 0, newKey, 32, 32); Buffer.BlockCopy(mac, 0, newKey, 32, 32);
return new SymmetricCryptoKey(newKey); return keyCreator(newKey);
} }
private List<string> HashPhrase(byte[] hash, int minimumEntropy = 64) private List<string> HashPhrase(byte[] hash, int minimumEntropy = 64)
@@ -855,13 +905,14 @@ namespace Bit.Core.Services
return phrase; return phrase;
} }
private async Task<Tuple<T, EncString>> BuildProtectedSymmetricKey<T>(SymmetricCryptoKey key, // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack
byte[] encKey) where T : SymmetricCryptoKey private async Task<Tuple<TKey, EncString>> BuildProtectedSymmetricKeyAsync<TKey>(SymmetricCryptoKey key,
byte[] encKey, Func<byte[], TKey> keyCreator) where TKey : SymmetricCryptoKey
{ {
EncString encKeyEnc = null; EncString encKeyEnc = null;
if (key.Key.Length == 32) if (key.Key.Length == 32)
{ {
var newKey = await StretchKeyAsync(key); var newKey = await StretchKeyAsync(key, keyCreator);
encKeyEnc = await EncryptAsync(encKey, newKey); encKeyEnc = await EncryptAsync(encKey, newKey);
} }
else if (key.Key.Length == 64) else if (key.Key.Length == 64)
@@ -872,14 +923,14 @@ namespace Bit.Core.Services
{ {
throw new Exception("Invalid key size."); throw new Exception("Invalid key size.");
} }
return new Tuple<T, EncString>(new SymmetricCryptoKey(encKey) as T, encKeyEnc); return new Tuple<TKey, EncString>(keyCreator(encKey), encKeyEnc);
} }
// TODO: This intantiator needs to be moved into each key type in order to get rid of the keyCreator hack // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack
private async Task<TKey> MakeKeyAsync<TKey>(string password, string salt, KdfConfig kdfConfig, Func<byte[], TKey> keyCreator) private async Task<TKey> MakeKeyAsync<TKey>(string password, string salt, KdfConfig kdfConfig, Func<byte[], TKey> keyCreator)
where TKey : SymmetricCryptoKey where TKey : SymmetricCryptoKey
{ {
byte[] key = null; byte[] key;
if (kdfConfig.Type == null || kdfConfig.Type == KdfType.PBKDF2_SHA256) if (kdfConfig.Type == null || kdfConfig.Type == KdfType.PBKDF2_SHA256)
{ {
var iterations = kdfConfig.Iterations.GetValueOrDefault(5000); var iterations = kdfConfig.Iterations.GetValueOrDefault(5000);
@@ -937,6 +988,37 @@ namespace Bit.Core.Services
// We previously used the master key for additional keys, but now we use the user key. // We previously used the master key for additional keys, but now we use the user key.
// These methods support migrating the old keys to the new ones. // These methods support migrating the old keys to the new ones.
private async Task MigrateAutoAndBioKeysIfNeededAsync(string userId = null)
{
var oldKey = await _stateService.GetKeyEncryptedAsync(userId);
if (oldKey == null)
{
return;
}
// Decrypt
var masterKey = new MasterKey(Convert.FromBase64String(oldKey));
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
var userKey = await DecryptUserKeyWithMasterKeyAsync(
masterKey,
new EncString(encryptedUserKey),
userId);
// Migrate
if (await _stateService.GetVaultTimeoutAsync(userId) == null)
{
await _stateService.SetUserKeyAutoUnlockAsync(userKey, userId);
}
if ((await _stateService.GetBiometricUnlockAsync(userId)).GetValueOrDefault())
{
await _stateService.SetUserKeyBiometricUnlockAsync(userKey, userId);
}
await _stateService.SetKeyEncryptedAsync(null, userId);
// Set encrypted user key just in case the user locks without syncing
await SetMasterKeyEncryptedUserKeyAsync(encryptedUserKey);
}
public async Task<UserKey> DecryptAndMigrateOldPinKeyAsync( public async Task<UserKey> DecryptAndMigrateOldPinKeyAsync(
bool masterPasswordOnRestart, bool masterPasswordOnRestart,
string pin, string pin,
@@ -964,25 +1046,28 @@ namespace Bit.Core.Services
if (masterPasswordOnRestart) if (masterPasswordOnRestart)
{ {
await _stateService.SetPinProtectedKeyAsync(null); await _stateService.SetPinProtectedKeyAsync(null);
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey); await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(pinProtectedKey);
} }
else else
{ {
await _stateService.SetPinProtectedAsync(null); await _stateService.SetPinProtectedAsync(null);
await _stateService.SetUserKeyPinAsync(pinProtectedKey); await _stateService.SetPinKeyEncryptedUserKeyAsync(pinProtectedKey);
// We previously only set the protected pin if MP on Restart was enabled // We previously only set the protected pin if MP on Restart was enabled
// now we set it regardless // now we set it regardless
var encPin = await EncryptAsync(pin, userKey); var encPin = await EncryptAsync(pin, userKey);
await _stateService.SetProtectedPinAsync(encPin.EncryptedString); await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
} }
// Clear old key
await _stateService.SetEncKeyEncryptedAsync(null);
return userKey; return userKey;
} }
public async Task clearDeprecatedPinKeysAsync(string userId = null) public Task ClearDeprecatedPinKeysAsync(string userId = null)
{ {
await _stateService.SetPinProtectedAsync(null); return Task.WhenAll(
await _stateService.SetPinProtectedKeyAsync(null); _stateService.SetPinProtectedAsync(null, userId),
_stateService.SetPinProtectedKeyAsync(null, userId));
} }
} }
} }

View File

@@ -24,11 +24,11 @@ namespace Bit.Core.Services
_organizationService = organizationService; _organizationService = organizationService;
} }
public async Task GetAndSetKey(string url) public async Task GetAndSetMasterKeyAsync(string url)
{ {
try try
{ {
var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnector(url); var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnectorAsync(url);
var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key); var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key);
var masterKey = new MasterKey(masterKeyArr); var masterKey = new MasterKey(masterKeyArr);
await _cryptoService.SetMasterKeyAsync(masterKey); await _cryptoService.SetMasterKeyAsync(masterKey);
@@ -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,9 +57,9 @@ 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 masterKey = await _cryptoService.GetMasterKeyAsync(); var masterKey = await _cryptoService.GetMasterKeyAsync();
try try
@@ -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;
} }

View File

@@ -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 },
@@ -334,14 +347,29 @@ namespace Bit.Core.Services
await SaveAccountAsync(account, reconciledOptions); await SaveAccountAsync(account, reconciledOptions);
} }
public async Task<string> GetUserKeyMasterKeyAsync(string userId = null) public async Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null)
{ {
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyKey(userId), false); return await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), false);
} }
public async Task SetUserKeyMasterKeyAsync(string value, string userId = null) public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
{ {
await _storageMediatorService.SaveAsync(Constants.UserKeyKey(userId), value, false); 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)
@@ -350,6 +378,7 @@ namespace Bit.Core.Services
{ {
userId = await GetActiveUserIdAsync(); userId = await GetActiveUserIdAsync();
} }
if (!await IsAuthenticatedAsync(userId)) if (!await IsAuthenticatedAsync(userId))
{ {
return false; return false;
@@ -395,66 +424,35 @@ namespace Bit.Core.Services
await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions); await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions);
} }
public async Task<EncString> GetUserKeyPinAsync(string userId = null) public async Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null)
{ {
var key = await _storageMediatorService.GetAsync<string>(Constants.UserKeyPinKey(userId), false); var key = await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), false);
return key != null ? new EncString(key) : null; return key != null ? new EncString(key) : null;
} }
public async Task SetUserKeyPinAsync(EncString value, string userId = null) public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null)
{ {
await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value?.EncryptedString, false); await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), value?.EncryptedString, false);
} }
public async Task<EncString> GetUserKeyPinEphemeralAsync(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?.UserKeyPinEphemeral; ))?.VolatileData?.PinKeyEncryptedUserKeyEphemeral;
} }
public async Task SetUserKeyPinEphemeralAsync(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.UserKeyPinEphemeral = value; account.VolatileData.PinKeyEncryptedUserKeyEphemeral = value;
await SaveAccountAsync(account, reconciledOptions); await SaveAccountAsync(account, reconciledOptions);
} }
[Obsolete("Use GetUserKeyPinAsync 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 SetUserKeyPinAsync 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 GetUserKeyPinEphemeralAsync 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 SetUserKeyPinEphemeralAsync 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);
}
public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null) public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null)
{ {
@@ -1478,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)
@@ -1688,7 +1689,41 @@ namespace Bit.Core.Services
shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync()); shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync());
} }
[Obsolete] [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) public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
{ {
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1696,7 +1731,7 @@ namespace Bit.Core.Services
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions); return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
} }
[Obsolete] [Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
public async Task SetEncKeyEncryptedAsync(string value, string userId) public async Task SetEncKeyEncryptedAsync(string value, string userId)
{ {
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1704,15 +1739,7 @@ namespace Bit.Core.Services
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions); await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
} }
[Obsolete] [Obsolete("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]
public async Task SetKeyEncryptedAsync(string value, string userId) public async Task SetKeyEncryptedAsync(string value, string userId)
{ {
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1720,22 +1747,20 @@ namespace Bit.Core.Services
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions); await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
} }
[Obsolete] [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) public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
{ {
return (await GetAccountAsync( return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.Key; ))?.VolatileData?.Key;
} }
[Obsolete]
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);
}
} }
} }

View File

@@ -328,7 +328,7 @@ namespace Bit.Core.Services
return; return;
} }
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key);
await _cryptoService.SetPrivateKeyAsync(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)

View File

@@ -44,7 +44,7 @@ namespace Bit.Core.Services
} }
else else
{ {
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(secret, null); var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(secret, null);
if (!passwordValid) if (!passwordValid)
{ {
await InvalidSecretErrorAsync(verificationType); await InvalidSecretErrorAsync(verificationType);

View File

@@ -1,13 +1,11 @@
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 PinLockEnum public enum PinLockType
{ {
Disabled, Disabled,
Persistent, Persistent,
@@ -61,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.HasUserKeyAsync(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;
} }
@@ -170,13 +179,13 @@ namespace Bit.Core.Services
userId = await _stateService.GetActiveUserIdAsync(); userId = await _stateService.GetActiveUserIdAsync();
} }
if (await _keyConnectorService.GetUsesKeyConnector()) if (await _keyConnectorService.GetUsesKeyConnectorAsync())
{ {
var pinStatus = await IsPinLockSetAsync(userId); var pinStatus = await GetPinLockTypeAsync(userId);
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync(); ?? await _stateService.GetPinProtectedKeyAsync();
var pinEnabled = (pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
pinStatus == PinLockEnum.Persistent; pinStatus == PinLockType.Persistent;
if (!pinEnabled && !await IsBiometricLockSetAsync()) if (!pinEnabled && !await IsBiometricLockSetAsync())
{ {
@@ -198,6 +207,7 @@ namespace Bit.Core.Services
await Task.WhenAll( await Task.WhenAll(
_cryptoService.ClearUserKeyAsync(userId), _cryptoService.ClearUserKeyAsync(userId),
_cryptoService.ClearMasterKeyAsync(userId), _cryptoService.ClearMasterKeyAsync(userId),
_stateService.SetUserKeyAutoUnlockAsync(null, userId),
_cryptoService.ClearOrgKeysAsync(true, userId), _cryptoService.ClearOrgKeysAsync(true, userId),
_cryptoService.ClearKeyPairAsync(true, userId)); _cryptoService.ClearKeyPairAsync(true, userId));
@@ -223,30 +233,27 @@ namespace Bit.Core.Services
{ {
await _stateService.SetVaultTimeoutAsync(timeout); await _stateService.SetVaultTimeoutAsync(timeout);
await _stateService.SetVaultTimeoutActionAsync(action); await _stateService.SetVaultTimeoutActionAsync(action);
await _cryptoService.ToggleKeysAsync(); await _cryptoService.RefreshKeysAsync();
await _tokenService.ToggleTokensAsync(); await _tokenService.ToggleTokensAsync();
} }
public async Task<PinLockEnum> IsPinLockSetAsync(string userId = null) public async Task<PinLockType> GetPinLockTypeAsync(string userId = null)
{ {
// we can't depend on only the protected pin being set because old // we can't depend on only the protected pin being set because old
// versions only used it for MP on Restart // versions only used it for MP on Restart
var pinIsEnabled = await _stateService.GetProtectedPinAsync(userId); var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null;
var userKeyPin = await _stateService.GetUserKeyPinAsync(userId); var hasUserKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null;
var oldUserKeyPin = await _stateService.GetPinProtectedAsync(userId); var hasOldUserKeyPin = await _stateService.GetPinProtectedAsync(userId) != null;
if (userKeyPin != null || oldUserKeyPin != null) if (hasUserKeyPin || hasOldUserKeyPin)
{ {
return PinLockEnum.Persistent; return PinLockType.Persistent;
} }
else if (pinIsEnabled != null && userKeyPin == null && oldUserKeyPin == null) else if (isPinEnabled && !hasUserKeyPin && !hasOldUserKeyPin)
{ {
return PinLockEnum.Transient; return PinLockType.Transient;
}
else
{
return PinLockEnum.Disabled;
} }
return PinLockType.Disabled;
} }
public async Task<bool> IsBiometricLockSetAsync(string userId = null) public async Task<bool> IsBiometricLockSetAsync(string userId = null)

View File

@@ -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);

View File

@@ -30,7 +30,7 @@ namespace Bit.iOS.Core.Controllers
private IBiometricService _biometricService; private IBiometricService _biometricService;
private IKeyConnectorService _keyConnectorService; private IKeyConnectorService _keyConnectorService;
private IAccountsManager _accountManager; private IAccountsManager _accountManager;
private PinLockEnum _pinStatus; private PinLockType _pinStatus;
private bool _pinEnabled; private bool _pinEnabled;
private bool _biometricEnabled; private bool _biometricEnabled;
private bool _biometricIntegrityValid = true; private bool _biometricIntegrityValid = true;
@@ -104,24 +104,24 @@ namespace Bit.iOS.Core.Controllers
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync()) if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
{ {
_passwordReprompt = true; _passwordReprompt = true;
_pinStatus = PinLockEnum.Disabled; _pinStatus = PinLockType.Disabled;
_pinEnabled = false; _pinEnabled = false;
_biometricEnabled = false; _biometricEnabled = false;
} }
else else
{ {
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync(); ?? await _stateService.GetPinProtectedKeyAsync();
_pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
_pinStatus == PinLockEnum.Persistent; _pinStatus == PinLockType.Persistent;
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() _biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
&& await _cryptoService.HasEncryptedUserKeyAsync(); && await _cryptoService.HasEncryptedUserKeyAsync();
_biometricIntegrityValid = _biometricIntegrityValid =
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey); await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector(); _usesKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled; _biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
} }
@@ -257,15 +257,15 @@ namespace Bit.iOS.Core.Controllers
{ {
EncString userKeyPin = null; EncString userKeyPin = null;
EncString oldPinProtected = null; EncString oldPinProtected = null;
if (_pinStatus == PinLockEnum.Persistent) if (_pinStatus == PinLockType.Persistent)
{ {
userKeyPin = await _stateService.GetUserKeyPinAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
} }
else if (_pinStatus == PinLockEnum.Transient) else if (_pinStatus == PinLockType.Transient)
{ {
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
} }
@@ -273,7 +273,7 @@ namespace Bit.iOS.Core.Controllers
if (oldPinProtected != null) if (oldPinProtected != null)
{ {
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockEnum.Transient, _pinStatus == PinLockType.Transient,
inputtedValue, inputtedValue,
email, email,
kdfConfig, kdfConfig,
@@ -312,18 +312,18 @@ namespace Bit.iOS.Core.Controllers
{ {
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig); var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
var storedPasswordHash = await _cryptoService.GetPasswordHashAsync(); var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync();
if (storedPasswordHash == null) if (storedPasswordHash == null)
{ {
var oldKey = await _secureStorageService.GetAsync<string>("oldKey"); var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
if (masterKey.KeyB64 == oldKey) if (masterKey.KeyB64 == oldKey)
{ {
var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
await _secureStorageService.RemoveAsync("oldKey"); await _secureStorageService.RemoveAsync("oldKey");
await _cryptoService.SetPasswordHashAsync(localPasswordHash); await _cryptoService.SetMasterKeyHashAsync(localPasswordHash);
} }
} }
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey); var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey);
if (passwordValid) if (passwordValid)
{ {
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();

View File

@@ -1,20 +1,20 @@
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 Xamarin.Forms; 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.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Utilities;
using Bit.iOS.Core.Views;
using Foundation;
using UIKit;
using Xamarin.Forms;
namespace Bit.iOS.Core.Controllers namespace Bit.iOS.Core.Controllers
{ {
@@ -30,7 +30,7 @@ namespace Bit.iOS.Core.Controllers
private IPlatformUtilsService _platformUtilsService; private IPlatformUtilsService _platformUtilsService;
private IBiometricService _biometricService; private IBiometricService _biometricService;
private IKeyConnectorService _keyConnectorService; private IKeyConnectorService _keyConnectorService;
private PinLockEnum _pinStatus; private PinLockType _pinStatus;
private bool _pinEnabled; private bool _pinEnabled;
private bool _biometricEnabled; private bool _biometricEnabled;
private bool _biometricIntegrityValid = true; private bool _biometricIntegrityValid = true;
@@ -96,24 +96,24 @@ namespace Bit.iOS.Core.Controllers
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync()) if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
{ {
_passwordReprompt = true; _passwordReprompt = true;
_pinStatus = PinLockEnum.Disabled; _pinStatus = PinLockType.Disabled;
_pinEnabled = false; _pinEnabled = false;
_biometricEnabled = false; _biometricEnabled = false;
} }
else else
{ {
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync(); ?? await _stateService.GetPinProtectedKeyAsync();
_pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
_pinStatus == PinLockEnum.Persistent; _pinStatus == PinLockType.Persistent;
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() _biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
&& await _cryptoService.HasEncryptedUserKeyAsync(); && await _cryptoService.HasEncryptedUserKeyAsync();
_biometricIntegrityValid = _biometricIntegrityValid =
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey); await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector(); _usesKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled; _biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
} }
@@ -129,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)
@@ -224,15 +224,15 @@ namespace Bit.iOS.Core.Controllers
{ {
EncString userKeyPin = null; EncString userKeyPin = null;
EncString oldPinProtected = null; EncString oldPinProtected = null;
if (_pinStatus == PinLockEnum.Persistent) if (_pinStatus == PinLockType.Persistent)
{ {
userKeyPin = await _stateService.GetUserKeyPinAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
} }
else if (_pinStatus == PinLockEnum.Transient) else if (_pinStatus == PinLockType.Transient)
{ {
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
} }
@@ -240,7 +240,7 @@ namespace Bit.iOS.Core.Controllers
if (oldPinProtected != null) if (oldPinProtected != null)
{ {
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockEnum.Transient, _pinStatus == PinLockType.Transient,
inputtedValue, inputtedValue,
email, email,
kdfConfig, kdfConfig,
@@ -284,19 +284,19 @@ namespace Bit.iOS.Core.Controllers
else else
{ {
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig); var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
var storedPasswordHash = await _cryptoService.GetPasswordHashAsync(); var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync();
if (storedPasswordHash == null) if (storedPasswordHash == null)
{ {
var oldKey = await _secureStorageService.GetAsync<string>("oldKey"); var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
if (masterKey.KeyB64 == oldKey) if (masterKey.KeyB64 == oldKey)
{ {
var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
await _secureStorageService.RemoveAsync("oldKey"); await _secureStorageService.RemoveAsync("oldKey");
await _cryptoService.SetPasswordHashAsync(localPasswordHash); await _cryptoService.SetMasterKeyHashAsync(localPasswordHash);
} }
} }
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey); var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey);
if (passwordValid) if (passwordValid)
{ {
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -395,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());

View File

@@ -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)

View File

@@ -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);