diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index 33015c4f6..de40bdd9b 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -157,9 +157,9 @@ namespace Bit.Droid messagingService, broadcasterService); var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService, platformUtilsService, new LazyResolve()); - var biometricService = new BiometricService(stateService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoService = new CryptoService(stateService, cryptoFunctionService); + var biometricService = new BiometricService(stateService, cryptoService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService); ServiceContainer.Register(preferencesStorage); diff --git a/src/Android/Services/BiometricService.cs b/src/Android/Services/BiometricService.cs index 7a41b0319..fbca61cc0 100644 --- a/src/Android/Services/BiometricService.cs +++ b/src/Android/Services/BiometricService.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Android.OS; using Android.Security.Keystore; +using Bit.App.Services; using Bit.Core.Abstractions; using Bit.Core.Services; using Java.Security; @@ -9,10 +10,8 @@ using Javax.Crypto; 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 KeyStoreName = "AndroidKeyStore"; @@ -24,14 +23,14 @@ namespace Bit.Droid.Services private readonly KeyStore _keystore; - public BiometricService(IStateService stateService) + public BiometricService(IStateService stateService, ICryptoService cryptoService) + : base(stateService, cryptoService) { - _stateService = stateService; _keystore = KeyStore.GetInstance(KeyStoreName); _keystore.Load(null); } - public async Task SetupBiometricAsync(string bioIntegritySrcKey = null) + public override async Task SetupBiometricAsync(string bioIntegritySrcKey = null) { if (Build.VERSION.SdkInt >= BuildVersionCodes.M) { @@ -41,7 +40,7 @@ namespace Bit.Droid.Services return true; } - public async Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) + public override async Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) { if (Build.VERSION.SdkInt < BuildVersionCodes.M) { diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index f16c2fafb..2bb5d620b 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -180,7 +180,7 @@ namespace Bit.App.Pages PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || _pinStatus == PinLockType.Persistent; - BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync(); + BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _biometricService.CanUseBiometricsUnlockAsync(); // Users without MP and without biometric or pin has no MP to unlock with _hasMasterPassword = await _userVerificationService.HasMasterPasswordAsync(); @@ -305,7 +305,7 @@ namespace Bit.App.Pages { Pin = string.Empty; await AppHelpers.ResetInvalidUnlockAttemptsAsync(); - await SetKeyAndContinueAsync(userKey); + await SetUserKeyAndContinueAsync(userKey); } } catch @@ -372,7 +372,7 @@ namespace Bit.App.Pages var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey); await _cryptoService.SetMasterKeyAsync(masterKey); - await SetKeyAndContinueAsync(userKey); + await SetUserKeyAndContinueAsync(userKey); // Re-enable biometrics if (BiometricEnabled & !BiometricIntegrityValid) @@ -470,11 +470,12 @@ namespace Bit.App.Pages await _stateService.SetBiometricLockedAsync(!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(); if (!hasKey) diff --git a/src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs b/src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs index ca29c71ae..11e39579e 100644 --- a/src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs @@ -30,14 +30,14 @@ namespace Bit.App.Pages public async Task Init() { - Organization = await _keyConnectorService.GetManagingOrganization(); + Organization = await _keyConnectorService.GetManagingOrganizationAsync(); } public async Task MigrateAccount() { await _deviceActionService.ShowLoadingAsync(AppResources.Loading); - await _keyConnectorService.MigrateUser(); + await _keyConnectorService.MigrateUserAsync(); await _syncService.FullSyncAsync(true); await _deviceActionService.HideLoadingAsync(); diff --git a/src/App/Pages/TabsPage.cs b/src/App/Pages/TabsPage.cs index 861724cf0..88c638101 100644 --- a/src/App/Pages/TabsPage.cs +++ b/src/App/Pages/TabsPage.cs @@ -94,7 +94,7 @@ namespace Bit.App.Pages } }); await UpdateVaultButtonTitleAsync(); - if (await _keyConnectorService.UserNeedsMigration()) + if (await _keyConnectorService.UserNeedsMigrationAsync()) { _messagingService.Send("convertAccountToKeyConnector"); } diff --git a/src/App/Services/BaseBiometricService.cs b/src/App/Services/BaseBiometricService.cs new file mode 100644 index 000000000..3d9ea9cc2 --- /dev/null +++ b/src/App/Services/BaseBiometricService.cs @@ -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 CanUseBiometricsUnlockAsync() + { + return await _cryptoService.HasEncryptedUserKeyAsync() || await _stateService.GetKeyEncryptedAsync() != null; + } + + public abstract Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null); + public abstract Task SetupBiometricAsync(string bioIntegritySrcKey = null); + } +} diff --git a/src/Core/Abstractions/IApiService.cs b/src/Core/Abstractions/IApiService.cs index 68d0340be..917389009 100644 --- a/src/Core/Abstractions/IApiService.cs +++ b/src/Core/Abstractions/IApiService.cs @@ -71,7 +71,7 @@ namespace Bit.Core.Abstractions Task GetOrganizationAutoEnrollStatusAsync(string identifier); Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId, OrganizationUserResetPasswordEnrollmentRequest request); - Task GetMasterKeyFromKeyConnector(string keyConnectorUrl); + Task GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl); Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request); Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request); Task PostConvertToKeyConnector(); diff --git a/src/Core/Abstractions/IBiometricService.cs b/src/Core/Abstractions/IBiometricService.cs index 232b301ca..3ce26c058 100644 --- a/src/Core/Abstractions/IBiometricService.cs +++ b/src/Core/Abstractions/IBiometricService.cs @@ -4,6 +4,7 @@ namespace Bit.Core.Abstractions { public interface IBiometricService { + Task CanUseBiometricsUnlockAsync(); Task SetupBiometricAsync(string bioIntegritySrcKey = null); Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null); } diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index b7d03df2d..4b9bf570a 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -21,6 +21,7 @@ namespace Bit.Core.Abstractions Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); Task GetAutoUnlockKeyAsync(string userId = null); Task HasAutoUnlockKeyAsync(string userId = null); + Task GetBiometricUnlockKeyAsync(string userId = null); Task SetMasterKeyAsync(MasterKey masterKey, string userId = null); Task GetMasterKeyAsync(string userId = null); Task MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig); diff --git a/src/Core/Abstractions/IKeyConnectorService.cs b/src/Core/Abstractions/IKeyConnectorService.cs index 4de3793bb..aeb07fa92 100644 --- a/src/Core/Abstractions/IKeyConnectorService.cs +++ b/src/Core/Abstractions/IKeyConnectorService.cs @@ -6,12 +6,12 @@ namespace Bit.Core.Abstractions { public interface IKeyConnectorService { - Task SetUsesKeyConnector(bool usesKeyConnector); + Task SetUsesKeyConnectorAsync(bool usesKeyConnector); Task GetUsesKeyConnectorAsync(); - Task UserNeedsMigration(); - Task MigrateUser(); - Task GetAndSetKeyAsync(string url); - Task GetManagingOrganization(); + Task UserNeedsMigrationAsync(); + Task MigrateUserAsync(); + Task SetMasterKeyFromUrlAsync(string url); + Task GetManagingOrganizationAsync(); Task ConvertNewUserToKeyConnectorAsync(string orgId, IdentityTokenResponse tokenResponse); } } diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index 5815a883f..5b40b60d6 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -20,7 +20,7 @@ namespace Bit.Core.Abstractions Task GetMasterKeyEncryptedUserKeyAsync(string userId = null); Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); Task GetUserKeyAutoUnlockAsync(string userId = null); - Task SetUserKeyAutoUnlockAsync(string value, string userId = null); + Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null); Task GetActiveUserIdAsync(); Task GetActiveUserEmailAsync(); Task GetActiveUserCustomDataAsync(Func dataMapper); @@ -35,6 +35,8 @@ namespace Bit.Core.Abstractions Task GetPreAuthEnvironmentUrlsAsync(); Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value); Task GetEnvironmentUrlsAsync(string userId = null); + Task GetUserKeyBiometricUnlockAsync(string userId = null); + Task SetUserKeyBiometricUnlockAsync(UserKey value, string userId = null); Task GetBiometricUnlockAsync(string userId = null); Task SetBiometricUnlockAsync(bool? value, string userId = null); Task GetBiometricLockedAsync(string userId = null); diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 3c0b4fceb..7c4b686f7 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -87,6 +87,7 @@ namespace Bit.Core public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}"; public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}"; public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}"; + public static string UserKeyBiometricUnlockKey(string userId) => $"biometricUnlock_{userId}"; public static string CiphersKey(string userId) => $"ciphers_{userId}"; public static string FoldersKey(string userId) => $"folders_{userId}"; public static string CollectionsKey(string userId) => $"collections_{userId}"; diff --git a/src/Core/Models/Domain/Account.cs b/src/Core/Models/Domain/Account.cs index 7b6ed9496..3377d60a7 100644 --- a/src/Core/Models/Domain/Account.cs +++ b/src/Core/Models/Domain/Account.cs @@ -121,7 +121,7 @@ namespace Bit.Core.Models.Domain { public UserKey UserKey; public MasterKey MasterKey; - public EncString UserKeyPinEphemeral; + public EncString PinKeyEncryptedUserKeyEphemeral; public bool? BiometricLocked; [Obsolete("Jul 6 2023: Key has been deprecated. We will use the User Key in the future. It remains here for migration during app upgrade.")] public SymmetricCryptoKey Key; diff --git a/src/Core/Services/ApiService.cs b/src/Core/Services/ApiService.cs index d8c0ed7d4..7734e5796 100644 --- a/src/Core/Services/ApiService.cs +++ b/src/Core/Services/ApiService.cs @@ -511,7 +511,7 @@ namespace Bit.Core.Services #region Key Connector - public async Task GetMasterKeyFromKeyConnector(string keyConnectorUrl) + public async Task GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl) { using (var requestMessage = new HttpRequestMessage()) { diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 24f979f0f..83f98ca63 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -48,7 +48,6 @@ namespace Bit.Core.Services II18nService i18nService, IPlatformUtilsService platformUtilsService, IMessagingService messagingService, - IVaultTimeoutService vaultTimeoutService, IKeyConnectorService keyConnectorService, IPasswordGenerationService passwordGenerationService, IPolicyService policyService, @@ -510,6 +509,7 @@ namespace Bit.Core.Services await _cryptoService.SetUserKeyAsync(userKey); } + // Trusted Device var decryptOptions = await _stateService.GetAccountDecryptionOptions(); var hasUserKey = await _cryptoService.HasUserKeyAsync(); if (decryptOptions?.TrustedDeviceOption != null && !hasUserKey) @@ -524,22 +524,24 @@ namespace Bit.Core.Services if (code == null || tokenResponse.Key != null) { await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key); + + // Key Connector if (!string.IsNullOrEmpty(tokenResponse.KeyConnectorUrl) || !string.IsNullOrEmpty(decryptOptions?.KeyConnectorOption?.KeyConnectorUrl)) { - - await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key); - if (masterKey != null) - { - await _cryptoService.SetMasterKeyAsync(masterKey); - var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey); - await _cryptoService.SetUserKeyAsync(userKey); - } + var url = tokenResponse.KeyConnectorUrl ?? decryptOptions.KeyConnectorOption.KeyConnectorUrl; + await _keyConnectorService.SetMasterKeyFromUrlAsync(url); } // Login with Device if (masterKey != null && !string.IsNullOrEmpty(authRequestId)) { await _cryptoService.SetMasterKeyAsync(masterKey); + } + + // Decrypt UserKey with MasterKey + masterKey ??= await _stateService.GetMasterKeyAsync(); + if (masterKey != null) + { var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey); await _cryptoService.SetUserKeyAsync(userKey); } @@ -571,7 +573,7 @@ namespace Bit.Core.Services } else { - await _keyConnectorService.GetAndSetKeyAsync(tokenResponse.KeyConnectorUrl); + await _keyConnectorService.SetMasterKeyFromUrlAsync(tokenResponse.KeyConnectorUrl); } } } diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 3f0974a49..34d87018d 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -101,7 +101,7 @@ namespace Bit.Core.Services public async Task GetAutoUnlockKeyAsync(string userId = null) { - await MigrateAutoUnlockKeyIfNeededAsync(userId); + await MigrateAutoAndBioKeysIfNeededAsync(userId); return await _stateService.GetUserKeyAutoUnlockAsync(userId); } @@ -110,6 +110,12 @@ namespace Bit.Core.Services return await GetAutoUnlockKeyAsync(userId) != null; } + public async Task 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); @@ -149,7 +155,7 @@ namespace Bit.Core.Services public async Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey) { var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync(); - return await BuildProtectedSymmetricKey(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes)); + return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes)); } public async Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null) @@ -163,10 +169,27 @@ namespace Bit.Core.Services if (encUserKey == null) { 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); + await _stateService.SetEncKeyEncryptedAsync(null, userId); + return userKey; } + encUserKey = new EncString(userKeyMasterKey); } @@ -204,7 +227,7 @@ namespace Bit.Core.Services } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); + return await BuildProtectedSymmetricKeyAsync(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } public async Task HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) @@ -676,10 +699,10 @@ namespace Bit.Core.Services private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null) { - // Refresh, set, or clear the pin key + // Set, refresh, or clear the pin key if (await _stateService.GetProtectedPinAsync(userId) != null) { - await UpdateUserKeyPinAsync(userKey, userId); + await UpdatePinKeyAsync(userKey, userId); } else { @@ -687,18 +710,28 @@ namespace Bit.Core.Services await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId); } - // Refresh, set, or clear the auto key + // Set, refresh, or clear the auto unlock key if (await _stateService.GetVaultTimeoutAsync(userId) == null) { - await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId); + await _stateService.SetUserKeyAutoUnlockAsync(userKey, userId); } else { await _stateService.SetUserKeyAutoUnlockAsync(null, userId); } + + // Set, refresh, or clear the biometric unlock key + if (await _stateService.GetBiometricUnlockAsync(userId) is true) + { + await _stateService.SetUserKeyBiometricUnlockAsync(userKey, userId); + } + else + { + await _stateService.SetUserKeyBiometricUnlockAsync(null, userId); + } } - private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null) + private async Task UpdatePinKeyAsync(UserKey userKey, string userId = null) { var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId))); var pinKey = await MakePinKeyAsync( @@ -880,7 +913,7 @@ namespace Bit.Core.Services } // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack - private async Task> BuildProtectedSymmetricKey(SymmetricCryptoKey key, + private async Task> BuildProtectedSymmetricKeyAsync(SymmetricCryptoKey key, byte[] encKey, Func keyCreator) where TKey : SymmetricCryptoKey { EncString encKeyEnc = null; @@ -904,7 +937,7 @@ namespace Bit.Core.Services private async Task MakeKeyAsync(string password, string salt, KdfConfig kdfConfig, Func keyCreator) where TKey : SymmetricCryptoKey { - byte[] key = null; + byte[] key; if (kdfConfig.Type == null || kdfConfig.Type == KdfType.PBKDF2_SHA256) { var iterations = kdfConfig.Iterations.GetValueOrDefault(5000); @@ -962,23 +995,33 @@ namespace Bit.Core.Services // 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. - private async Task MigrateAutoUnlockKeyIfNeededAsync(string userId = null) + private async Task MigrateAutoAndBioKeysIfNeededAsync(string userId = null) { - var oldAutoKey = await _stateService.GetKeyEncryptedAsync(userId); - if (oldAutoKey == null) + var oldKey = await _stateService.GetKeyEncryptedAsync(userId); + if (oldKey == null) { return; } + // Decrypt - var masterKey = new MasterKey(Convert.FromBase64String(oldAutoKey)); + var masterKey = new MasterKey(Convert.FromBase64String(oldKey)); var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId); var userKey = await DecryptUserKeyWithMasterKeyAsync( masterKey, new EncString(encryptedUserKey), userId); + // Migrate - await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId); + if (await _stateService.GetVaultTimeoutAsync(userId) == null) + { + await _stateService.SetUserKeyAutoUnlockAsync(userKey, userId); + } + if (await _stateService.GetBiometricUnlockAsync(userId) is true) + { + 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); } diff --git a/src/Core/Services/KeyConnectorService.cs b/src/Core/Services/KeyConnectorService.cs index 85d1ae1b4..b80a446c5 100644 --- a/src/Core/Services/KeyConnectorService.cs +++ b/src/Core/Services/KeyConnectorService.cs @@ -27,13 +27,13 @@ namespace Bit.Core.Services _organizationService = organizationService; } - public async Task GetAndSetKeyAsync(string url) + public async Task SetMasterKeyFromUrlAsync(string url) { try { - var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnector(url); - var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key); - var masterKey = new MasterKey(masterKeyArr); + var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnectorAsync(url); + var masterKeyBytes = Convert.FromBase64String(masterKeyResponse.Key); + var masterKey = new MasterKey(masterKeyBytes); await _cryptoService.SetMasterKeyAsync(masterKey); } catch (Exception e) @@ -42,7 +42,7 @@ namespace Bit.Core.Services } } - public async Task SetUsesKeyConnector(bool usesKeyConnector) + public async Task SetUsesKeyConnectorAsync(bool usesKeyConnector) { await _stateService.SetUsesKeyConnectorAsync(usesKeyConnector); } @@ -52,7 +52,7 @@ namespace Bit.Core.Services return await _stateService.GetUsesKeyConnectorAsync(); } - public async Task GetManagingOrganization() + public async Task GetManagingOrganizationAsync() { var orgs = await _organizationService.GetAllAsync(); return orgs.Find(o => @@ -60,9 +60,9 @@ namespace Bit.Core.Services !o.IsAdmin); } - public async Task MigrateUser() + public async Task MigrateUserAsync() { - var organization = await GetManagingOrganization(); + var organization = await GetManagingOrganizationAsync(); var masterKey = await _cryptoService.GetMasterKeyAsync(); try @@ -78,10 +78,10 @@ namespace Bit.Core.Services await _apiService.PostConvertToKeyConnector(); } - public async Task UserNeedsMigration() + public async Task UserNeedsMigrationAsync() { var loggedInUsingSso = await _tokenService.GetIsExternal(); - var requiredByOrganization = await GetManagingOrganization() != null; + var requiredByOrganization = await GetManagingOrganizationAsync() != null; var userIsNotUsingKeyConnector = !await GetUsesKeyConnectorAsync(); return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector; diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index ea032ea5d..d4d466c14 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -241,6 +241,19 @@ namespace Bit.Core.Services ))?.Settings?.EnvironmentUrls; } + public async Task GetUserKeyBiometricUnlockAsync(string userId = null) + { + var keyB64 = await _storageMediatorService.GetAsync( + 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 GetBiometricUnlockAsync(string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -353,10 +366,10 @@ namespace Bit.Core.Services return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64)); } - public async Task SetUserKeyAutoUnlockAsync(string value, string userId = null) + public async Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null) { await _storageMediatorService.SaveAsync( - await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value, true); + await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value?.KeyB64, true); } public async Task CanAccessPremiumAsync(string userId = null) @@ -428,7 +441,7 @@ namespace Bit.Core.Services { return (await GetAccountAsync( ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) - ))?.VolatileData?.UserKeyPinEphemeral; + ))?.VolatileData?.PinKeyEncryptedUserKeyEphemeral; } public async Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null) @@ -436,7 +449,7 @@ namespace Bit.Core.Services var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()); var account = await GetAccountAsync(reconciledOptions); - account.VolatileData.UserKeyPinEphemeral = value; + account.VolatileData.PinKeyEncryptedUserKeyEphemeral = value; await SaveAccountAsync(account, reconciledOptions); } @@ -1520,6 +1533,7 @@ namespace Bit.Core.Services // Non-state storage await Task.WhenAll( SetUserKeyAutoUnlockAsync(null, userId), + SetUserKeyBiometricUnlockAsync(null, userId), SetProtectedPinAsync(null, userId), SetKeyHashAsync(null, userId), SetOrgKeysEncryptedAsync(null, userId), diff --git a/src/Core/Services/SyncService.cs b/src/Core/Services/SyncService.cs index e4dfd6660..40abff9d3 100644 --- a/src/Core/Services/SyncService.cs +++ b/src/Core/Services/SyncService.cs @@ -337,7 +337,7 @@ namespace Bit.Core.Services await _stateService.SetNameAsync(response.Name); await _stateService.SetPersonalPremiumAsync(response.Premium); await _stateService.SetAvatarColorAsync(response.AvatarColor); - await _keyConnectorService.SetUsesKeyConnector(response.UsesKeyConnector); + await _keyConnectorService.SetUsesKeyConnectorAsync(response.UsesKeyConnector); } private async Task SyncFoldersAsync(string userId, List response) diff --git a/src/Core/Utilities/ServiceContainer.cs b/src/Core/Utilities/ServiceContainer.cs index 39c6aa797..4a85ab354 100644 --- a/src/Core/Utilities/ServiceContainer.cs +++ b/src/Core/Utilities/ServiceContainer.cs @@ -82,7 +82,7 @@ namespace Bit.Core.Utilities var deviceTrustCryptoService = new DeviceTrustCryptoService(apiService, appIdService, cryptoFunctionService, cryptoService, stateService); var passwordResetEnrollmentService = new PasswordResetEnrollmentService(apiService, cryptoService, organizationService, stateService); var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService, - tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService, + tokenService, appIdService, i18nService, platformUtilsService, messagingService, keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService, passwordResetEnrollmentService); var exportService = new ExportService(folderService, cipherService, cryptoService); var auditService = new AuditService(cryptoFunctionService, apiService); diff --git a/src/iOS.Core/Services/BiometricService.cs b/src/iOS.Core/Services/BiometricService.cs index d34a4adff..3e68fdf66 100644 --- a/src/iOS.Core/Services/BiometricService.cs +++ b/src/iOS.Core/Services/BiometricService.cs @@ -1,20 +1,19 @@ using System.Threading.Tasks; +using Bit.App.Services; using Bit.Core.Abstractions; using Foundation; using LocalAuthentication; namespace Bit.iOS.Core.Services { - public class BiometricService : IBiometricService + public class BiometricService : BaseBiometricService { - private IStateService _stateService; - - public BiometricService(IStateService stateService) + public BiometricService(IStateService stateService, ICryptoService cryptoService) + : base(stateService, cryptoService) { - _stateService = stateService; } - public async Task SetupBiometricAsync(string bioIntegritySrcKey = null) + public override async Task SetupBiometricAsync(string bioIntegritySrcKey = null) { if (bioIntegritySrcKey == null) { @@ -30,7 +29,7 @@ namespace Bit.iOS.Core.Services return true; } - public async Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) + public override async Task IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) { var state = GetState(); if (state == null) diff --git a/src/iOS.Core/Utilities/iOSCoreHelpers.cs b/src/iOS.Core/Utilities/iOSCoreHelpers.cs index fa76caccd..14588cd59 100644 --- a/src/iOS.Core/Utilities/iOSCoreHelpers.cs +++ b/src/iOS.Core/Utilities/iOSCoreHelpers.cs @@ -112,9 +112,9 @@ namespace Bit.iOS.Core.Utilities var clipboardService = new ClipboardService(stateService); var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService, messagingService, broadcasterService); - var biometricService = new BiometricService(stateService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoService = new CryptoService(stateService, cryptoFunctionService); + var biometricService = new BiometricService(stateService, cryptoService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService); ServiceContainer.Register(preferencesStorage);