Merge branch 'main' into feature/maui-migration

This commit is contained in:
Federico Maccaroni 2024-01-24 11:22:28 -03:00
commit e0b58461b5
No known key found for this signature in database
GPG key ID: 5D233F8F2B034536
13 changed files with 60 additions and 11 deletions

View file

@ -158,7 +158,7 @@ namespace Bit.Droid
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService, var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
platformUtilsService, new LazyResolve<IEventService>()); platformUtilsService, new LazyResolve<IEventService>());
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService); var cryptoService = new CryptoService(stateService, cryptoFunctionService, logger);
var biometricService = new BiometricService(stateService, cryptoService); var biometricService = new BiometricService(stateService, cryptoService);
var userPinService = new UserPinService(stateService, cryptoService); var userPinService = new UserPinService(stateService, cryptoService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService);

View file

@ -63,5 +63,7 @@ namespace Bit.Core.Abstractions
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey); Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
Task<MasterKey> GetOrDeriveMasterKeyAsync(string password, string userId = null); Task<MasterKey> GetOrDeriveMasterKeyAsync(string password, string userId = null);
Task UpdateMasterKeyAndUserKeyAsync(MasterKey masterKey); Task UpdateMasterKeyAndUserKeyAsync(MasterKey masterKey);
Task<string> HashAsync(string value, CryptoHashAlgorithm hashAlgorithm);
Task<bool> ValidateUriChecksumAsync(EncString remoteUriChecksum, string rawUri, string orgId, SymmetricCryptoKey key);
} }
} }

View file

@ -70,7 +70,7 @@ namespace Bit.Core
public const int Argon2Parallelism = 4; public const int Argon2Parallelism = 4;
public const int MasterPasswordMinimumChars = 12; public const int MasterPasswordMinimumChars = 12;
public const int CipherKeyRandomBytesLength = 64; public const int CipherKeyRandomBytesLength = 64;
public const string CipherKeyEncryptionMinServerVersion = "2023.9.1"; public const string CipherKeyEncryptionMinServerVersion = "2023.12.0";
public const string DefaultFido2CredentialType = "public-key"; public const string DefaultFido2CredentialType = "public-key";
public const string DefaultFido2CredentialAlgorithm = "ECDSA"; public const string DefaultFido2CredentialAlgorithm = "ECDSA";
public const string DefaultFido2CredentialCurve = "P-256"; public const string DefaultFido2CredentialCurve = "P-256";

View file

@ -6,5 +6,6 @@ namespace Bit.Core.Models.Api
{ {
public string Uri { get; set; } public string Uri { get; set; }
public UriMatchType? Match { get; set; } public UriMatchType? Match { get; set; }
public string UriChecksum { get; set; }
} }
} }

View file

@ -11,9 +11,11 @@ namespace Bit.Core.Models.Data
{ {
Uri = data.Uri; Uri = data.Uri;
Match = data.Match; Match = data.Match;
UriChecksum = data.UriChecksum;
} }
public string Uri { get; set; } public string Uri { get; set; }
public UriMatchType? Match { get; set; } public UriMatchType? Match { get; set; }
public string UriChecksum { get; set; }
} }
} }

View file

@ -115,7 +115,7 @@ namespace Bit.Core.Models.Domain
switch (Type) switch (Type)
{ {
case Enums.CipherType.Login: case Enums.CipherType.Login:
model.Login = await Login.DecryptAsync(OrganizationId, model.Key); model.Login = await Login.DecryptAsync(OrganizationId, Key == null, model.Key);
break; break;
case Enums.CipherType.SecureNote: case Enums.CipherType.SecureNote:
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key); model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key);

View file

