mirror of
https://github.com/bitwarden/mobile
synced 2025-12-21 18:53:29 +00:00
[PM-115] Cipher key encryption update (#2421)
* PM-115 Added new cipher key and encryption/decryption mechanisms on cipher * PM-115 fix format * PM-115 removed ForceKeyRotation from new cipher encryption model given that another approach will be taken * [PM-1690] Added minimum server version restriction to cipher key encryption (#2463) * PM-1690 added minimum server version restriction to cipher key encryption and also change the force key rotation flag * PM-1690 Updated min server version for new cipher encryption key and fixed configService registration * PM-1690 removed forcekeyrotation * PM-115 Temporarily Changed cipher key new encryption config to help testing (this change should be reseted eventually) * PM-2456 Fix attachment encryption on new cipher item encryption model (#2556) * PM-2531 Fix new cipher encryption on adding attachments on ciphers with no item level key (#2559) * PM-115 Changed temporarily cipher key encryption min server version to 2023.6.0 to test * PM-115 Reseted cipher key encryption minimum server version to 2023.5.0 and disable new cipher key on local cipher creation * Added Key value to the cipher export model (#2628) * Update Constants.cs Updated minimum encryption server version to 2023.9.0 so QA can test its behavior * PM-115 Fix file format * PM-115 Changed new encryption off and minimum new encryption server version to 2023.8.0 for testing purposes * PM-115 Changed CIpher key encryption minimum server version to 2023.9.0 * PM-3737 Remove suffix on client version sent to server (#2779) * PM-115 QA testing server min version and enable new cipher key encryption * PM-115 Disable new cipher encryption creation and change minimum server encryption version to 2023.9.1 --------- Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e97a37222a
commit
3cdf5ccd3b
@@ -28,6 +28,7 @@ namespace Bit.Core.Models.Data
|
||||
Notes = response.Notes;
|
||||
CollectionIds = collectionIds?.ToList() ?? response.CollectionIds;
|
||||
Reprompt = response.Reprompt;
|
||||
Key = response.Key;
|
||||
|
||||
try // Added to address Issue (https://github.com/bitwarden/mobile/issues/1006)
|
||||
{
|
||||
@@ -88,5 +89,6 @@ namespace Bit.Core.Models.Data
|
||||
public List<PasswordHistoryData> PasswordHistory { get; set; }
|
||||
public List<string> CollectionIds { get; set; }
|
||||
public Enums.CipherRepromptType Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
@@ -11,11 +13,11 @@ namespace Bit.Core.Models.Domain
|
||||
{
|
||||
private HashSet<string> _map = new HashSet<string>
|
||||
{
|
||||
"Id",
|
||||
"Url",
|
||||
"SizeName",
|
||||
"FileName",
|
||||
"Key"
|
||||
nameof(Id),
|
||||
nameof(Url),
|
||||
nameof(SizeName),
|
||||
nameof(FileName),
|
||||
nameof(Key)
|
||||
};
|
||||
|
||||
public Attachment() { }
|
||||
@@ -23,7 +25,7 @@ namespace Bit.Core.Models.Domain
|
||||
public Attachment(AttachmentData obj, bool alreadyEncrypted = false)
|
||||
{
|
||||
Size = obj.Size;
|
||||
BuildDomainModel(this, obj, _map, alreadyEncrypted, new HashSet<string> { "Id", "Url", "SizeName" });
|
||||
BuildDomainModel(this, obj, _map, alreadyEncrypted, new HashSet<string> { nameof(Id), nameof(Url), nameof(SizeName) });
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
@@ -33,25 +35,26 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString Key { get; set; }
|
||||
public EncString FileName { get; set; }
|
||||
|
||||
public async Task<AttachmentView> DecryptAsync(string orgId)
|
||||
public async Task<AttachmentView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
var view = await DecryptObjAsync(new AttachmentView(this), this, new HashSet<string>
|
||||
{
|
||||
"FileName"
|
||||
}, orgId);
|
||||
nameof(FileName)
|
||||
}, orgId, key);
|
||||
|
||||
if (Key != null)
|
||||
{
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
try
|
||||
{
|
||||
var orgKey = await cryptoService.GetOrgKeyAsync(orgId);
|
||||
var decValue = await cryptoService.DecryptToBytesAsync(Key, orgKey);
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>();
|
||||
|
||||
var decryptKey = key ?? await cryptoService.GetOrgKeyAsync(orgId);
|
||||
var decValue = await cryptoService.DecryptToBytesAsync(Key, decryptKey);
|
||||
view.Key = new SymmetricCryptoKey(decValue);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: error?
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
@@ -61,7 +64,7 @@ namespace Bit.Core.Models.Domain
|
||||
{
|
||||
var a = new AttachmentData();
|
||||
a.Size = Size;
|
||||
BuildDataModel(this, a, _map, new HashSet<string> { "Id", "Url", "SizeName" });
|
||||
BuildDataModel(this, a, _map, new HashSet<string> { nameof(Id), nameof(Url), nameof(SizeName) });
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString ExpYear { get; set; }
|
||||
public EncString Code { get; set; }
|
||||
|
||||
public Task<CardView> DecryptAsync(string orgId)
|
||||
public Task<CardView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return DecryptObjAsync(new CardView(this), this, _map, orgId);
|
||||
return DecryptObjAsync(new CardView(this), this, _map, orgId, key);
|
||||
}
|
||||
|
||||
public CardData ToCardData()
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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
|
||||
{
|
||||
@@ -16,12 +18,12 @@ namespace Bit.Core.Models.Domain
|
||||
{
|
||||
BuildDomainModel(this, obj, new HashSet<string>
|
||||
{
|
||||
"Id",
|
||||
"OrganizationId",
|
||||
"FolderId",
|
||||
"Name",
|
||||
"Notes"
|
||||
}, alreadyEncrypted, new HashSet<string> { "Id", "OrganizationId", "FolderId" });
|
||||
nameof(Id),
|
||||
nameof(OrganizationId),
|
||||
nameof(FolderId),
|
||||
nameof(Name),
|
||||
nameof(Notes)
|
||||
}, alreadyEncrypted, new HashSet<string> { nameof(Id), nameof(OrganizationId), nameof(FolderId) });
|
||||
|
||||
Type = obj.Type;
|
||||
Favorite = obj.Favorite;
|
||||
@@ -34,6 +36,11 @@ namespace Bit.Core.Models.Domain
|
||||
LocalData = localData;
|
||||
Reprompt = obj.Reprompt;
|
||||
|
||||
if (obj.Key != null)
|
||||
{
|
||||
Key = new EncString(obj.Key);
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case Enums.CipherType.Login:
|
||||
@@ -81,29 +88,43 @@ namespace Bit.Core.Models.Domain
|
||||
public List<PasswordHistory> PasswordHistory { get; set; }
|
||||
public HashSet<string> CollectionIds { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public EncString Key { get; set; }
|
||||
|
||||
public async Task<CipherView> DecryptAsync()
|
||||
{
|
||||
var model = new CipherView(this);
|
||||
|
||||
if (Key != null)
|
||||
{
|
||||
// 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 orgKey = await cryptoService.GetOrgKeyAsync(OrganizationId);
|
||||
|
||||
var key = await cryptoService.DecryptToBytesAsync(Key, orgKey);
|
||||
model.Key = new CipherKey(key);
|
||||
}
|
||||
|
||||
await DecryptObjAsync(model, this, new HashSet<string>
|
||||
{
|
||||
"Name",
|
||||
"Notes"
|
||||
}, OrganizationId);
|
||||
nameof(Name),
|
||||
nameof(Notes)
|
||||
}, OrganizationId, model.Key);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case Enums.CipherType.Login:
|
||||
model.Login = await Login.DecryptAsync(OrganizationId);
|
||||
model.Login = await Login.DecryptAsync(OrganizationId, model.Key);
|
||||
break;
|
||||
case Enums.CipherType.SecureNote:
|
||||
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId);
|
||||
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key);
|
||||
break;
|
||||
case Enums.CipherType.Card:
|
||||
model.Card = await Card.DecryptAsync(OrganizationId);
|
||||
model.Card = await Card.DecryptAsync(OrganizationId, model.Key);
|
||||
break;
|
||||
case Enums.CipherType.Identity:
|
||||
model.Identity = await Identity.DecryptAsync(OrganizationId);
|
||||
model.Identity = await Identity.DecryptAsync(OrganizationId, model.Key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -115,7 +136,7 @@ namespace Bit.Core.Models.Domain
|
||||
var tasks = new List<Task>();
|
||||
async Task decryptAndAddAttachmentAsync(Attachment attachment)
|
||||
{
|
||||
var decAttachment = await attachment.DecryptAsync(OrganizationId);
|
||||
var decAttachment = await attachment.DecryptAsync(OrganizationId, model.Key);
|
||||
model.Attachments.Add(decAttachment);
|
||||
}
|
||||
foreach (var attachment in Attachments)
|
||||
@@ -130,7 +151,7 @@ namespace Bit.Core.Models.Domain
|
||||
var tasks = new List<Task>();
|
||||
async Task decryptAndAddFieldAsync(Field field)
|
||||
{
|
||||
var decField = await field.DecryptAsync(OrganizationId);
|
||||
var decField = await field.DecryptAsync(OrganizationId, model.Key);
|
||||
model.Fields.Add(decField);
|
||||
}
|
||||
foreach (var field in Fields)
|
||||
@@ -145,7 +166,7 @@ namespace Bit.Core.Models.Domain
|
||||
var tasks = new List<Task>();
|
||||
async Task decryptAndAddHistoryAsync(PasswordHistory ph)
|
||||
{
|
||||
var decPh = await ph.DecryptAsync(OrganizationId);
|
||||
var decPh = await ph.DecryptAsync(OrganizationId, model.Key);
|
||||
model.PasswordHistory.Add(decPh);
|
||||
}
|
||||
foreach (var ph in PasswordHistory)
|
||||
@@ -174,11 +195,12 @@ namespace Bit.Core.Models.Domain
|
||||
CollectionIds = CollectionIds.ToList(),
|
||||
DeletedDate = DeletedDate,
|
||||
Reprompt = Reprompt,
|
||||
Key = Key?.EncryptedString
|
||||
};
|
||||
BuildDataModel(this, c, new HashSet<string>
|
||||
{
|
||||
"Name",
|
||||
"Notes"
|
||||
nameof(Name),
|
||||
nameof(Notes)
|
||||
});
|
||||
switch (c.Type)
|
||||
{
|
||||
|
||||
@@ -41,9 +41,9 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString UserName { get; set; }
|
||||
public EncString Counter { get; set; }
|
||||
|
||||
public async Task<Fido2KeyView> DecryptAsync(string orgId)
|
||||
public async Task<Fido2KeyView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return await DecryptObjAsync(new Fido2KeyView(), this, EncryptableProperties, orgId);
|
||||
return await DecryptObjAsync(new Fido2KeyView(), this, EncryptableProperties, orgId, key);
|
||||
}
|
||||
|
||||
public Fido2KeyData ToFido2KeyData()
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace Bit.Core.Models.Domain
|
||||
public FieldType Type { get; set; }
|
||||
public LinkedIdType? LinkedId { get; set; }
|
||||
|
||||
public Task<FieldView> DecryptAsync(string orgId)
|
||||
public Task<FieldView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return DecryptObjAsync(new FieldView(this), this, _map, orgId);
|
||||
return DecryptObjAsync(new FieldView(this), this, _map, orgId, key);
|
||||
}
|
||||
|
||||
public FieldData ToFieldData()
|
||||
|
||||
@@ -55,9 +55,9 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString PassportNumber { get; set; }
|
||||
public EncString LicenseNumber { get; set; }
|
||||
|
||||
public Task<IdentityView> DecryptAsync(string orgId)
|
||||
public Task<IdentityView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return DecryptObjAsync(new IdentityView(this), this, _map, orgId);
|
||||
return DecryptObjAsync(new IdentityView(this), this, _map, orgId, key);
|
||||
}
|
||||
|
||||
public IdentityData ToIdentityData()
|
||||
|
||||
@@ -31,20 +31,20 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString Totp { get; set; }
|
||||
public List<Fido2Key> Fido2Keys { get; set; }
|
||||
|
||||
public async Task<LoginView> DecryptAsync(string orgId)
|
||||
public async Task<LoginView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string>
|
||||
{
|
||||
"Username",
|
||||
"Password",
|
||||
"Totp"
|
||||
}, orgId);
|
||||
}, orgId, key);
|
||||
if (Uris != null)
|
||||
{
|
||||
view.Uris = new List<LoginUriView>();
|
||||
foreach (var uri in Uris)
|
||||
{
|
||||
view.Uris.Add(await uri.DecryptAsync(orgId));
|
||||
view.Uris.Add(await uri.DecryptAsync(orgId, key));
|
||||
}
|
||||
}
|
||||
if (Fido2Keys != null)
|
||||
@@ -52,7 +52,7 @@ namespace Bit.Core.Models.Domain
|
||||
view.Fido2Keys = new List<Fido2KeyView>();
|
||||
foreach (var fido2Key in Fido2Keys)
|
||||
{
|
||||
view.Fido2Keys.Add(await fido2Key.DecryptAsync(orgId));
|
||||
view.Fido2Keys.Add(await fido2Key.DecryptAsync(orgId, key));
|
||||
}
|
||||
}
|
||||
return view;
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString Uri { get; set; }
|
||||
public UriMatchType? Match { get; set; }
|
||||
|
||||
public Task<LoginUriView> DecryptAsync(string orgId)
|
||||
public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return DecryptObjAsync(new LoginUriView(this), this, _map, orgId);
|
||||
return DecryptObjAsync(new LoginUriView(this), this, _map, orgId, key);
|
||||
}
|
||||
|
||||
public LoginUriData ToLoginUriData()
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace Bit.Core.Models.Domain
|
||||
public EncString Password { get; set; }
|
||||
public DateTime LastUsedDate { get; set; }
|
||||
|
||||
public Task<PasswordHistoryView> DecryptAsync(string orgId)
|
||||
public Task<PasswordHistoryView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return DecryptObjAsync(new PasswordHistoryView(this), this, _map, orgId);
|
||||
return DecryptObjAsync(new PasswordHistoryView(this), this, _map, orgId, key);
|
||||
}
|
||||
|
||||
public PasswordHistoryData ToPasswordHistoryData()
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.Core.Models.Domain
|
||||
|
||||
public SecureNoteType Type { get; set; }
|
||||
|
||||
public Task<SecureNoteView> DecryptAsync(string orgId)
|
||||
public Task<SecureNoteView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return Task.FromResult(new SecureNoteView(this));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
@@ -102,4 +101,11 @@ namespace Bit.Core.Models.Domain
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class CipherKey : SymmetricCryptoKey
|
||||
{
|
||||
public CipherKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.View;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -46,6 +47,7 @@ namespace Bit.Core.Models.Export
|
||||
Name = obj.Name?.EncryptedString;
|
||||
Notes = obj.Notes?.EncryptedString;
|
||||
Favorite = obj.Favorite;
|
||||
Key = obj.Key?.EncryptedString;
|
||||
|
||||
Fields = obj.Fields?.Select(f => new Field(f)).ToList();
|
||||
|
||||
@@ -82,6 +84,8 @@ namespace Bit.Core.Models.Export
|
||||
public Card Card { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Identity Identity { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Key { get; set; }
|
||||
|
||||
public CipherView ToView(Cipher req, CipherView view = null)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Bit.Core.Models.Request
|
||||
Favorite = cipher.Favorite;
|
||||
LastKnownRevisionDate = cipher.RevisionDate;
|
||||
Reprompt = cipher.Reprompt;
|
||||
Key = cipher.Key?.EncryptedString;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
@@ -125,5 +126,6 @@ namespace Bit.Core.Models.Request
|
||||
public Dictionary<string, AttachmentRequest> Attachments2 { get; set; }
|
||||
public DateTime LastKnownRevisionDate { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Bit.Core.Models.Response
|
||||
public List<string> CollectionIds { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace Bit.Core.Models.View
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public CipherKey Key { get; set; }
|
||||
|
||||
public ItemView Item
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user