diff --git a/src/Core/Models/Domain/Attachment.cs b/src/Core/Models/Domain/Attachment.cs index b12395a58..d4669e988 100644 --- a/src/Core/Models/Domain/Attachment.cs +++ b/src/Core/Models/Domain/Attachment.cs @@ -34,6 +34,7 @@ namespace Bit.Core.Models.Domain public string SizeName { get; set; } public EncString Key { get; set; } public EncString FileName { get; set; } + public EncString CipherKey { get; set; } public async Task DecryptAsync(string orgId, SymmetricCryptoKey key = null) { diff --git a/src/Core/Models/Domain/Cipher.cs b/src/Core/Models/Domain/Cipher.cs index a7a2d6943..79152eb3c 100644 --- a/src/Core/Models/Domain/Cipher.cs +++ b/src/Core/Models/Domain/Cipher.cs @@ -134,14 +134,25 @@ namespace Bit.Core.Models.Domain { model.Attachments = new List(); var tasks = new List(); - async Task decryptAndAddAttachmentAsync(Attachment attachment) - { - var decAttachment = await attachment.DecryptAsync(OrganizationId, model.Key); + async Task decryptAndAddAttachmentAsync(Attachment attachment, SymmetricCryptoKey decKey) + { + var decAttachment = await attachment.DecryptAsync(OrganizationId, model.Key ?? decKey); model.Attachments.Add(decAttachment); } foreach (var attachment in Attachments) { - tasks.Add(decryptAndAddAttachmentAsync(attachment)); + SymmetricCryptoKey decKey = null; + //If the cipher.key is null but the attachment.cipherKey has a value we will use it to decrypt the attachment + if (Key == null && attachment.CipherKey != null) + { + var cryptoService = ServiceContainer.Resolve(); + + var orgKey = await cryptoService.GetOrgKeyAsync(OrganizationId); + + var key = await cryptoService.DecryptToBytesAsync(attachment.CipherKey, orgKey); + decKey = new CipherKey(key); + } + tasks.Add(decryptAndAddAttachmentAsync(attachment, decKey)); } await Task.WhenAll(tasks); } diff --git a/src/Core/Models/View/AttachmentView.cs b/src/Core/Models/View/AttachmentView.cs index 879ec583c..1e712ed3c 100644 --- a/src/Core/Models/View/AttachmentView.cs +++ b/src/Core/Models/View/AttachmentView.cs @@ -20,6 +20,7 @@ namespace Bit.Core.Models.View public string SizeName { get; set; } public string FileName { get; set; } public SymmetricCryptoKey Key { get; set; } + public CipherKey CipherKey { get; set; } public long FileSize { diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index e22a5f5e9..c2377bdb5 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -1,4 +1,4 @@ -//#define ENABLE_NEW_CIPHER_KEY_ENCRYPTION_ON_CREATION +#define ENABLE_NEW_CIPHER_KEY_ENCRYPTION_ON_CREATION using System; using System.Collections.Generic; @@ -571,25 +571,39 @@ namespace Bit.Core.Services await UpsertAsync(data); } - public async Task ShareWithServerAsync(CipherView cipher, string organizationId, HashSet collectionIds) + public async Task ShareWithServerAsync(CipherView cipherView, string organizationId, HashSet collectionIds) { var attachmentTasks = new List(); - if (cipher.Attachments != null) + Cipher cipher = null; + //If the cipher doesn't have a key, we update it + if(cipherView.Key == null) { - foreach (var attachment in cipher.Attachments) + cipher = await EncryptAsync(cipherView); + var putCipherRequest = new CipherRequest(cipher); + var putCipherResponse = await _apiService.PutCipherAsync(cipherView.Id, putCipherRequest); + var cipherData = new CipherData(putCipherResponse, await _stateService.GetActiveUserIdAsync()); + await UpsertAsync(cipherData); + cipher = await GetAsync(cipherView.Id); + cipherView = await cipher.DecryptAsync(); + } + + if (cipherView.Attachments != null) + { + foreach (var attachment in cipherView.Attachments) { if (attachment.Key == null) { - attachmentTasks.Add(ShareAttachmentWithServerAsync(attachment, cipher.Id, organizationId)); + attachmentTasks.Add(ShareAttachmentWithServerAsync(attachment, cipherView.Id, organizationId, cipherView.Key, cipher?.Key)); + attachment.CipherKey = cipherView.Key; } } } await Task.WhenAll(attachmentTasks); - cipher.OrganizationId = organizationId; - cipher.CollectionIds = collectionIds; - var encCipher = await EncryptAsync(cipher); + cipherView.OrganizationId = organizationId; + cipherView.CollectionIds = collectionIds; + var encCipher = await EncryptAsync(cipherView); var request = new CipherShareRequest(encCipher); - var response = await _apiService.PutShareCipherAsync(cipher.Id, request); + var response = await _apiService.PutShareCipherAsync(cipherView.Id, request); var userId = await _stateService.GetActiveUserIdAsync(); var data = new CipherData(response, userId, collectionIds); await UpsertAsync(data); @@ -855,12 +869,13 @@ namespace Bit.Core.Services // Helpers - private async Task> MakeAttachmentKeyAsync(string organizationId, Cipher cipher = null, CipherView cipherView = null) + private async Task> MakeAttachmentKeyAsync(string organizationId, Cipher cipher = null, CipherView cipherView = null, SymmetricCryptoKey cipherKey = null) { var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId); - SymmetricCryptoKey encryptionKey = orgKey; - if (cipher != null && cipherView != null) + //We give priority to the use of cipher.key if it exists + SymmetricCryptoKey encryptionKey = cipherKey ?? orgKey; + if (cipher != null && cipherView != null && cipher.Key == null) { encryptionKey = await UpdateCipherAndGetCipherKeyAsync(cipher, cipherView, orgKey, false); } @@ -872,7 +887,7 @@ namespace Bit.Core.Services } private async Task ShareAttachmentWithServerAsync(AttachmentView attachmentView, string cipherId, - string organizationId) + string organizationId, SymmetricCryptoKey cipherKey = null, EncString cipherKeyProtected = null) { var attachmentResponse = await _httpClient.GetAsync(attachmentView.Url); if (!attachmentResponse.IsSuccessStatusCode) @@ -883,7 +898,7 @@ namespace Bit.Core.Services var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync(); var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null); - var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId); + var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId, cipherKey:cipherKey); var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, encKey); var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey); @@ -892,6 +907,10 @@ namespace Bit.Core.Services var fd = new MultipartFormDataContent(boundary); fd.Add(new StringContent(protectedAttachmentKey.EncryptedString), "key"); fd.Add(new StreamContent(new MemoryStream(encFileData.Buffer)), "data", encFileName.EncryptedString); + if(cipherKey != null && cipherKeyProtected != null) + { + fd.Add(new StringContent(cipherKeyProtected.EncryptedString), "cipherKey"); + } await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId); }