mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 19:08:32 +03:00
[PS-2358] Add kdf configuration options (#2328)
* Implement kdf configuration * Remove unused import * Move kdf parameters to kdfConfiguration struct * Remove unused state migration service keys * Revert newline changes in PCLCryptoFunctionService * Update KdfConfiguration.cs * Add checks for argon2, clean statemigration service * Update constants * Clean up code * Further cleanup * Change KdfType to non-nullable in SetKeyConnectorKeyRequest --------- Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
parent
8b08f906bd
commit
c3ad5f0580
20 changed files with 135 additions and 101 deletions
|
@ -228,8 +228,7 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
|
||||||
|
|
||||||
if (PinLock)
|
if (PinLock)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +238,7 @@ namespace Bit.App.Pages
|
||||||
if (_isPinProtected)
|
if (_isPinProtected)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
await _stateService.GetPinProtectedKeyAsync());
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
@ -254,8 +253,7 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
|
||||||
failed = false;
|
failed = false;
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
@ -280,7 +278,7 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
var passwordValid = false;
|
var passwordValid = false;
|
||||||
|
|
||||||
|
@ -314,8 +312,7 @@ namespace Bit.App.Pages
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||||
}
|
}
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
|
|
|
@ -175,8 +175,8 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||||
Email = Email.Trim().ToLower();
|
Email = Email.Trim().ToLower();
|
||||||
var kdf = KdfType.PBKDF2_SHA256;
|
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdf, Constants.KdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig);
|
||||||
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||||
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
||||||
|
@ -187,8 +187,10 @@ namespace Bit.App.Pages
|
||||||
MasterPasswordHash = hashedPassword,
|
MasterPasswordHash = hashedPassword,
|
||||||
MasterPasswordHint = Hint,
|
MasterPasswordHint = Hint,
|
||||||
Key = encKey.Item2.EncryptedString,
|
Key = encKey.Item2.EncryptedString,
|
||||||
Kdf = kdf,
|
Kdf = kdfConfig.Type,
|
||||||
KdfIterations = Constants.KdfIterations,
|
KdfIterations = kdfConfig.Iterations,
|
||||||
|
KdfMemory = kdfConfig.Memory,
|
||||||
|
KdfParallelism = kdfConfig.Parallelism,
|
||||||
Keys = new KeysRequest
|
Keys = new KeysRequest
|
||||||
{
|
{
|
||||||
PublicKey = keys.Item1,
|
PublicKey = keys.Item1,
|
||||||
|
|
|
@ -163,9 +163,9 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var kdf = KdfType.PBKDF2_SHA256;
|
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, Constants.KdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
|
|
||||||
|
@ -186,8 +186,10 @@ namespace Bit.App.Pages
|
||||||
MasterPasswordHash = masterPasswordHash,
|
MasterPasswordHash = masterPasswordHash,
|
||||||
Key = encKey.Item2.EncryptedString,
|
Key = encKey.Item2.EncryptedString,
|
||||||
MasterPasswordHint = Hint,
|
MasterPasswordHint = Hint,
|
||||||
Kdf = kdf,
|
Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256),
|
||||||
KdfIterations = Constants.KdfIterations,
|
KdfIterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Pbkdf2Iterations),
|
||||||
|
KdfMemory = kdfConfig.Memory,
|
||||||
|
KdfParallelism = kdfConfig.Parallelism,
|
||||||
OrgIdentifier = OrgIdentifier,
|
OrgIdentifier = OrgIdentifier,
|
||||||
Keys = new KeysRequest
|
Keys = new KeysRequest
|
||||||
{
|
{
|
||||||
|
@ -201,8 +203,7 @@ namespace Bit.App.Pages
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
// Set Password and relevant information
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _stateService.SetKdfTypeAsync(kdf);
|
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||||
await _stateService.SetKdfIterationsAsync(Constants.KdfIterations);
|
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
|
|
|
@ -43,12 +43,11 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve details for key generation
|
// Retrieve details for key generation
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new key and hash new password
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdfConfig);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
|
|
||||||
// Create new encKey for the User
|
// Create new encKey for the User
|
||||||
|
|
|
@ -422,12 +422,9 @@ namespace Bit.App.Pages
|
||||||
AppResources.Yes, AppResources.No);
|
AppResources.Yes, AppResources.No);
|
||||||
}
|
}
|
||||||
|
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
|
||||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
|
||||||
kdfIterations.GetValueOrDefault(5000));
|
|
||||||
var key = await _cryptoService.GetKeyAsync();
|
var key = await _cryptoService.GetKeyAsync();
|
||||||
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,10 @@ namespace Bit.Core.Abstractions
|
||||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||||
Task<bool> HasKeyAsync(string userId = null);
|
Task<bool> HasKeyAsync(string userId = null);
|
||||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
||||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
|
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config);
|
||||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,
|
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null);
|
||||||
EncString protectedKeyEs = null);
|
|
||||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||||
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfType kdf, int kdfIterations);
|
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config);
|
||||||
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
||||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||||
Task<int> RandomNumberAsync(int min, int max);
|
Task<int> RandomNumberAsync(int min, int max);
|
||||||
|
|
|
@ -37,10 +37,7 @@ namespace Bit.Core.Abstractions
|
||||||
Task SetPinProtectedAsync(string value, string userId = null);
|
Task SetPinProtectedAsync(string value, string userId = null);
|
||||||
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
|
Task<EncString> GetPinProtectedKeyAsync(string userId = null);
|
||||||
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
|
Task SetPinProtectedKeyAsync(EncString value, string userId = null);
|
||||||
Task<KdfType?> GetKdfTypeAsync(string userId = null);
|
Task SetKdfConfigurationAsync(KdfConfig config, string userId = null);
|
||||||
Task SetKdfTypeAsync(KdfType? value, string userId = null);
|
|
||||||
Task<int?> GetKdfIterationsAsync(string userId = null);
|
|
||||||
Task SetKdfIterationsAsync(int? value, string userId = null);
|
|
||||||
Task<string> GetKeyEncryptedAsync(string userId = null);
|
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||||
|
|
|
@ -46,7 +46,10 @@
|
||||||
public const int SaveFileRequestCode = 44;
|
public const int SaveFileRequestCode = 44;
|
||||||
public const int TotpDefaultTimer = 30;
|
public const int TotpDefaultTimer = 30;
|
||||||
public const int PasswordlessNotificationTimeoutInMinutes = 15;
|
public const int PasswordlessNotificationTimeoutInMinutes = 15;
|
||||||
public const int KdfIterations = 600000;
|
public const int Pbkdf2Iterations = 600000;
|
||||||
|
public const int Argon2Iterations = 3;
|
||||||
|
public const int Argon2MemoryInMB = 64;
|
||||||
|
public const int Argon2Parallelism = 4;
|
||||||
public const int MasterPasswordMinimumChars = 8;
|
public const int MasterPasswordMinimumChars = 8;
|
||||||
|
|
||||||
public static readonly string[] AndroidAllClearCipherCacheKeys =
|
public static readonly string[] AndroidAllClearCipherCacheKeys =
|
||||||
|
|
|
@ -46,6 +46,8 @@ namespace Bit.Core.Models.Domain
|
||||||
OrgIdentifier = copy.OrgIdentifier;
|
OrgIdentifier = copy.OrgIdentifier;
|
||||||
KdfType = copy.KdfType;
|
KdfType = copy.KdfType;
|
||||||
KdfIterations = copy.KdfIterations;
|
KdfIterations = copy.KdfIterations;
|
||||||
|
KdfMemory = copy.KdfMemory;
|
||||||
|
KdfParallelism = copy.KdfParallelism;
|
||||||
EmailVerified = copy.EmailVerified;
|
EmailVerified = copy.EmailVerified;
|
||||||
HasPremiumPersonally = copy.HasPremiumPersonally;
|
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||||
AvatarColor = copy.AvatarColor;
|
AvatarColor = copy.AvatarColor;
|
||||||
|
@ -59,6 +61,8 @@ namespace Bit.Core.Models.Domain
|
||||||
public string AvatarColor;
|
public string AvatarColor;
|
||||||
public KdfType? KdfType;
|
public KdfType? KdfType;
|
||||||
public int? KdfIterations;
|
public int? KdfIterations;
|
||||||
|
public int? KdfMemory;
|
||||||
|
public int? KdfParallelism;
|
||||||
public bool? EmailVerified;
|
public bool? EmailVerified;
|
||||||
public bool? HasPremiumPersonally;
|
public bool? HasPremiumPersonally;
|
||||||
}
|
}
|
||||||
|
|
27
src/Core/Models/Domain/KdfConfiguration.cs
Executable file
27
src/Core/Models/Domain/KdfConfiguration.cs
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
public struct KdfConfig
|
||||||
|
{
|
||||||
|
public static KdfConfig Default = new KdfConfig(KdfType.PBKDF2_SHA256, 5000, null, null);
|
||||||
|
public KdfConfig(KdfType? type, int? iterations, int? memory, int? parallelism)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Iterations = iterations;
|
||||||
|
Memory = memory;
|
||||||
|
Parallelism = parallelism;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KdfConfig(Account.AccountProfile profile)
|
||||||
|
{
|
||||||
|
Type = profile.KdfType;
|
||||||
|
Iterations = profile.KdfIterations;
|
||||||
|
Memory = profile.KdfMemory;
|
||||||
|
Parallelism = profile.KdfParallelism;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KdfType? Type { get; set; }
|
||||||
|
public int? Iterations { get; set; }
|
||||||
|
public int? Memory { get; set; }
|
||||||
|
public int? Parallelism { get; set; }
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ namespace Bit.Core.Models.Request
|
||||||
public Guid? OrganizationUserId { get; set; }
|
public Guid? OrganizationUserId { get; set; }
|
||||||
public KdfType? Kdf { get; set; }
|
public KdfType? Kdf { get; set; }
|
||||||
public int? KdfIterations { get; set; }
|
public int? KdfIterations { get; set; }
|
||||||
|
public int? KdfMemory { get; set; }
|
||||||
|
public int? KdfParallelism { get; set; }
|
||||||
public string CaptchaResponse { get; set; }
|
public string CaptchaResponse { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,18 @@ namespace Bit.Core.Models.Request
|
||||||
public KeysRequest Keys { get; set; }
|
public KeysRequest Keys { get; set; }
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int? KdfIterations { get; set; }
|
public int? KdfIterations { get; set; }
|
||||||
|
public int? KdfMemory { get; set; }
|
||||||
|
public int? KdfParallelism { get; set; }
|
||||||
public string OrgIdentifier { get; set; }
|
public string OrgIdentifier { get; set; }
|
||||||
|
|
||||||
public SetKeyConnectorKeyRequest(string key, KeysRequest keys,
|
public SetKeyConnectorKeyRequest(string key, KeysRequest keys, KdfConfig kdfConfig, string orgIdentifier)
|
||||||
KdfType kdf, int? kdfIterations, string orgIdentifier)
|
|
||||||
{
|
{
|
||||||
this.Key = key;
|
this.Key = key;
|
||||||
this.Keys = keys;
|
this.Keys = keys;
|
||||||
this.Kdf = kdf;
|
this.Kdf = kdfConfig.Type.GetValueOrDefault(KdfType.PBKDF2_SHA256);
|
||||||
this.KdfIterations = kdfIterations;
|
this.KdfIterations = kdfConfig.Iterations;
|
||||||
|
this.KdfMemory = kdfConfig.Memory;
|
||||||
|
this.KdfParallelism = kdfConfig.Parallelism;
|
||||||
this.OrgIdentifier = orgIdentifier;
|
this.OrgIdentifier = orgIdentifier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace Bit.Core.Models.Request
|
||||||
public KeysRequest Keys { get; set; }
|
public KeysRequest Keys { get; set; }
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int KdfIterations { get; set; }
|
public int KdfIterations { get; set; }
|
||||||
|
public int? KdfMemory { get; set; }
|
||||||
|
public int? KdfParallelism { get; set; }
|
||||||
public string OrgIdentifier { get; set; }
|
public string OrgIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,11 @@ namespace Bit.Core.Models.Response
|
||||||
public string TwoFactorToken { get; set; }
|
public string TwoFactorToken { get; set; }
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int? KdfIterations { get; set; }
|
public int? KdfIterations { get; set; }
|
||||||
|
public int? KdfMemory { get; set; }
|
||||||
|
public int? KdfParallelism { get; set; }
|
||||||
public bool ForcePasswordReset { get; set; }
|
public bool ForcePasswordReset { get; set; }
|
||||||
public string KeyConnectorUrl { get; set; }
|
public string KeyConnectorUrl { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Response
|
namespace Bit.Core.Models.Response
|
||||||
{
|
{
|
||||||
|
@ -6,5 +7,9 @@ namespace Bit.Core.Models.Response
|
||||||
{
|
{
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int KdfIterations { get; set; }
|
public int KdfIterations { get; set; }
|
||||||
|
public int? KdfMemory { get; set; }
|
||||||
|
public int? KdfParallelism { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,15 +276,13 @@ namespace Bit.Core.Services
|
||||||
private async Task<SymmetricCryptoKey> MakePreloginKeyAsync(string masterPassword, string email)
|
private async Task<SymmetricCryptoKey> MakePreloginKeyAsync(string masterPassword, string email)
|
||||||
{
|
{
|
||||||
email = email.Trim().ToLower();
|
email = email.Trim().ToLower();
|
||||||
KdfType? kdf = null;
|
KdfConfig kdfConfig = KdfConfig.Default;
|
||||||
int? kdfIterations = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var preloginResponse = await _apiService.PostPreloginAsync(new PreloginRequest { Email = email });
|
var preloginResponse = await _apiService.PostPreloginAsync(new PreloginRequest { Email = email });
|
||||||
if (preloginResponse != null)
|
if (preloginResponse != null)
|
||||||
{
|
{
|
||||||
kdf = preloginResponse.Kdf;
|
kdfConfig = preloginResponse.KdfConfig;
|
||||||
kdfIterations = preloginResponse.KdfIterations;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
|
@ -294,7 +292,7 @@ namespace Bit.Core.Services
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await _cryptoService.MakeKeyAsync(masterPassword, email, kdf, kdfIterations);
|
return await _cryptoService.MakeKeyAsync(masterPassword, email, kdfConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||||
|
@ -442,7 +440,7 @@ namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
// SSO Key Connector Onboarding
|
// SSO Key Connector Onboarding
|
||||||
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||||
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.Kdf, tokenResponse.KdfIterations);
|
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
|
||||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
|
||||||
await _cryptoService.SetKeyAsync(k);
|
await _cryptoService.SetKeyAsync(k);
|
||||||
|
|
||||||
|
@ -465,7 +463,7 @@ namespace Bit.Core.Services
|
||||||
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
||||||
};
|
};
|
||||||
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
encKey.Item2.EncryptedString, keys, tokenResponse.Kdf, tokenResponse.KdfIterations, orgId
|
encKey.Item2.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
||||||
);
|
);
|
||||||
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,30 +389,46 @@ namespace Bit.Core.Services
|
||||||
await SetKeyAsync(key);
|
await SetKeyAsync(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt,
|
public async Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig kdfConfig)
|
||||||
KdfType? kdf, int? kdfIterations)
|
|
||||||
{
|
{
|
||||||
byte[] key = null;
|
byte[] key = null;
|
||||||
if (kdf == null || kdf == KdfType.PBKDF2_SHA256)
|
if (kdfConfig.Type == null || kdfConfig.Type == KdfType.PBKDF2_SHA256)
|
||||||
{
|
{
|
||||||
if (kdfIterations == null)
|
var iterations = kdfConfig.Iterations.GetValueOrDefault(5000);
|
||||||
{
|
if (iterations < 5000)
|
||||||
kdfIterations = 5000;
|
|
||||||
}
|
|
||||||
if (kdfIterations < 5000)
|
|
||||||
{
|
{
|
||||||
throw new Exception("PBKDF2 iteration minimum is 5000.");
|
throw new Exception("PBKDF2 iteration minimum is 5000.");
|
||||||
}
|
}
|
||||||
key = await _cryptoFunctionService.Pbkdf2Async(password, salt,
|
key = await _cryptoFunctionService.Pbkdf2Async(password, salt,
|
||||||
CryptoHashAlgorithm.Sha256, kdfIterations.Value);
|
CryptoHashAlgorithm.Sha256, iterations);
|
||||||
}
|
}
|
||||||
else if (kdf == KdfType.Argon2id)
|
else if (kdfConfig.Type == KdfType.Argon2id)
|
||||||
{
|
{
|
||||||
var iterations = kdfIterations.Value;
|
var iterations = kdfConfig.Iterations.GetValueOrDefault(Constants.Argon2Iterations);
|
||||||
const int parallelism = 1;
|
var memory = kdfConfig.Memory.GetValueOrDefault(Constants.Argon2MemoryInMB) * 1024;
|
||||||
const int memory = 1024 * 16; // 16 MiB
|
var parallelism = kdfConfig.Parallelism.GetValueOrDefault(Constants.Argon2Parallelism);
|
||||||
|
|
||||||
key = await _cryptoFunctionService.Argon2Async(password, salt, iterations, memory, parallelism);
|
if (kdfConfig.Iterations < 2)
|
||||||
|
{
|
||||||
|
throw new Exception("Argon2 iterations minimum is 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kdfConfig.Memory < 16)
|
||||||
|
{
|
||||||
|
throw new Exception("Argon2 memory minimum is 16 MB");
|
||||||
|
}
|
||||||
|
else if (kdfConfig.Memory > 1024)
|
||||||
|
{
|
||||||
|
throw new Exception("Argon2 memory maximum is 1024 MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kdfConfig.Parallelism < 1)
|
||||||
|
{
|
||||||
|
throw new Exception("Argon2 parallelism minimum is 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var saltHash = await _cryptoFunctionService.HashAsync(salt, CryptoHashAlgorithm.Sha256);
|
||||||
|
key = await _cryptoFunctionService.Argon2Async(password, saltHash, iterations, memory, parallelism);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -422,7 +438,7 @@ namespace Bit.Core.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt,
|
public async Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt,
|
||||||
KdfType kdf, int kdfIterations, EncString protectedKeyCs = null)
|
KdfConfig config, EncString protectedKeyCs = null)
|
||||||
{
|
{
|
||||||
if (protectedKeyCs == null)
|
if (protectedKeyCs == null)
|
||||||
{
|
{
|
||||||
|
@ -433,7 +449,7 @@ namespace Bit.Core.Services
|
||||||
}
|
}
|
||||||
protectedKeyCs = new EncString(pinProtectedKey);
|
protectedKeyCs = new EncString(pinProtectedKey);
|
||||||
}
|
}
|
||||||
var pinKey = await MakePinKeyAysnc(pin, salt, kdf, kdfIterations);
|
var pinKey = await MakePinKeyAysnc(pin, salt, config);
|
||||||
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
|
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
|
||||||
return new SymmetricCryptoKey(decKey);
|
return new SymmetricCryptoKey(decKey);
|
||||||
}
|
}
|
||||||
|
@ -454,9 +470,9 @@ namespace Bit.Core.Services
|
||||||
return new Tuple<string, EncString>(publicB64, privateEnc);
|
return new Tuple<string, EncString>(publicB64, privateEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfType kdf, int kdfIterations)
|
public async Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config)
|
||||||
{
|
{
|
||||||
var pinKey = await MakeKeyAsync(pin, salt, kdf, kdfIterations);
|
var pinKey = await MakeKeyAsync(pin, salt, config);
|
||||||
return await StretchKeyAsync(pinKey);
|
return await StretchKeyAsync(pinKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -339,35 +339,15 @@ namespace Bit.Core.Services
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<KdfType?> GetKdfTypeAsync(string userId = null)
|
public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null)
|
||||||
{
|
|
||||||
return (await GetAccountAsync(
|
|
||||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
|
|
||||||
))?.Profile?.KdfType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetKdfTypeAsync(KdfType? value, string userId = null)
|
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
await GetDefaultStorageOptionsAsync());
|
await GetDefaultStorageOptionsAsync());
|
||||||
var account = await GetAccountAsync(reconciledOptions);
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
account.Profile.KdfType = value;
|
account.Profile.KdfType = config.Type;
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
account.Profile.KdfIterations = config.Iterations;
|
||||||
}
|
account.Profile.KdfMemory = config.Memory;
|
||||||
|
account.Profile.KdfParallelism = config.Parallelism;
|
||||||
public async Task<int?> GetKdfIterationsAsync(string userId = null)
|
|
||||||
{
|
|
||||||
return (await GetAccountAsync(
|
|
||||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
|
|
||||||
))?.Profile?.KdfIterations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetKdfIterationsAsync(int? value, string userId = null)
|
|
||||||
{
|
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
|
||||||
await GetDefaultStorageOptionsAsync());
|
|
||||||
var account = await GetAccountAsync(reconciledOptions);
|
|
||||||
account.Profile.KdfIterations = value;
|
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,8 +221,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
|
||||||
var inputtedValue = MasterPasswordCell.TextField.Text;
|
var inputtedValue = MasterPasswordCell.TextField.Text;
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinLock)
|
||||||
|
@ -233,7 +232,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
if (_isPinProtected)
|
if (_isPinProtected)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
await _stateService.GetPinProtectedKeyAsync());
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
@ -248,7 +247,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdfConfig);
|
||||||
failed = false;
|
failed = false;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(key2);
|
||||||
|
@ -265,7 +264,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdf, kdfIterations);
|
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedKeyHash == null)
|
||||||
|
@ -287,7 +286,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdfConfig);
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
||||||
}
|
}
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
|
@ -210,8 +210,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
|
||||||
var inputtedValue = MasterPasswordCell.TextField.Text;
|
var inputtedValue = MasterPasswordCell.TextField.Text;
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinLock)
|
||||||
|
@ -222,7 +221,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
if (_isPinProtected)
|
if (_isPinProtected)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
await _stateService.GetPinProtectedKeyAsync());
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
@ -237,7 +236,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdfConfig);
|
||||||
failed = false;
|
failed = false;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(key2);
|
||||||
|
@ -260,7 +259,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdf, kdfIterations);
|
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedKeyHash == null)
|
||||||
|
@ -282,7 +281,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdfConfig);
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
||||||
}
|
}
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
Loading…
Reference in a new issue