mirror of
https://github.com/bitwarden/android.git
synced 2024-11-01 07:35:52 +03:00
support for prelogin kdf params
This commit is contained in:
parent
e70dbf8d8d
commit
7862005055
12 changed files with 140 additions and 17 deletions
|
@ -6,6 +6,7 @@ namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface IAccountsApiRepository
|
public interface IAccountsApiRepository
|
||||||
{
|
{
|
||||||
|
Task<ApiResult<PreloginResponse>> PostPreloginAsync(PreloginRequest requestObj);
|
||||||
Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj);
|
Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj);
|
||||||
Task<ApiResult> PostPasswordHintAsync(PasswordHintRequest requestObj);
|
Task<ApiResult> PostPasswordHintAsync(PasswordHintRequest requestObj);
|
||||||
Task<ApiResult<DateTime?>> GetAccountRevisionDateAsync();
|
Task<ApiResult<DateTime?>> GetAccountRevisionDateAsync();
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace Bit.App.Abstractions
|
||||||
string PreviousUserId { get; }
|
string PreviousUserId { get; }
|
||||||
bool UserIdChanged { get; }
|
bool UserIdChanged { get; }
|
||||||
string Email { get; set; }
|
string Email { get; set; }
|
||||||
|
KdfType Kdf { get; set; }
|
||||||
|
int KdfIterations { get; set; }
|
||||||
string PIN { get; set; }
|
string PIN { get; set; }
|
||||||
bool BelongsToOrganization(string orgId);
|
bool BelongsToOrganization(string orgId);
|
||||||
void LogOut(string logoutMessage = null);
|
void LogOut(string logoutMessage = null);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Bit.App.Models;
|
using Bit.App.Enums;
|
||||||
|
using Bit.App.Models;
|
||||||
using Bit.App.Models.Api;
|
using Bit.App.Models.Api;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
|
@ -23,8 +23,7 @@ namespace Bit.App.Abstractions
|
||||||
byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
|
byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey);
|
||||||
CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null);
|
CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null);
|
||||||
byte[] EncryptToBytes(byte[] plainBytes, SymmetricCryptoKey key = null);
|
byte[] EncryptToBytes(byte[] plainBytes, SymmetricCryptoKey key = null);
|
||||||
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt);
|
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt, KdfType kdf, int kdfIterations);
|
||||||
string MakeKeyFromPasswordBase64(string password, string salt);
|
|
||||||
byte[] HashPassword(SymmetricCryptoKey key, string password);
|
byte[] HashPassword(SymmetricCryptoKey key, string password);
|
||||||
string HashPasswordBase64(SymmetricCryptoKey key, string password);
|
string HashPasswordBase64(SymmetricCryptoKey key, string password);
|
||||||
CipherString MakeEncKey(SymmetricCryptoKey key);
|
CipherString MakeEncKey(SymmetricCryptoKey key);
|
||||||
|
|
7
src/App/Enums/KdfType.cs
Normal file
7
src/App/Enums/KdfType.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Bit.App.Enums
|
||||||
|
{
|
||||||
|
public enum KdfType : short
|
||||||
|
{
|
||||||
|
PBKDF2 = 0
|
||||||
|
}
|
||||||
|
}
|
7
src/App/Models/Api/Request/PreloginRequest.cs
Normal file
7
src/App/Models/Api/Request/PreloginRequest.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class PreloginRequest
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Bit.App.Models.Api
|
using Bit.App.Enums;
|
||||||
|
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
{
|
{
|
||||||
public class RegisterRequest
|
public class RegisterRequest
|
||||||
{
|
{
|
||||||
|
@ -7,5 +9,7 @@
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
public string MasterPasswordHint { get; set; }
|
public string MasterPasswordHint { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
public KdfType Kdf { get; set; }
|
||||||
|
public int KdfIterations { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
src/App/Models/Api/Response/PreloginResponse.cs
Normal file
10
src/App/Models/Api/Response/PreloginResponse.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class PreloginResponse
|
||||||
|
{
|
||||||
|
public KdfType Kdf { get; set; }
|
||||||
|
public int KdfIterations { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -151,7 +151,8 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, _authService.Email);
|
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, _authService.Email,
|
||||||
|
_authService.Kdf, _authService.KdfIterations);
|
||||||
if(key.Key.SequenceEqual(_cryptoService.Key.Key))
|
if(key.Key.SequenceEqual(_cryptoService.Key.Key))
|
||||||
{
|
{
|
||||||
_appSettingsService.Locked = false;
|
_appSettingsService.Locked = false;
|
||||||
|
|
|
@ -192,8 +192,10 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var kdf = Enums.KdfType.PBKDF2;
|
||||||
|
var kdfIterations = 5000;
|
||||||
var normalizedEmail = EmailCell.Entry.Text.ToLower();
|
var normalizedEmail = EmailCell.Entry.Text.ToLower();
|
||||||
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail);
|
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail, kdf, kdfIterations);
|
||||||
var encKey = _cryptoService.MakeEncKey(key);
|
var encKey = _cryptoService.MakeEncKey(key);
|
||||||
var request = new RegisterRequest
|
var request = new RegisterRequest
|
||||||
{
|
{
|
||||||
|
@ -201,7 +203,9 @@ namespace Bit.App.Pages
|
||||||
MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text),
|
MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text),
|
||||||
MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text)
|
MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text)
|
||||||
? PasswordHintCell.Entry.Text : null,
|
? PasswordHintCell.Entry.Text : null,
|
||||||
Key = encKey.EncryptedString
|
Key = encKey.EncryptedString,
|
||||||
|
Kdf = kdf,
|
||||||
|
KdfIterations = kdfIterations
|
||||||
};
|
};
|
||||||
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
|
|
|
@ -20,6 +20,40 @@ namespace Bit.App.Repositories
|
||||||
|
|
||||||
protected override string ApiRoute => "/accounts";
|
protected override string ApiRoute => "/accounts";
|
||||||
|
|
||||||
|
public virtual async Task<ApiResult<PreloginResponse>> PostPreloginAsync(PreloginRequest requestObj)
|
||||||
|
{
|
||||||
|
if(!Connectivity.IsConnected)
|
||||||
|
{
|
||||||
|
return HandledNotConnected<PreloginResponse>();
|
||||||
|
}
|
||||||
|
|
||||||
|
using(var client = HttpService.ApiClient)
|
||||||
|
{
|
||||||
|
var requestMessage = new TokenHttpRequestMessage(requestObj)
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri(string.Concat(client.BaseAddress, ApiRoute, "/prelogin")),
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await HandleErrorAsync<PreloginResponse>(response).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
var responseObj = JsonConvert.DeserializeObject<PreloginResponse>(responseContent);
|
||||||
|
return ApiResult<PreloginResponse>.Success(responseObj, response.StatusCode);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return HandledWebException<PreloginResponse>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj)
|
public virtual async Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj)
|
||||||
{
|
{
|
||||||
if(!Connectivity.IsConnected)
|
if(!Connectivity.IsConnected)
|
||||||
|
|
|
@ -17,6 +17,8 @@ namespace Bit.App.Services
|
||||||
public class AuthService : IAuthService
|
public class AuthService : IAuthService
|
||||||
{
|
{
|
||||||
private const string EmailKey = "email";
|
private const string EmailKey = "email";
|
||||||
|
private const string KdfKey = "kdf";
|
||||||
|
private const string KdfIterationsKey = "kdfIterations";
|
||||||
private const string UserIdKey = "userId";
|
private const string UserIdKey = "userId";
|
||||||
private const string PreviousUserIdKey = "previousUserId";
|
private const string PreviousUserIdKey = "previousUserId";
|
||||||
private const string PinKey = "pin";
|
private const string PinKey = "pin";
|
||||||
|
@ -34,6 +36,8 @@ namespace Bit.App.Services
|
||||||
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
private readonly IGoogleAnalyticsService _googleAnalyticsService;
|
||||||
|
|
||||||
private string _email;
|
private string _email;
|
||||||
|
private KdfType? _kdf;
|
||||||
|
private int? _kdfIterations;
|
||||||
private string _userId;
|
private string _userId;
|
||||||
private string _previousUserId;
|
private string _previousUserId;
|
||||||
private string _pin;
|
private string _pin;
|
||||||
|
@ -158,6 +162,40 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KdfType Kdf
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(!_kdf.HasValue)
|
||||||
|
{
|
||||||
|
_kdf = (KdfType)_settings.GetValueOrDefault(KdfKey, (short)KdfType.PBKDF2);
|
||||||
|
}
|
||||||
|
return _kdf.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(KdfKey, (short)value);
|
||||||
|
_kdf = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int KdfIterations
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(!_kdfIterations.HasValue)
|
||||||
|
{
|
||||||
|
_kdfIterations = _settings.GetValueOrDefault(KdfIterationsKey, 5000);
|
||||||
|
}
|
||||||
|
return _kdfIterations.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(KdfIterationsKey, value);
|
||||||
|
_kdfIterations = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsAuthenticated
|
public bool IsAuthenticated
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -231,10 +269,20 @@ namespace Bit.App.Services
|
||||||
|
|
||||||
public async Task<FullLoginResult> TokenPostAsync(string email, string masterPassword)
|
public async Task<FullLoginResult> TokenPostAsync(string email, string masterPassword)
|
||||||
{
|
{
|
||||||
|
Kdf = KdfType.PBKDF2;
|
||||||
|
KdfIterations = 5000;
|
||||||
|
var preloginResponse = await _accountsApiRepository.PostPreloginAsync(
|
||||||
|
new PreloginRequest { Email = email });
|
||||||
|
if(preloginResponse.Succeeded)
|
||||||
|
{
|
||||||
|
Kdf = preloginResponse.Result.Kdf;
|
||||||
|
KdfIterations = preloginResponse.Result.KdfIterations;
|
||||||
|
}
|
||||||
|
|
||||||
var result = new FullLoginResult();
|
var result = new FullLoginResult();
|
||||||
|
|
||||||
var normalizedEmail = email.Trim().ToLower();
|
var normalizedEmail = email.Trim().ToLower();
|
||||||
var key = _cryptoService.MakeKeyFromPassword(masterPassword, normalizedEmail);
|
var key = _cryptoService.MakeKeyFromPassword(masterPassword, normalizedEmail, Kdf, KdfIterations);
|
||||||
|
|
||||||
var request = new TokenRequest
|
var request = new TokenRequest
|
||||||
{
|
{
|
||||||
|
|
|
@ -429,7 +429,7 @@ namespace Bit.App.Services
|
||||||
return decryptedBytes;
|
return decryptedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SymmetricCryptoKey MakeKeyFromPassword(string password, string salt)
|
public SymmetricCryptoKey MakeKeyFromPassword(string password, string salt, KdfType kdf, int kdfIterations)
|
||||||
{
|
{
|
||||||
if(password == null)
|
if(password == null)
|
||||||
{
|
{
|
||||||
|
@ -444,16 +444,22 @@ namespace Bit.App.Services
|
||||||
var passwordBytes = Encoding.UTF8.GetBytes(NormalizePassword(password));
|
var passwordBytes = Encoding.UTF8.GetBytes(NormalizePassword(password));
|
||||||
var saltBytes = Encoding.UTF8.GetBytes(salt);
|
var saltBytes = Encoding.UTF8.GetBytes(salt);
|
||||||
|
|
||||||
var keyBytes = _keyDerivationService.DeriveKey(passwordBytes, saltBytes, 5000);
|
byte[] keyBytes = null;
|
||||||
|
if(kdf == KdfType.PBKDF2)
|
||||||
|
{
|
||||||
|
if(kdfIterations < 5000)
|
||||||
|
{
|
||||||
|
throw new Exception("PBKDF2 iteration minimum is 5000.");
|
||||||
|
}
|
||||||
|
keyBytes = _keyDerivationService.DeriveKey(passwordBytes, saltBytes, (uint)kdfIterations);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Unknown Kdf.");
|
||||||
|
}
|
||||||
return new SymmetricCryptoKey(keyBytes);
|
return new SymmetricCryptoKey(keyBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MakeKeyFromPasswordBase64(string password, string salt)
|
|
||||||
{
|
|
||||||
var key = MakeKeyFromPassword(password, salt);
|
|
||||||
return Convert.ToBase64String(key.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] HashPassword(SymmetricCryptoKey key, string password)
|
public byte[] HashPassword(SymmetricCryptoKey key, string password)
|
||||||
{
|
{
|
||||||
if(key == null)
|
if(key == null)
|
||||||
|
|
Loading…
Reference in a new issue