1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Merge branch 'feature/pm-1208-f3-options' into feature/pm-2293-admin-approval

# Conflicts:
#	src/App/Pages/Accounts/LoginApproveDeviceViewModel.cs
#	src/App/Resources/AppResources.resx
This commit is contained in:
André Bispo
2023-07-10 12:36:22 +01:00
11 changed files with 136 additions and 30 deletions

View File

@@ -11,6 +11,7 @@ using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
using Xamarin.Essentials;
namespace Bit.App.Pages
{

View File

@@ -93,9 +93,10 @@ namespace Bit.Core.Abstractions
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier);
Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest deviceRequest);
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes);
Task<DeviceResponse> PutUpdateTrustedDeviceKeys(UpdateTrustedDeviceKeysRequest request);
Task<ConfigResponse> GetConfigsAsync();
Task<string> GetFastmailAccountIdAsync(string apiKey);
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Bit.Core.Models.Domain;
namespace Bit.Core.Abstractions
{
public interface IDeviceTrustCryptoService
{
Task<SymmetricCryptoKey> GetDeviceKeyAsync();
Task<DeviceResponse> TrustDeviceAsync();
}
}

View File

@@ -56,6 +56,8 @@ namespace Bit.Core.Abstractions
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null);
Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null);
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
Task<bool?> GetAutofillTileAddedAsync();

View File

@@ -91,6 +91,7 @@
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
public static string EncKeyKey(string userId) => $"encKey_{userId}";
public static string DeviceKeyKey(string userId) => $"deviceKey_{userId}";
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";

View File

@@ -0,0 +1,10 @@

namespace Bit.Core.Models.Request
{
public class TrustedDeviceKeysRequest
{
public string EncryptedUserKey { get; set; }
public string EncryptedPublicKey { get; set; }
public string EncryptedPrivateKey { get; set; }
}
}

View File

@@ -1,12 +0,0 @@
using System;
namespace Bit.Core.Models.Request
{
public class UpdateTrustedDeviceKeysRequest
{
public string DeviceIdentifier { get; set; }
public string DevicePublicKeyEncryptedUserKey { get; set; }
public string UserKeyEncryptedDevicePublicKey { get; set; }
public string DeviceKeyEncryptedDevicePrivateKey { get; set; }
}
}

View File

@@ -1,17 +1,13 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Enums;
namespace Bit.Core.Models.Response
public class DeviceResponse
{
public class DeviceResponse
{
public string Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string Identifier { get; set; }
public DeviceType Type { get; set; }
public string CreationDate { get; set; }
public string RevisionDate { get; set; }
}
public string Id { get; set; }
public int Name { get; set; }
public string Identifier { get; set; }
public DeviceType Type { get; set; }
public string CreationDate { get; set; }
public string EncryptedUserKey { get; set; }
public string EncryptedPublicKey { get; set; }
public string EncryptedPrivateKey { get; set; }
}

View File

@@ -419,10 +419,14 @@ namespace Bit.Core.Services
HttpMethod.Post, "/devices/exist-by-types", deviceTypes, true, true);
}
public Task<DeviceResponse> PutUpdateTrustedDeviceKeys(UpdateTrustedDeviceKeysRequest request)
public Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier)
{
return SendAsync<UpdateTrustedDeviceKeysRequest, DeviceResponse>(
HttpMethod.Put, $"/devices/${request.DeviceIdentifier}/keys", request, true, true);
return SendAsync<object, DeviceResponse>(HttpMethod.Get, $"/devices/identifier/{deviceIdentifier}", null, true, true);
}
public Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest trustedDeviceKeysRequest)
{
return SendAsync<TrustedDeviceKeysRequest, DeviceResponse>(HttpMethod.Put, $"/devices/{deviceIdentifier}/keys", trustedDeviceKeysRequest, true, true);
}
#endregion

View File

@@ -0,0 +1,81 @@

using System;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
namespace Bit.Core.Services
{
public class DeviceTrustCryptoService : IDeviceTrustCryptoService
{
private readonly IApiService _apiService;
private readonly IAppIdService _appIdService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly ICryptoService _cryptoService;
private readonly IStateService _stateService;
private const int DEVICE_KEY_SIZE = 64;
public DeviceTrustCryptoService(
IApiService apiService,
IAppIdService appIdService,
ICryptoFunctionService cryptoFunctionService,
ICryptoService cryptoService,
IStateService stateService)
{
_apiService = apiService;
_appIdService = appIdService;
_cryptoFunctionService = cryptoFunctionService;
_cryptoService = cryptoService;
_stateService = stateService;
}
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync()
{
return await _stateService.GetDeviceKeyAsync();
}
private async Task SetDeviceKeyAsync(SymmetricCryptoKey deviceKey)
{
await _stateService.SetDeviceKeyAsync(deviceKey);
}
public async Task<DeviceResponse> TrustDeviceAsync()
{
// Attempt to get user key
var userKey = await _cryptoService.GetEncKeyAsync();
if (userKey == null)
{
return null;
}
// Generate deviceKey
var deviceKey = await MakeDeviceKeyAsync();
// Generate asymmetric RSA key pair: devicePrivateKey, devicePublicKey
var (devicePublicKey, devicePrivateKey) = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
// Send encrypted keys to server
var deviceIdentifier = await _appIdService.GetAppIdAsync();
var deviceRequest = new TrustedDeviceKeysRequest
{
EncryptedUserKey = (await _cryptoService.RsaEncryptAsync(userKey.EncKey, devicePublicKey)).EncryptedString,
EncryptedPublicKey = (await _cryptoService.EncryptAsync(devicePublicKey, userKey)).EncryptedString,
EncryptedPrivateKey = (await _cryptoService.EncryptAsync(devicePrivateKey, deviceKey)).EncryptedString,
};
var deviceResponse = await _apiService.UpdateTrustedDeviceKeysAsync(deviceIdentifier, deviceRequest);
// Store device key if successful
await SetDeviceKeyAsync(deviceKey);
return deviceResponse;
}
private async Task<SymmetricCryptoKey> MakeDeviceKeyAsync()
{
// Create 512-bit device key
var randomBytes = await _cryptoFunctionService.RandomBytesAsync(DEVICE_KEY_SIZE);
return new SymmetricCryptoKey(randomBytes);
}
}
}

View File

@@ -482,6 +482,17 @@ namespace Bit.Core.Services
await SetValueAsync(Constants.EncPrivateKeyKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
{
var deviceKeyB64 = await _storageMediatorService.GetAsync<string>(Constants.DeviceKeyKey(userId), true);
return new SymmetricCryptoKey(Convert.FromBase64String(deviceKeyB64));
}
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
{
await _storageMediatorService.SaveAsync(Constants.DeviceKeyKey(userId), value.KeyB64, true);
}
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },