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);
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
platformUtilsService, new LazyResolve<IEventService>());
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<ISynchronousStorageService>(preferencesStorage);

View File

@@ -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<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
public override async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
@@ -41,7 +40,7 @@ namespace Bit.Droid.Services
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)
{

View File

@@ -38,7 +38,7 @@ namespace Bit.App.Pages
private string _masterPassword;
private string _pin;
private bool _showPassword;
private PinLockEnum _pinStatus;
private PinLockType _pinStatus;
private bool _pinEnabled;
private bool _biometricEnabled;
private bool _biometricIntegrityValid = true;
@@ -161,17 +161,17 @@ namespace Bit.App.Pages
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();
PinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
_pinStatus == PinLockEnum.Persistent;
PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
_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
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnectorAsync();
if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))
{
await _vaultTimeoutService.LogOutAsync();
@@ -257,15 +257,15 @@ namespace Bit.App.Pages
{
EncString userKeyPin = 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();
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();
}
@@ -273,7 +273,7 @@ namespace Bit.App.Pages
if (oldPinProtected != null)
{
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockEnum.Transient,
_pinStatus == PinLockType.Transient,
Pin,
_email,
kdfConfig,
@@ -297,7 +297,7 @@ namespace Bit.App.Pages
{
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(userKey);
await SetUserKeyAndContinueAsync(userKey);
}
}
catch
@@ -319,20 +319,20 @@ namespace Bit.App.Pages
else
{
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetPasswordHashAsync();
var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync();
var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null)
{
// Offline unlock possible
passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey);
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, masterKey);
}
else
{
// Online unlock required
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();
request.MasterPasswordHash = keyHash;
@@ -341,8 +341,8 @@ namespace Bit.App.Pages
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
await _cryptoService.SetPasswordHashAsync(localKeyHash);
var localKeyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
await _cryptoService.SetMasterKeyHashAsync(localKeyHash);
}
catch (Exception e)
{
@@ -364,7 +364,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)
@@ -462,11 +462,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)

View File

@@ -178,11 +178,8 @@ namespace Bit.App.Pages
Email = Email.Trim().ToLower();
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
newMasterKey,
await _cryptoService.MakeUserKeyAsync()
);
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new RegisterRequest
{

View File

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

View File

@@ -166,13 +166,12 @@ namespace Bit.App.Pages
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
var email = await _stateService.GetEmailAsync();
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig);
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
var keys = await _cryptoService.MakeKeyPairAsync(newUserKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new SetPasswordRequest
{
MasterPasswordHash = masterPasswordHash,
@@ -185,8 +184,8 @@ namespace Bit.App.Pages
OrgIdentifier = OrgIdentifier,
Keys = new KeysRequest
{
PublicKey = keys.Item1,
EncryptedPrivateKey = keys.Item2.EncryptedString
PublicKey = newPublicKey,
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
}
};
@@ -196,10 +195,11 @@ namespace Bit.App.Pages
// Set Password and relevant information
await _apiService.SetPasswordAsync(request);
await _stateService.SetKdfConfigurationAsync(kdfConfig);
await _cryptoService.SetUserKeyAsync(newUserKey);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash);
await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString);
await _cryptoService.SetUserPrivateKeyAsync(newProtectedPrivateKey.EncryptedString);
if (ResetPasswordAutoEnroll)
{

View File

@@ -95,7 +95,7 @@ namespace Bit.App.Pages
// Create new master key and hash new password
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
var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey);
@@ -155,7 +155,7 @@ namespace Bit.App.Pages
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
{

View File

@@ -67,7 +67,7 @@ namespace Bit.App.Pages
_initialized = true;
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnector();
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnectorAsync();
if (UseOTPVerification)
{
@@ -165,7 +165,7 @@ namespace Bit.App.Pages
return;
}
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
var verificationType = await _keyConnectorService.GetUsesKeyConnectorAsync()
? VerificationType.OTP
: VerificationType.MasterPassword;
if (!await _userVerificationService.VerifyUser(Secret, verificationType))

View File

@@ -138,8 +138,8 @@ namespace Bit.App.Pages
t.Value != null).ToList();
}
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet != PinLockEnum.Disabled;
var pinSet = await _vaultTimeoutService.GetPinLockTypeAsync();
_pin = pinSet != PinLockType.Disabled;
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
@@ -149,7 +149,7 @@ namespace Bit.App.Pages
}
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
!await _keyConnectorService.GetUsesKeyConnector();
!await _keyConnectorService.GetUsesKeyConnectorAsync();
_reportLoggingEnabled = await _loggerService.IsEnabled();
_approvePasswordlessLoginRequests = await _stateService.GetApprovePasswordlessLoginsAsync();
_shouldConnectToWatch = await _stateService.GetShouldConnectToWatchAsync();
@@ -323,6 +323,7 @@ namespace Bit.App.Pages
}
if (oldTimeout != newTimeout)
{
await _cryptoService.RefreshKeysAsync();
await Device.InvokeOnMainThreadAsync(BuildList);
}
}
@@ -428,7 +429,7 @@ namespace Bit.App.Pages
if (!string.IsNullOrWhiteSpace(pin))
{
var masterPassOnRestart = false;
if (!await _keyConnectorService.GetUsesKeyConnector())
if (!await _keyConnectorService.GetUsesKeyConnectorAsync())
{
masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
@@ -439,18 +440,18 @@ namespace Bit.App.Pages
var email = await _stateService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig);
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);
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
if (masterPassOnRestart)
{
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey);
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(protectedPinKey);
}
else
{
await _stateService.SetUserKeyPinAsync(pinProtectedKey);
await _stateService.SetPinKeyEncryptedUserKeyAsync(protectedPinKey);
}
}
else
@@ -491,7 +492,7 @@ namespace Bit.App.Pages
await _stateService.SetBiometricUnlockAsync(null);
}
await _stateService.SetBiometricLockedAsync(false);
await _cryptoService.ToggleKeysAsync();
await _cryptoService.RefreshKeysAsync();
BuildList();
}

View File

@@ -94,7 +94,7 @@ namespace Bit.App.Pages
}
});
await UpdateVaultButtonTitleAsync();
if (await _keyConnectorService.UserNeedsMigration())
if (await _keyConnectorService.UserNeedsMigrationAsync())
{
_messagingService.Send("convertAccountToKeyConnector");
}

View File

@@ -176,7 +176,7 @@ namespace Bit.App.Pages
}
});
// Hide password reprompt option if using key connector
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnector();
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnectorAsync();
}
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 await _cryptoService.CompareAndUpdatePasswordHashAsync(password, null);
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
}
public async Task<bool> Enabled()
{
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()
{
await AppHelpers.ClearServiceCacheAsync();
await Device.InvokeOnMainThreadAsync(() =>
{
Options.HideAccountSwitcher = false;

View File

@@ -107,7 +107,7 @@ namespace Bit.App.Utilities
public async Task ValidateAndExecuteAsync()
{
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
var verificationType = await _keyConnectorService.GetUsesKeyConnectorAsync()
? VerificationType.OTP
: VerificationType.MasterPassword;
@@ -121,7 +121,7 @@ namespace Bit.App.Utilities
}
var parameters = GetParameters();
parameters.Secret = await _cryptoService.HashPasswordAsync(password, null);
parameters.Secret = await _cryptoService.HashMasterKeyAsync(password, null);
parameters.VerificationType = VerificationType.MasterPassword;
await ExecuteAsync(parameters);
break;

View File

@@ -70,7 +70,7 @@ namespace Bit.Core.Abstractions
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
OrganizationUserResetPasswordEnrollmentRequest request);
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnector(string keyConnectorUrl);
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl);
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
Task PostConvertToKeyConnector();

View File

@@ -4,6 +4,7 @@ namespace Bit.Core.Abstractions
{
public interface IBiometricService
{
Task<bool> CanUseBiometricsUnlockAsync();
Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null);
Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
}

View File

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

View File

@@ -6,11 +6,11 @@ namespace Bit.Core.Abstractions
{
public interface IKeyConnectorService
{
Task SetUsesKeyConnector(bool usesKeyConnector);
Task<bool> GetUsesKeyConnector();
Task<bool> UserNeedsMigration();
Task MigrateUser();
Task GetAndSetKey(string url);
Task<Organization> GetManagingOrganization();
Task SetUsesKeyConnectorAsync(bool usesKeyConnector);
Task<bool> GetUsesKeyConnectorAsync();
Task<bool> UserNeedsMigrationAsync();
Task MigrateUserAsync();
Task GetAndSetMasterKeyAsync(string url);
Task<Organization> GetManagingOrganizationAsync();
}
}

View File

@@ -17,8 +17,10 @@ namespace Bit.Core.Abstractions
Task SetUserKeyAsync(UserKey value, string userId = null);
Task<MasterKey> GetMasterKeyAsync(string userId = null);
Task SetMasterKeyAsync(MasterKey value, string userId = null);
Task<string> GetUserKeyMasterKeyAsync(string userId = null);
Task SetUserKeyMasterKeyAsync(string value, string userId = null);
Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null);
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null);
Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null);
Task<string> GetActiveUserIdAsync();
Task<string> GetActiveUserEmailAsync();
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
@@ -33,6 +35,8 @@ namespace Bit.Core.Abstractions
Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync();
Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value);
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 SetBiometricUnlockAsync(bool? value, string userId = null);
Task<bool> GetBiometricLockedAsync(string userId = null);
@@ -44,19 +48,11 @@ namespace Bit.Core.Abstractions
Task<bool> CanAccessPremiumAsync(string userId = null);
Task<string> GetProtectedPinAsync(string userId = null);
Task SetPersonalPremiumAsync(bool value, string userId = null);
Task<EncString> GetUserKeyPinAsync(string userId = null);
Task SetUserKeyPinAsync(EncString value, string userId = null);
Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null);
Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null);
Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null);
Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null);
Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null);
Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null);
Task SetProtectedPinAsync(string value, string userId = null);
[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<string> GetKeyHashAsync(string userId = null);
Task SetKeyHashAsync(string value, string userId = null);
@@ -184,17 +180,24 @@ namespace Bit.Core.Abstractions
void SetLocale(string locale);
ConfigResponse GetConfigs();
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);
[Obsolete("Use SetUserKeyMasterKey instead")]
[Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
Task SetEncKeyEncryptedAsync(string value, string userId = null);
[Obsolete]
Task<string> GetKeyEncryptedAsync(string userId = null);
[Obsolete]
[Obsolete("Left for migration purposes")]
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);
[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> IsLoggedOutByTimeoutAsync(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 LockAsync(bool allowSoftLock = false, bool userInitiated = false, 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 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 FoldersKey(string userId) => $"folders_{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 EncPrivateKeyKey(string userId) => $"encPrivateKey_{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 PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";

View File

@@ -119,7 +119,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;

View File

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

View File

@@ -46,8 +46,6 @@ namespace Bit.Core.Services
II18nService i18nService,
IPlatformUtilsService platformUtilsService,
IMessagingService messagingService,
IVaultTimeoutService vaultTimeoutService,
IKeyConnectorService keyConnectorService,
IPasswordGenerationService passwordGenerationService,
IPolicyService policyService,
bool setCryptoKeys = true)
@@ -61,7 +59,6 @@ namespace Bit.Core.Services
_i18nService = i18nService;
_platformUtilsService = platformUtilsService;
_messagingService = messagingService;
_keyConnectorService = keyConnectorService;
_passwordGenerationService = passwordGenerationService;
_policyService = policyService;
_setCryptoKeys = setCryptoKeys;
@@ -145,8 +142,8 @@ namespace Bit.Core.Services
SelectedTwoFactorProviderType = null;
_2faForcePasswordResetReason = null;
var key = await MakePreloginKeyAsync(masterPassword, email);
var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key);
var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization);
var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key);
var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization);
var result = await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null, captchaToken);
if (await RequirePasswordChangeAsync(email, masterPassword))
@@ -236,8 +233,8 @@ namespace Bit.Core.Services
{
SelectedTwoFactorProviderType = null;
var key = await MakePreloginKeyAsync(masterPassword, email);
var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key);
var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization);
var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key);
var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization);
return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, twoFactorProvider,
twoFactorToken, remember);
}
@@ -473,14 +470,14 @@ namespace Bit.Core.Services
if (localHashedPassword != null)
{
await _cryptoService.SetPasswordHashAsync(localHashedPassword);
await _cryptoService.SetMasterKeyHashAsync(localHashedPassword);
}
if (code == null || tokenResponse.Key != null)
{
if (tokenResponse.KeyConnectorUrl != null)
{
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
await _keyConnectorService.GetAndSetMasterKeyAsync(tokenResponse.KeyConnectorUrl);
}
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
@@ -508,21 +505,25 @@ namespace Bit.Core.Services
catch { }
}
await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey);
await _cryptoService.SetUserPrivateKeyAsync(tokenResponse.PrivateKey);
}
else if (tokenResponse.KeyConnectorUrl != null)
{
// SSO Key Connector Onboarding
var password = await _cryptoFunctionService.RandomBytesAsync(64);
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(
Convert.ToBase64String(password),
_tokenService.GetEmail(),
tokenResponse.KdfConfig);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
newMasterKey,
await _cryptoService.MakeUserKeyAsync());
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
await _cryptoService.SetUserKeyAsync(newUserKey);
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
try

