1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-31 15:43:19 +00:00

PM-4739 Implement checksum uri validation

This commit is contained in:
Carlos Gonçalves
2023-12-04 16:39:23 +00:00
parent f013f69669
commit b2374ed5f6
9 changed files with 37 additions and 5 deletions

View File

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

View File

@@ -70,7 +70,7 @@ namespace Bit.Core
public const int Argon2Parallelism = 4;
public const int MasterPasswordMinimumChars = 12;
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 DefaultFido2CredentialAlgorithm = "ECDSA";
public const string DefaultFido2CredentialCurve = "P-256";

View File

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

View File

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

View File

@@ -93,6 +93,7 @@ namespace Bit.Core.Models.Domain
public async Task<CipherView> DecryptAsync()
{
var model = new CipherView(this);
var bypassValidation = true;
if (Key != null)
{
@@ -104,6 +105,7 @@ namespace Bit.Core.Models.Domain
var key = await cryptoService.DecryptToBytesAsync(Key, orgKey);
model.Key = new CipherKey(key);
bypassValidation = false;
}
await DecryptObjAsync(model, this, new HashSet<string>
@@ -115,7 +117,7 @@ namespace Bit.Core.Models.Domain
switch (Type)
{
case Enums.CipherType.Login:
model.Login = await Login.DecryptAsync(OrganizationId, model.Key);
model.Login = await Login.DecryptAsync(OrganizationId, bypassValidation, model.Key);
break;
case Enums.CipherType.SecureNote:
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key);

View File

@@ -31,7 +31,7 @@ namespace Bit.Core.Models.Domain
public EncString Totp { 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 bypassValidation, SymmetricCryptoKey key = null)
{
var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string>
{
@@ -44,7 +44,10 @@ namespace Bit.Core.Models.Domain
view.Uris = new List<LoginUriView>();
foreach (var uri in Uris)
{
view.Uris.Add(await uri.DecryptAsync(orgId, key));
var loginUriView = await uri.DecryptAsync(orgId, key);
if (bypassValidation || (await uri.ValidateChecksum(loginUriView.Uri, orgId, key))) {
view.Uris.Add(loginUriView);
}
}
}
if (Fido2Credentials != null)

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Domain
{
@@ -23,6 +25,7 @@ namespace Bit.Core.Models.Domain
public EncString Uri { get; set; }
public UriMatchType? Match { get; set; }
public EncString UriChecksum { get; set; }
public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
{
@@ -35,5 +38,16 @@ namespace Bit.Core.Models.Domain
BuildDataModel(this, u, _map, new HashSet<string> { "Match" });
return u;
}
public async Task<bool> ValidateChecksum(string clearTextUri, string orgId, SymmetricCryptoKey key)
{
// HACK: I don't like resolving this here but I can't see a better way without
// refactoring a lot of things.
var cryptoService = ServiceContainer.Resolve<ICryptoService>();
var localChecksum = await cryptoService.HashAsync(clearTextUri, CryptoHashAlgorithm.Sha256);
var remoteChecksum = await this.UriChecksum.DecryptAsync(orgId, key);
return remoteChecksum == localChecksum;
}
}
}

View File

@@ -1153,6 +1153,8 @@ namespace Bit.Core.Services
Match = uri.Match
};
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);
}
}

View File

@@ -1,4 +1,4 @@
using System;
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@@ -730,6 +730,12 @@ namespace Bit.Core.Services
}
}
public async Task<string> HashAsync(string value, CryptoHashAlgorithm hashAlgorithm)
{
var hashArray = await _cryptoFunctionService.HashAsync(value, hashAlgorithm);
return Convert.ToBase64String(hashArray);
}
// --HELPER METHODS--
private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null)