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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -6,5 +6,6 @@ namespace Bit.Core.Models.Api
|
||||
{
|
||||
public string Uri { get; set; }
|
||||
public UriMatchType? Match { get; set; }
|
||||
public string UriChecksum { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user