View File

@@ -249,8 +249,7 @@ namespace Bit.Core.Services
{
try
{
var hashKey = await _cryptoService.HasUserKeyAsync();
if (!hashKey)
if (!await _cryptoService.HasUserKeyAsync())
{
throw new Exception("No key.");
}
@@ -557,20 +556,9 @@ namespace Bit.Core.Services
public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)
{
SymmetricCryptoKey attachmentKey;
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 (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(cipher.OrganizationId);
var encFileName = await _cryptoService.EncryptAsync(filename, orgKey);
var encFileName = await _cryptoService.EncryptAsync(filename, encKey);
var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey);
CipherResponse response;
@@ -807,6 +795,14 @@ namespace Bit.Core.Services
// 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,
string organizationId)
{
@@ -819,20 +815,9 @@ namespace Bit.Core.Services
var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync();
var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null);
SymmetricCryptoKey attachmentKey;
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 (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId);
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, orgKey);
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, encKey);
var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey);
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);

View File

@@ -20,7 +20,7 @@ namespace Bit.Core.Services
private readonly ICryptoFunctionService _cryptoFunctionService;
private SymmetricCryptoKey _legacyEtmKey;
private string _passwordHash;
private string _masterKeyHash;
private byte[] _publicKey;
private byte[] _privateKey;
private Dictionary<string, OrgKey> _orgKeys;
@@ -37,42 +37,28 @@ namespace Bit.Core.Services
public void ClearCache()
{
_legacyEtmKey = null;
_passwordHash = null;
_masterKeyHash = null;
_publicKey = null;
_privateKey = 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());
// 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)
{
await _stateService.SetUserKeyAsync(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);
}
await StoreAdditionalKeysAsync(userKey, 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)
@@ -85,7 +71,7 @@ namespace Bit.Core.Services
// 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.
return (SymmetricCryptoKey)await GetMasterKeyAsync() as UserKey;
return new UserKey((await GetMasterKeyAsync()).Key);
}
public async Task<bool> HasUserKeyAsync(string userId = null)
@@ -95,7 +81,7 @@ namespace Bit.Core.Services
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()
@@ -103,20 +89,36 @@ namespace Bit.Core.Services
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)
@@ -125,7 +127,7 @@ namespace Bit.Core.Services
if (masterKey == null)
{
// Migration support
masterKey = await _stateService.GetKeyDecryptedAsync(userId) as MasterKey;
masterKey = new MasterKey((await _stateService.GetKeyDecryptedAsync(userId)).Key);
if (masterKey != null)
{
await SetMasterKeyAsync(masterKey, userId);
@@ -134,20 +136,20 @@ namespace Bit.Core.Services
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();
return await BuildProtectedSymmetricKey<UserKey>(masterKey, userKey.Key);
var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync();
return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
}
public async Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null)
@@ -160,11 +162,27 @@ namespace Bit.Core.Services
if (encUserKey == null)
{
var userKeyMasterKey = await _stateService.GetUserKeyMasterKeyAsync(userId);
if (userKeyMasterKey == null)
var userKeyMasterKey = await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId);
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);
}
@@ -175,12 +193,12 @@ namespace Bit.Core.Services
}
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);
}
else
{
throw new Exception("Unsupported encKey type.");
throw new Exception($"Unsupported encrypted user key type: {encUserKey.EncryptionType}");
}
if (decUserKey == null)
@@ -190,86 +208,82 @@ namespace Bit.Core.Services
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);
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);
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey);
}
if (masterKey is null)
{
masterKey = await GetMasterKeyAsync();
// TODO(Jake): Uses Master Key
public async Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization)
if (masterKey is null)
{
if (key == null)
{
key = await GetMasterKeyAsync();
throw new ArgumentNullException(nameof(masterKey));
}
if (password == null || key == null)
{
throw new Exception("Invalid parameters.");
}
var iterations = hashPurpose == HashPurpose.LocalAuthorization ? 2 : 1;
var hash = await _cryptoFunctionService.Pbkdf2Async(key.Key, password, CryptoHashAlgorithm.Sha256, iterations);
var hash = await _cryptoFunctionService.Pbkdf2Async(masterKey.Key, password, CryptoHashAlgorithm.Sha256, (int)hashPurpose);
return Convert.ToBase64String(hash);
}
public async Task SetPasswordHashAsync(string keyHash)
public Task SetMasterKeyHashAsync(string keyHash)
{
_passwordHash = keyHash;
await _stateService.SetKeyHashAsync(keyHash);
_masterKeyHash = 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();
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;
await _stateService.SetKeyHashAsync(null, userId);
_masterKeyHash = null;
return _stateService.SetKeyHashAsync(null, userId);
}
// TODO(Jake): Uses Master Key
public async Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key)
public async Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key)
{
var storedPasswordHash = await GetPasswordHashAsync();
var storedPasswordHash = await GetMasterKeyHashAsync();
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)
{
return true;
}
var serverPasswordHash = await HashPasswordAsync(masterPassword, key, HashPurpose.ServerAuthorization);
if (serverPasswordHash != null & storedPasswordHash == serverPasswordHash)
var serverPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.ServerAuthorization);
if (serverPasswordHash != null && storedPasswordHash == serverPasswordHash)
{
await SetPasswordHashAsync(localPasswordHash);
await SetMasterKeyHashAsync(localPasswordHash);
return true;
}
}
@@ -277,11 +291,11 @@ namespace Bit.Core.Services
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);
_orgKeys = null;
await _stateService.SetOrgKeysEncryptedAsync(orgKeys);
return _stateService.SetOrgKeysEncryptedAsync(orgKeys);
}
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)
{
return _publicKey;
}
var privateKey = await GetPrivateKeyAsync();
var privateKey = await GetUserPrivateKeyAsync();
if (privateKey == null)
{
return null;
@@ -366,7 +380,7 @@ namespace Bit.Core.Services
return _publicKey;
}
public async Task SetPrivateKeyAsync(string encPrivateKey)
public async Task SetUserPrivateKeyAsync(string encPrivateKey)
{
if (encPrivateKey == null)
{
@@ -376,7 +390,7 @@ namespace Bit.Core.Services
_privateKey = null;
}
public async Task<byte[]> GetPrivateKeyAsync()
public async Task<byte[]> GetUserPrivateKeyAsync()
{
if (_privateKey != null)
{
@@ -395,7 +409,7 @@ namespace Bit.Core.Services
{
if (publicKey == null)
{
publicKey = await GetPublicKeyAsync();
publicKey = await GetUserPublicKeyAsync();
}
if (publicKey == null)
{
@@ -426,28 +440,29 @@ namespace Bit.Core.Services
public async Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config)
{
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);
await _stateService.SetUserKeyPinEphemeralAsync(null, userId);
await _stateService.SetProtectedPinAsync(null, userId);
await clearDeprecatedPinKeysAsync(userId);
return Task.WhenAll(
_stateService.SetPinKeyEncryptedUserKeyAsync(null, userId),
_stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId),
_stateService.SetProtectedPinAsync(null, userId),
ClearDeprecatedPinKeysAsync(userId));
}
public async Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null)
{
pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync();
pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync();
pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyAsync();
pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
if (pinProtectedUserKey == null)
{
throw new Exception("No PIN protected user key found.");
}
var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
return new UserKey(userKey);
var userKeyBytes = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
return new UserKey(userKeyBytes);
}
// Only for migration purposes
@@ -481,7 +496,7 @@ namespace Bit.Core.Services
{
if (publicKey == null)
{
publicKey = await GetPublicKeyAsync();
publicKey = await GetUserPublicKeyAsync();
}
if (publicKey == null)
{
@@ -521,7 +536,7 @@ namespace Bit.Core.Services
if (privateKey is null)
{
privateKey = await GetPrivateKeyAsync();
privateKey = await GetUserPrivateKeyAsync();
}
if (privateKey == null)
@@ -618,17 +633,17 @@ namespace Bit.Core.Services
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 data = Convert.FromBase64String(encString.Data);
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);
}
@@ -675,7 +690,41 @@ namespace Bit.Core.Services
// --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 pinKey = await MakePinKeyAsync(
@@ -685,13 +734,12 @@ namespace Bit.Core.Services
);
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.GetUserKeyPinAsync(userId) != null)
if (await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null)
{
await _stateService.SetUserKeyPinAsync(encPin, userId);
await _stateService.SetPinKeyEncryptedUserKeyAsync(encPin, userId);
return;
}
await _stateService.SetUserKeyPinEphemeralAsync(encPin, userId);
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(encPin, userId);
}
private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key)
@@ -821,14 +869,16 @@ namespace Bit.Core.Services
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 enc = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("enc"), 32, HkdfAlgorithm.Sha256);
Buffer.BlockCopy(enc, 0, newKey, 0, 32);
var mac = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("mac"), 32, HkdfAlgorithm.Sha256);
Buffer.BlockCopy(mac, 0, newKey, 32, 32);
return new SymmetricCryptoKey(newKey);
return keyCreator(newKey);
}
private List<string> HashPhrase(byte[] hash, int minimumEntropy = 64)
@@ -855,13 +905,14 @@ namespace Bit.Core.Services
return phrase;
}
private async Task<Tuple<T, EncString>> BuildProtectedSymmetricKey<T>(SymmetricCryptoKey key,
byte[] encKey) where T : SymmetricCryptoKey
// TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack
private async Task<Tuple<TKey, EncString>> BuildProtectedSymmetricKeyAsync<TKey>(SymmetricCryptoKey key,
byte[] encKey, Func<byte[], TKey> keyCreator) where TKey : SymmetricCryptoKey
{
EncString encKeyEnc = null;
if (key.Key.Length == 32)
{
var newKey = await StretchKeyAsync(key);
var newKey = await StretchKeyAsync(key, keyCreator);
encKeyEnc = await EncryptAsync(encKey, newKey);
}
else if (key.Key.Length == 64)
@@ -872,14 +923,14 @@ namespace Bit.Core.Services
{
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)
where TKey : SymmetricCryptoKey
{
byte[] key = null;
byte[] key;
if (kdfConfig.Type == null || kdfConfig.Type == KdfType.PBKDF2_SHA256)
{
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.
// 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(
bool masterPasswordOnRestart,
string pin,
@@ -964,25 +1046,28 @@ namespace Bit.Core.Services
if (masterPasswordOnRestart)
{
await _stateService.SetPinProtectedKeyAsync(null);
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey);
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(pinProtectedKey);
}
else
{
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
// now we set it regardless
var encPin = await EncryptAsync(pin, userKey);
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
}
// Clear old key
await _stateService.SetEncKeyEncryptedAsync(null);
return userKey;
}
public async Task clearDeprecatedPinKeysAsync(string userId = null)
public Task ClearDeprecatedPinKeysAsync(string userId = null)
{
await _stateService.SetPinProtectedAsync(null);
await _stateService.SetPinProtectedKeyAsync(null);
return Task.WhenAll(
_stateService.SetPinProtectedAsync(null, userId),
_stateService.SetPinProtectedKeyAsync(null, userId));
}
}
}

