1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-15 15:53:44 +00:00

Merge branch 'auth/pm-2713/drop-master-key-dependency' into feature/pm-1029-tde-login

This commit is contained in:
Jacob Fink
2023-08-03 22:01:38 -04:00
21 changed files with 438 additions and 394 deletions

View File

@@ -39,7 +39,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;
@@ -171,12 +171,12 @@ namespace Bit.App.Pages
return;
}
_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();
@@ -278,15 +278,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();
}
@@ -294,7 +294,7 @@ namespace Bit.App.Pages
if (oldPinProtected != null)
{
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockEnum.Transient,
_pinStatus == PinLockType.Transient,
Pin,
_email,
kdfConfig,
@@ -340,20 +340,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;
@@ -362,8 +362,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)
{

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

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

@@ -150,8 +150,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();
@@ -335,6 +335,7 @@ namespace Bit.App.Pages
}
if (oldTimeout != newTimeout)
{
await _cryptoService.RefreshKeysAsync();
await Device.InvokeOnMainThreadAsync(BuildList);
}
}
@@ -454,18 +455,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
@@ -507,7 +508,7 @@ namespace Bit.App.Pages
await UpdateVaultTimeoutActionIfNeededAsync();
}
await _stateService.SetBiometricLockedAsync(false);
await _cryptoService.ToggleKeysAsync();
await _cryptoService.RefreshKeysAsync();
BuildList();
}

View File

@@ -38,7 +38,7 @@ namespace Bit.App.Services
return false;
};
return await _cryptoService.CompareAndUpdatePasswordHashAsync(password, null);
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
}
public async Task<bool> Enabled()

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

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

@@ -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,27 @@ 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 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

@@ -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(string value, string userId = null);
Task<string> GetActiveUserIdAsync();
Task<string> GetActiveUserEmailAsync();
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
@@ -44,19 +46,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);
@@ -191,17 +185,24 @@ namespace Bit.Core.Abstractions
void SetConfigs(ConfigResponse value);
Task<bool> GetShouldTrustDeviceAsync();
Task SetShouldTrustDeviceAsync(bool 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

@@ -85,7 +85,8 @@ 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 CiphersKey(string userId) => $"ciphers_{userId}";
public static string FoldersKey(string userId) => $"folders_{userId}";
public static string CollectionsKey(string userId) => $"collections_{userId}";
@@ -98,7 +99,7 @@ namespace Bit.Core
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
public static string DeviceKeyKey(string userId) => $"deviceKey_{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

@@ -151,8 +151,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))
@@ -265,8 +265,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);
}
@@ -502,7 +502,7 @@ namespace Bit.Core.Services
{
if (localHashedPassword != null)
{
await _cryptoService.SetPasswordHashAsync(localHashedPassword);
await _cryptoService.SetMasterKeyHashAsync(localHashedPassword);
await _cryptoService.SetMasterKeyAsync(masterKey);
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
await _cryptoService.SetUserKeyAsync(userKey);
@@ -558,7 +558,7 @@ namespace Bit.Core.Services
catch { }
}
await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey);
await _cryptoService.SetUserPrivateKeyAsync(tokenResponse.PrivateKey);
}
else if (tokenResponse.KeyConnectorUrl != null)
{

View File

@@ -250,8 +250,7 @@ namespace Bit.Core.Services
{
try
{
var hashKey = await _cryptoService.HasUserKeyAsync();
if (!hashKey)
if (!await _cryptoService.HasUserKeyAsync())
{
throw new Exception("No key.");
}
@@ -591,20 +590,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;
@@ -841,6 +829,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)
{
@@ -853,20 +849,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,30 @@ 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 MigrateAutoUnlockKeyIfNeededAsync(userId);
return await _stateService.GetUserKeyAutoUnlockAsync(userId);
}
public async Task<bool> HasAutoUnlockKeyAsync(string userId = null)
{
return await GetAutoUnlockKeyAsync(userId) != null;
}
public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null)
{
return _stateService.SetMasterKeyAsync(masterKey, userId);
}
public async Task<MasterKey> GetMasterKeyAsync(string userId = null)
@@ -125,7 +121,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 +130,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 BuildProtectedSymmetricKey(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
}
public async Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null)
@@ -160,7 +156,7 @@ namespace Bit.Core.Services
if (encUserKey == null)
{
var userKeyMasterKey = await _stateService.GetUserKeyMasterKeyAsync(userId);
var userKeyMasterKey = await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId);
if (userKeyMasterKey == null)
{
throw new Exception("No encrypted user key found");
@@ -175,12 +171,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 +186,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 BuildProtectedSymmetricKey(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 (key == null)
{
key = await GetMasterKeyAsync();
if (masterKey is null)
{
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 +269,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 +343,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 +358,7 @@ namespace Bit.Core.Services
return _publicKey;
}
public async Task SetPrivateKeyAsync(string encPrivateKey)
public async Task SetUserPrivateKeyAsync(string encPrivateKey)
{
if (encPrivateKey == null)
{
@@ -376,7 +368,7 @@ namespace Bit.Core.Services
_privateKey = null;
}
public async Task<byte[]> GetPrivateKeyAsync()
public async Task<byte[]> GetUserPrivateKeyAsync()
{
if (_privateKey != null)
{
@@ -395,7 +387,7 @@ namespace Bit.Core.Services
{
if (publicKey == null)
{
publicKey = await GetPublicKeyAsync();
publicKey = await GetUserPublicKeyAsync();
}
if (publicKey == null)
{
@@ -426,28 +418,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 +474,7 @@ namespace Bit.Core.Services
{
if (publicKey == null)
{
publicKey = await GetPublicKeyAsync();
publicKey = await GetUserPublicKeyAsync();
}
if (publicKey == null)
{
@@ -521,7 +514,7 @@ namespace Bit.Core.Services
if (privateKey is null)
{
privateKey = await GetPrivateKeyAsync();
privateKey = await GetUserPrivateKeyAsync();
}
if (privateKey == null)
@@ -618,17 +611,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 +668,31 @@ namespace Bit.Core.Services
// --HELPER METHODS--
private async Task StorePinKey(UserKey userKey, string userId = null)
private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null)
{
// Refresh, set, or clear the pin key
if (await _stateService.GetProtectedPinAsync(userId) != null)
{
await UpdateUserKeyPinAsync(userKey, userId);
}
else
{
await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId);
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId);
}
// Refresh, set, or clear the auto key
if (await _stateService.GetVaultTimeoutAsync(userId) == null)
{
await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId);
}
else
{
await _stateService.SetUserKeyAutoUnlockAsync(null, userId);
}
}
private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null)
{
var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId)));
var pinKey = await MakePinKeyAsync(
@@ -685,13 +702,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 +837,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 +873,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>> BuildProtectedSymmetricKey<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,10 +891,10 @@ 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
{
@@ -937,6 +956,27 @@ namespace Bit.Core.Services
// We previously used the master key for additional keys, but now we use the user key.
// These methods support migrating the old keys to the new ones.
private async Task MigrateAutoUnlockKeyIfNeededAsync(string userId = null)
{
var oldAutoKey = await _stateService.GetKeyEncryptedAsync(userId);
if (oldAutoKey == null)
{
return;
}
// Decrypt
var masterKey = new MasterKey(Convert.FromBase64String(oldAutoKey));
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
var userKey = await DecryptUserKeyWithMasterKeyAsync(
masterKey,
new EncString(encryptedUserKey),
userId);
// Migrate
await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, 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 +1004,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

@@ -334,18 +334,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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyKey(reconciledOptions.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)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
await _storageMediatorService.SaveAsync(Constants.UserKeyKey(reconciledOptions.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(string value, string userId = null)
{
await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value, true);
}
public async Task<bool> CanAccessPremiumAsync(string userId = null)
@@ -354,6 +365,7 @@ namespace Bit.Core.Services
{
userId = await GetActiveUserIdAsync();
}
if (!await IsAuthenticatedAsync(userId))
{
return false;
@@ -399,25 +411,27 @@ 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;
}
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());
@@ -426,39 +440,6 @@ namespace Bit.Core.Services
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)
{
@@ -1532,28 +1513,30 @@ 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),
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)
@@ -1742,7 +1725,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 },
@@ -1750,7 +1767,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 },
@@ -1758,15 +1775,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 },
@@ -1774,22 +1783,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));

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,
@@ -61,15 +59,26 @@ namespace Bit.Core.Services
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))
{
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;
}
@@ -172,11 +181,11 @@ namespace Bit.Core.Services
if (await _keyConnectorService.GetUsesKeyConnector())
{
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

@@ -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,18 +104,18 @@ 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();
@@ -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,18 +96,18 @@ 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();
@@ -129,7 +129,7 @@ namespace Bit.iOS.Core.Controllers
{
BaseNavItem.Title = AppResources.VerifyMasterPassword;
}
BaseCancelButton.Title = AppResources.Cancel;
if (_biometricUnlockOnly)
@@ -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,
@@ -284,19 +284,19 @@ namespace Bit.iOS.Core.Controllers
else
{
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();
@@ -395,7 +395,7 @@ namespace Bit.iOS.Core.Controllers
});
PresentViewController(alert, true, null);
}
private async Task LogOutAsync()
{
await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync());