1
0
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:
Matt Gibson
2021-01-25 14:27:38 -06:00
committed by GitHub
parent 9b6bf136f1
commit 8d5614cd7b
52 changed files with 2046 additions and 38 deletions

View File

@@ -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
{

View File

@@ -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);

View 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;
}
}
}

View 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);
}
}

View 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);
}
}