View File

@@ -24,11 +24,11 @@ namespace Bit.Core.Services
_organizationService = organizationService;
}
public async Task GetAndSetKey(string url)
public async Task GetAndSetMasterKeyAsync(string url)
{
try
{
var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnector(url);
var masterKeyResponse = await _apiService.GetMasterKeyFromKeyConnectorAsync(url);
var masterKeyArr = Convert.FromBase64String(masterKeyResponse.Key);
var masterKey = new MasterKey(masterKeyArr);
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);
}
public async Task<bool> GetUsesKeyConnector()
public async Task<bool> GetUsesKeyConnectorAsync()
{
return await _stateService.GetUsesKeyConnectorAsync();
}
public async Task<Organization> GetManagingOrganization()
public async Task<Organization> GetManagingOrganizationAsync()
{
var orgs = await _organizationService.GetAllAsync();
return orgs.Find(o =>
@@ -57,9 +57,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
@@ -75,11 +75,11 @@ namespace Bit.Core.Services
await _apiService.PostConvertToKeyConnector();
}
public async Task<bool> UserNeedsMigration()
public async Task<bool> UserNeedsMigrationAsync()
{
var loggedInUsingSso = await _tokenService.GetIsExternal();
var requiredByOrganization = await GetManagingOrganization() != null;
var userIsNotUsingKeyConnector = !await GetUsesKeyConnector();
var requiredByOrganization = await GetManagingOrganizationAsync() != null;
var userIsNotUsingKeyConnector = !await GetUsesKeyConnectorAsync();
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
}

View File

@@ -241,6 +241,19 @@ namespace Bit.Core.Services
))?.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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -334,14 +347,29 @@ namespace Bit.Core.Services
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)
@@ -350,6 +378,7 @@ namespace Bit.Core.Services
{
userId = await GetActiveUserIdAsync();
}
if (!await IsAuthenticatedAsync(userId))
{
return false;
@@ -395,66 +424,35 @@ namespace Bit.Core.Services
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;
}
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(
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 },
await GetDefaultInMemoryOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.VolatileData.UserKeyPinEphemeral = value;
account.VolatileData.PinKeyEncryptedUserKeyEphemeral = value;
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)
{
@@ -1478,28 +1476,31 @@ namespace Bit.Core.Services
}
// Non-state storage
await SetProtectedPinAsync(null, userId);
await SetPinProtectedAsync(null, userId);
await SetKeyEncryptedAsync(null, userId);
await SetKeyHashAsync(null, userId);
await SetEncKeyEncryptedAsync(null, userId);
await SetOrgKeysEncryptedAsync(null, userId);
await SetPrivateKeyEncryptedAsync(null, userId);
await SetLastActiveTimeAsync(null, userId);
await SetPreviousPageInfoAsync(null, userId);
await SetInvalidUnlockAttemptsAsync(null, userId);
await SetLocalDataAsync(null, userId);
await SetEncryptedCiphersAsync(null, userId);
await SetEncryptedCollectionsAsync(null, userId);
await SetLastSyncAsync(null, userId);
await SetEncryptedFoldersAsync(null, userId);
await SetEncryptedPoliciesAsync(null, userId);
await SetUsesKeyConnectorAsync(null, userId);
await SetOrganizationsAsync(null, userId);
await SetEncryptedPasswordGenerationHistoryAsync(null, userId);
await SetEncryptedSendsAsync(null, userId);
await SetSettingsAsync(null, userId);
await SetApprovePasswordlessLoginsAsync(null, userId);
await Task.WhenAll(
SetUserKeyAutoUnlockAsync(null, userId),
SetUserKeyBiometricUnlockAsync(null, userId),
SetProtectedPinAsync(null, userId),
SetKeyHashAsync(null, userId),
SetOrgKeysEncryptedAsync(null, userId),
SetPrivateKeyEncryptedAsync(null, userId),
SetLastActiveTimeAsync(null, userId),
SetPreviousPageInfoAsync(null, userId),
SetInvalidUnlockAttemptsAsync(null, userId),
SetLocalDataAsync(null, userId),
SetEncryptedCiphersAsync(null, userId),
SetEncryptedCollectionsAsync(null, userId),
SetLastSyncAsync(null, userId),
SetEncryptedFoldersAsync(null, userId),
SetEncryptedPoliciesAsync(null, userId),
SetUsesKeyConnectorAsync(null, userId),
SetOrganizationsAsync(null, userId),
SetEncryptedPasswordGenerationHistoryAsync(null, userId),
SetEncryptedSendsAsync(null, userId),
SetSettingsAsync(null, userId),
SetApprovePasswordlessLoginsAsync(null, userId),
SetEncKeyEncryptedAsync(null, userId),
SetKeyEncryptedAsync(null, userId),
SetPinProtectedAsync(null, userId));
}
private async Task ScaffoldNewAccountAsync(Account account)
@@ -1688,7 +1689,41 @@ namespace Bit.Core.Services
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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1696,7 +1731,7 @@ namespace Bit.Core.Services
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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1704,15 +1739,7 @@ namespace Bit.Core.Services
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
}
[Obsolete]
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]
[Obsolete("Left for migration purposes")]
public async Task SetKeyEncryptedAsync(string value, string userId)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
@@ -1720,22 +1747,20 @@ namespace Bit.Core.Services
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)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.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;
}
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key);
await _cryptoService.SetPrivateKeyAsync(response.PrivateKey);
await _cryptoService.SetUserPrivateKeyAsync(response.PrivateKey);
await _cryptoService.SetOrgKeysAsync(response.Organizations);
await _stateService.SetSecurityStampAsync(response.SecurityStamp);
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.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<FolderResponse> response)

View File

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

View File

@@ -1,13 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
namespace Bit.Core.Services
{
public enum PinLockEnum
public enum PinLockType
{
Disabled,
Persistent,
@@ -60,16 +58,27 @@ namespace Bit.Core.Services
public long? DelayLockAndLogoutMs { get; set; }
public async Task<bool> IsLockedAsync(string userId = null)
{
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
if (hasKey)
{
var biometricSet = await IsBiometricLockSetAsync(userId);
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
{
return true;
}
if (!await _cryptoService.HasUserKeyAsync(userId))
{
if (await _cryptoService.HasAutoUnlockKeyAsync(userId))
{
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId));
}
else
{
return true;
}
}
// Check again to verify auto key was set
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
return !hasKey;
}
@@ -170,13 +179,13 @@ namespace Bit.Core.Services
userId = await _stateService.GetActiveUserIdAsync();
}
if (await _keyConnectorService.GetUsesKeyConnector())
if (await _keyConnectorService.GetUsesKeyConnectorAsync())
{
var pinStatus = await IsPinLockSetAsync(userId);
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
var pinStatus = await GetPinLockTypeAsync(userId);
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync();
var pinEnabled = (pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
pinStatus == PinLockEnum.Persistent;
var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
pinStatus == PinLockType.Persistent;
if (!pinEnabled && !await IsBiometricLockSetAsync())
{
@@ -198,6 +207,7 @@ namespace Bit.Core.Services
await Task.WhenAll(
_cryptoService.ClearUserKeyAsync(userId),
_cryptoService.ClearMasterKeyAsync(userId),
_stateService.SetUserKeyAutoUnlockAsync(null, userId),
_cryptoService.ClearOrgKeysAsync(true, userId),
_cryptoService.ClearKeyPairAsync(true, userId));
@@ -223,30 +233,27 @@ namespace Bit.Core.Services
{
await _stateService.SetVaultTimeoutAsync(timeout);
await _stateService.SetVaultTimeoutActionAsync(action);
await _cryptoService.ToggleKeysAsync();
await _cryptoService.RefreshKeysAsync();
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
// versions only used it for MP on Restart
var pinIsEnabled = await _stateService.GetProtectedPinAsync(userId);
var userKeyPin = await _stateService.GetUserKeyPinAsync(userId);
var oldUserKeyPin = await _stateService.GetPinProtectedAsync(userId);
var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null;
var hasUserKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null;
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;
}
else
{
return PinLockEnum.Disabled;
return PinLockType.Transient;
}
return PinLockType.Disabled;
}
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 totpService = new TotpService(cryptoFunctionService);
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
keyConnectorService, passwordGenerationService, policyService);
tokenService, appIdService, i18nService, platformUtilsService, messagingService,
passwordGenerationService, policyService);
var exportService = new ExportService(folderService, cipherService, cryptoService);
var auditService = new AuditService(cryptoFunctionService, apiService);
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);

View File

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

View File

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

View File

@@ -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<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
public override async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
{
if (bioIntegritySrcKey == null)
{
@@ -30,7 +29,7 @@ namespace Bit.iOS.Core.Services
return true;
}
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
public override async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
{
var state = GetState();
if (state == null)

View File

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