mirror of
https://github.com/bitwarden/mobile
synced 2026-01-04 01:23:15 +00:00
Port send jslib to mobile (#1219)
* Expand Hkdf crypto functions * Add tests for hkdf crypto functions Took the testing infrastructure from bitwarden/server * Move Hkdf to cryptoFunctionService * Port changes from bitwarden/jslib#192 * Port changes from bitwarden/jslib#205 * Make Send Expiration Optional implement changes from bitwarden/jslib#242 * Bug fixes found by testing * Test helpers * Test conversion between model types * Test SendService These are mostly happy-path tests to ensure a reasonably correct implementation * Add run tests step to GitHub Actions * Test send decryption * Test Request generation from Send * Constructor dependencies on separate lines * Remove unused testing infrastructure * Rename to match class name * Move fat arrows to previous lines * Handle exceptions in App layer * PR review cleanups * Throw when attempting to save an unkown Send Type I think it's best to only throw on unknown send types here. I don't think we want to throw whenever we encounter one since that would do bad things like lock up Sync if clients get out of date relative to servers. Instead, keep the client from ruining saved data by complaining last minute that it doesn't know what it's doing.
This commit is contained in:
@@ -99,7 +99,7 @@ namespace Bit.Core.Models.Domain
|
||||
public string Data { get; private set; }
|
||||
public string Mac { get; private set; }
|
||||
|
||||
public async Task<string> DecryptAsync(string orgId = null)
|
||||
public async Task<string> DecryptAsync(string orgId = null, SymmetricCryptoKey key = null)
|
||||
{
|
||||
if (_decryptedValue != null)
|
||||
{
|
||||
@@ -109,8 +109,11 @@ namespace Bit.Core.Models.Domain
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
try
|
||||
{
|
||||
var orgKey = await cryptoService.GetOrgKeyAsync(orgId);
|
||||
_decryptedValue = await cryptoService.DecryptToUtf8Async(this, orgKey);
|
||||
if (key == null)
|
||||
{
|
||||
key = await cryptoService.GetOrgKeyAsync(orgId);
|
||||
}
|
||||
_decryptedValue = await cryptoService.DecryptToUtf8Async(this, key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Bit.Core.Models.Domain
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task<V> DecryptObjAsync<V, D>(V viewModel, D domain, HashSet<string> map, string orgId)
|
||||
protected async Task<V> DecryptObjAsync<V, D>(V viewModel, D domain, HashSet<string> map, string orgId, SymmetricCryptoKey key = null)
|
||||
where V : View.View
|
||||
{
|
||||
var viewModelType = viewModel.GetType();
|
||||
@@ -64,7 +64,7 @@ namespace Bit.Core.Models.Domain
|
||||
string val = null;
|
||||
if (domainPropInfo.GetValue(domain) is CipherString domainProp)
|
||||
{
|
||||
val = await domainProp.DecryptAsync(orgId);
|
||||
val = await domainProp.DecryptAsync(orgId, key);
|
||||
}
|
||||
var viewModelPropInfo = viewModelType.GetProperty(propName);
|
||||
viewModelPropInfo.SetValue(viewModel, val, null);
|
||||
|
||||
91
src/Core/Models/Domain/Send.cs
Normal file
91
src/Core/Models/Domain/Send.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
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
|
||||
{
|
||||
public class Send : Domain
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string AccessId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public SendType Type { get; set; }
|
||||
public CipherString Name { get; set; }
|
||||
public CipherString Notes { get; set; }
|
||||
public SendFile File { get; set; }
|
||||
public SendText Text { get; set; }
|
||||
public CipherString Key { get; set; }
|
||||
public int? MaxAccessCount { get; set; }
|
||||
public int AccessCount { get; set; }
|
||||
public DateTime RevisionDate { get; set; }
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
public DateTime DeletionDate { get; set; }
|
||||
public string Password { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public Send() : base() { }
|
||||
|
||||
public Send(SendData data, bool alreadyEncrypted = false) : base()
|
||||
{
|
||||
BuildDomainModel(this, data, new HashSet<string>{
|
||||
"Id",
|
||||
"AccessId",
|
||||
"UserId",
|
||||
"Name",
|
||||
"Notes",
|
||||
"Key",
|
||||
}, alreadyEncrypted, new HashSet<string> { "Id", "AccessId", "UserId" });
|
||||
|
||||
Type = data.Type;
|
||||
MaxAccessCount = data.MaxAccessCount;
|
||||
AccessCount = data.AccessCount;
|
||||
Password = data.Password;
|
||||
Disabled = data.Disabled;
|
||||
RevisionDate = data.RevisionDate;
|
||||
DeletionDate = data.DeletionDate;
|
||||
ExpirationDate = data.ExpirationDate;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case SendType.Text:
|
||||
Text = new SendText(data.Text, alreadyEncrypted);
|
||||
break;
|
||||
case SendType.File:
|
||||
File = new SendFile(data.File, alreadyEncrypted);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SendView> DecryptAsync()
|
||||
{
|
||||
var view = new SendView(this);
|
||||
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
|
||||
view.Key = await cryptoService.DecryptToBytesAsync(Key, null);
|
||||
view.CryptoKey = await cryptoService.MakeSendKeyAsync(view.Key);
|
||||
|
||||
await DecryptObjAsync(view, this, new HashSet<string> { "Name", "Notes" }, null, view.CryptoKey);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case SendType.File:
|
||||
view.File = await this.File.DecryptAsync(view.CryptoKey);
|
||||
break;
|
||||
case SendType.Text:
|
||||
view.Text = await this.Text.DecryptAsync(view.CryptoKey);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/Core/Models/Domain/SendFile.cs
Normal file
27
src/Core/Models/Domain/SendFile.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.View;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class SendFile : Domain
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Size { get; set; }
|
||||
public string SizeName { get; set; }
|
||||
public CipherString FileName { get; set; }
|
||||
|
||||
public SendFile() : base() { }
|
||||
|
||||
public SendFile(SendFileData file, bool alreadyEncrypted = false) : base()
|
||||
{
|
||||
Size = file.Size;
|
||||
BuildDomainModel(this, file, new HashSet<string> { "Id", "Url", "SizeName", "FileName" }, alreadyEncrypted, new HashSet<string> { "Id", "Url", "SizeName" });
|
||||
}
|
||||
|
||||
public Task<SendFileView> DecryptAsync(SymmetricCryptoKey key) =>
|
||||
DecryptObjAsync(new SendFileView(this), this, new HashSet<string> { "FileName" }, null, key);
|
||||
}
|
||||
}
|
||||
25
src/Core/Models/Domain/SendText.cs
Normal file
25
src/Core/Models/Domain/SendText.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.View;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class SendText : Domain
|
||||
{
|
||||
public CipherString Text { get; set; }
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
public SendText() : base() { }
|
||||
|
||||
public SendText(SendTextData data, bool alreadyEncrypted = false) : base()
|
||||
{
|
||||
Hidden = data.Hidden;
|
||||
BuildDomainModel(this, data, new HashSet<string> { "Text" }, alreadyEncrypted);
|
||||
}
|
||||
|
||||
public Task<SendTextView> DecryptAsync(SymmetricCryptoKey key) =>
|
||||
DecryptObjAsync(new SendTextView(this), this, new HashSet<string> { "Text" }, null, key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user