@ -2,8 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Domain namespace Bit.Core.Models.Domain
{ {
@ -31,7 +33,7 @@ namespace Bit.Core.Models.Domain
public EncString Totp { get; set; } public EncString Totp { get; set; }
public List<Fido2Credential> Fido2Credentials { get; set; } public List<Fido2Credential> Fido2Credentials { get; set; }
public async Task<LoginView> DecryptAsync(string orgId, SymmetricCryptoKey key = null) public async Task<LoginView> DecryptAsync(string orgId, bool bypassUriChecksumValidation, SymmetricCryptoKey key = null)
{ {
var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string> var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string>
{ {
@ -41,10 +43,15 @@ namespace Bit.Core.Models.Domain
}, orgId, key); }, orgId, key);
if (Uris != null) if (Uris != null)
{ {
var cryptoService = ServiceContainer.Resolve<ICryptoService>();
view.Uris = new List<LoginUriView>(); view.Uris = new List<LoginUriView>();
foreach (var uri in Uris) foreach (var uri in Uris)
{ {
view.Uris.Add(await uri.DecryptAsync(orgId, key)); var loginUriView = await uri.DecryptAsync(orgId, key);
if (bypassUriChecksumValidation || await cryptoService.ValidateUriChecksumAsync(uri.UriChecksum, loginUriView.Uri, orgId, key))
{
view.Uris.Add(loginUriView);
}
} }
} }
if (Fido2Credentials != null) if (Fido2Credentials != null)

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
@ -10,7 +11,8 @@ namespace Bit.Core.Models.Domain
{ {
private HashSet<string> _map = new HashSet<string> private HashSet<string> _map = new HashSet<string>
{ {
"Uri" nameof(Uri),
nameof(UriChecksum)
}; };
public LoginUri() { } public LoginUri() { }
@ -23,10 +25,11 @@ namespace Bit.Core.Models.Domain
public EncString Uri { get; set; } public EncString Uri { get; set; }
public UriMatchType? Match { get; set; } public UriMatchType? Match { get; set; }
public EncString UriChecksum { get; set; }
public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null) public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
{ {
return DecryptObjAsync(new LoginUriView(this), this, _map, orgId, key); return DecryptObjAsync(new LoginUriView(this), this, _map.Where(m => m != nameof(UriChecksum)).ToHashSet<string>(), orgId, key);
} }
public LoginUriData ToLoginUriData() public LoginUriData ToLoginUriData()

View file

@ -17,10 +17,12 @@ namespace Bit.Core.Models.Export
{ {
Match = obj.Match; Match = obj.Match;
Uri = obj.Uri?.EncryptedString; Uri = obj.Uri?.EncryptedString;
UriChecksum = obj.UriChecksum?.EncryptedString;
} }
public UriMatchType? Match { get; set; } public UriMatchType? Match { get; set; }
public string Uri { get; set; } public string Uri { get; set; }
public string UriChecksum { get; set; }
public static LoginUriView ToView(LoginUri req, LoginUriView view = null) public static LoginUriView ToView(LoginUri req, LoginUriView view = null)
{ {

View file

@ -27,7 +27,7 @@ namespace Bit.Core.Models.Request
Login = new LoginApi Login = new LoginApi
{ {
Uris = cipher.Login.Uris?.Select( Uris = cipher.Login.Uris?.Select(
u => new LoginUriApi { Match = u.Match, Uri = u.Uri?.EncryptedString }).ToList(), u => new LoginUriApi { Match = u.Match, Uri = u.Uri?.EncryptedString, UriChecksum = u.UriChecksum?.EncryptedString }).ToList(),
Username = cipher.Login.Username?.EncryptedString, Username = cipher.Login.Username?.EncryptedString,
Password = cipher.Login.Password?.EncryptedString, Password = cipher.Login.Password?.EncryptedString,
PasswordRevisionDate = cipher.Login.PasswordRevisionDate, PasswordRevisionDate = cipher.Login.PasswordRevisionDate,

View file

@ -1147,13 +1147,15 @@ namespace Bit.Core.Services
if (model.Login.Uris != null) if (model.Login.Uris != null)
{ {
cipher.Login.Uris = new List<LoginUri>(); cipher.Login.Uris = new List<LoginUri>();
foreach (var uri in model.Login.Uris) foreach (var uri in model.Login.Uris.Where(u => u.Uri != null))
{ {
var loginUri = new LoginUri var loginUri = new LoginUri
{ {
Match = uri.Match Match = uri.Match
}; };
await EncryptObjPropertyAsync(uri, loginUri, new HashSet<string> { "Uri" }, key); await EncryptObjPropertyAsync(uri, loginUri, new HashSet<string> { "Uri" }, key);
var uriHash = await _cryptoService.HashAsync(uri.Uri, CryptoHashAlgorithm.Sha256);
loginUri.UriChecksum = await _cryptoService.EncryptAsync(uriHash, key);
cipher.Login.Uris.Add(loginUri); cipher.Login.Uris.Add(loginUri);
} }
} }

View file

@ -19,6 +19,7 @@ namespace Bit.Core.Services
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly ILogger _logger;
private SymmetricCryptoKey _legacyEtmKey; private SymmetricCryptoKey _legacyEtmKey;
private string _masterKeyHash; private string _masterKeyHash;
@ -29,10 +30,12 @@ namespace Bit.Core.Services
public CryptoService( public CryptoService(
IStateService stateService, IStateService stateService,
ICryptoFunctionService cryptoFunctionService) ICryptoFunctionService cryptoFunctionService,
ILogger logger)
{ {
_stateService = stateService; _stateService = stateService;
_cryptoFunctionService = cryptoFunctionService; _cryptoFunctionService = cryptoFunctionService;
_logger = logger;
} }
public void ClearCache() public void ClearCache()
@ -730,6 +733,33 @@ namespace Bit.Core.Services
} }
} }
public async Task<string> HashAsync(string value, CryptoHashAlgorithm hashAlgorithm)
{
var hashArray = await _cryptoFunctionService.HashAsync(value, hashAlgorithm);
return Convert.ToBase64String(hashArray);
}
public async Task<bool> ValidateUriChecksumAsync(EncString remoteUriChecksum, string rawUri, string orgId, SymmetricCryptoKey key)
{
try
{
if (remoteUriChecksum == null)
{
return false;
}
var localChecksum = await HashAsync(rawUri, CryptoHashAlgorithm.Sha256);
var remoteChecksum = await remoteUriChecksum.DecryptAsync(orgId, key);
return remoteChecksum == localChecksum;
}
catch (Exception ex)
{
_logger.Exception(ex);
return false;
}
}
// --HELPER METHODS-- // --HELPER METHODS--
private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null) private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null)

View file

@ -157,7 +157,7 @@ namespace Bit.iOS.Core.Utilities
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService, var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
messagingService, broadcasterService); messagingService, broadcasterService);
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService); var cryptoService = new CryptoService(stateService, cryptoFunctionService, logger);
var biometricService = new BiometricService(stateService, cryptoService); var biometricService = new BiometricService(stateService, cryptoService);
var userPinService = new UserPinService(stateService, cryptoService); var userPinService = new UserPinService(stateService, cryptoService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService);