support attachment key, 100k iterations on regist.

This commit is contained in:
Kyle Spearrin 2018-11-19 22:24:10 -05:00
parent 36e263b9ff
commit a9d204d3fa
12 changed files with 44 additions and 21 deletions

View file

@ -6,7 +6,7 @@ namespace Bit.App.Abstractions
{ {
public interface ICipherApiRepository : IApiRepository<CipherRequest, CipherResponse, string> public interface ICipherApiRepository : IApiRepository<CipherRequest, CipherResponse, string>
{ {
Task<ApiResult<CipherResponse>> PostAttachmentAsync(string cipherId, byte[] data, string fileName); Task<ApiResult<CipherResponse>> PostAttachmentAsync(string cipherId, byte[] data, string key, string fileName);
Task<ApiResult> DeleteAttachmentAsync(string cipherId, string attachmentId); Task<ApiResult> DeleteAttachmentAsync(string cipherId, string attachmentId);
} }
} }

View file

@ -19,7 +19,7 @@ namespace Bit.App.Abstractions
Task UpsertDataAsync(CipherData cipher, bool sendMessage, bool created); Task UpsertDataAsync(CipherData cipher, bool sendMessage, bool created);
Task<ApiResult> DeleteAsync(string id); Task<ApiResult> DeleteAsync(string id);
Task DeleteDataAsync(string id, bool sendMessage); Task DeleteDataAsync(string id, bool sendMessage);
Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, string orgId = null); Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, CipherString key, string orgId = null);
Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Cipher cipher, byte[] data, string fileName); Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Cipher cipher, byte[] data, string fileName);
Task UpsertAttachmentDataAsync(IEnumerable<AttachmentData> attachments); Task UpsertAttachmentDataAsync(IEnumerable<AttachmentData> attachments);
Task<ApiResult> DeleteAttachmentAsync(Cipher cipher, string attachmentId); Task<ApiResult> DeleteAttachmentAsync(Cipher cipher, string attachmentId);

View file

@ -1,6 +1,7 @@
using Bit.App.Enums; using Bit.App.Enums;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Bit.App.Abstractions namespace Bit.App.Abstractions
@ -26,6 +27,6 @@ namespace Bit.App.Abstractions
SymmetricCryptoKey MakeKeyFromPassword(string password, string salt, KdfType kdf, int kdfIterations); SymmetricCryptoKey MakeKeyFromPassword(string password, string salt, KdfType kdf, int kdfIterations);
byte[] HashPassword(SymmetricCryptoKey key, string password); byte[] HashPassword(SymmetricCryptoKey key, string password);
string HashPasswordBase64(SymmetricCryptoKey key, string password); string HashPasswordBase64(SymmetricCryptoKey key, string password);
CipherString MakeEncKey(SymmetricCryptoKey key); Tuple<SymmetricCryptoKey, CipherString> MakeEncKey(SymmetricCryptoKey key);
} }
} }

View file

@ -5,6 +5,7 @@
public string Id { get; set; } public string Id { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string FileName { get; set; } public string FileName { get; set; }
public string Key { get; set; }
public string Size { get; set; } public string Size { get; set; }
public string SizeName { get; set; } public string SizeName { get; set; }
} }

View file

@ -13,6 +13,7 @@ namespace Bit.App.Models
Id = data.Id; Id = data.Id;
Url = data.Url; Url = data.Url;
FileName = data.FileName != null ? new CipherString(data.FileName) : null; FileName = data.FileName != null ? new CipherString(data.FileName) : null;
Key = data.Key != null ? new CipherString(data.Key) : null;
SetSize(data.Size); SetSize(data.Size);
SizeName = data.SizeName; SizeName = data.SizeName;
} }
@ -22,6 +23,7 @@ namespace Bit.App.Models
Id = response.Id; Id = response.Id;
Url = response.Url; Url = response.Url;
FileName = response.FileName != null ? new CipherString(response.FileName) : null; FileName = response.FileName != null ? new CipherString(response.FileName) : null;
Key = response.Key != null ? new CipherString(response.Key) : null;
SetSize(response.Size); SetSize(response.Size);
SizeName = response.SizeName; SizeName = response.SizeName;
} }
@ -29,6 +31,7 @@ namespace Bit.App.Models
public string Id { get; set; } public string Id { get; set; }
public string Url { get; set; } public string Url { get; set; }
public CipherString FileName { get; set; } public CipherString FileName { get; set; }
public CipherString Key { get; set; }
public long Size { get; set; } public long Size { get; set; }
public string SizeName { get; set; } public string SizeName { get; set; }

View file

@ -16,6 +16,7 @@ namespace Bit.App.Models.Data
LoginId = cipherId; LoginId = cipherId;
Url = attachment.Url; Url = attachment.Url;
FileName = attachment.FileName?.EncryptedString; FileName = attachment.FileName?.EncryptedString;
Key = attachment.Key?.EncryptedString;
Size = attachment.Size.ToString(); Size = attachment.Size.ToString();
SizeName = attachment.SizeName; SizeName = attachment.SizeName;
} }
@ -26,6 +27,7 @@ namespace Bit.App.Models.Data
LoginId = cipherId; LoginId = cipherId;
Url = response.Url; Url = response.Url;
FileName = response.FileName; FileName = response.FileName;
Key = response.Key;
Size = response.Size; Size = response.Size;
SizeName = response.SizeName; SizeName = response.SizeName;
} }
@ -37,6 +39,7 @@ namespace Bit.App.Models.Data
public string LoginId { get; set; } public string LoginId { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string FileName { get; set; } public string FileName { get; set; }
public string Key { get; set; }
public string Size { get; set; } public string Size { get; set; }
public string SizeName { get; set; } public string SizeName { get; set; }

View file

@ -579,6 +579,7 @@ namespace Bit.App.Models.Page
{ {
Id = attachment.Id, Id = attachment.Id,
Name = attachment.FileName?.Decrypt(cipher.OrganizationId), Name = attachment.FileName?.Decrypt(cipher.OrganizationId),
Key = attachment.Key,
SizeName = attachment.SizeName, SizeName = attachment.SizeName,
Size = attachment.Size, Size = attachment.Size,
Url = attachment.Url Url = attachment.Url
@ -670,6 +671,7 @@ namespace Bit.App.Models.Page
{ {
public string Id { get; set; } public string Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public CipherString Key { get; set; }
public string SizeName { get; set; } public string SizeName { get; set; }
public long Size { get; set; } public long Size { get; set; }
public string Url { get; set; } public string Url { get; set; }

View file

@ -193,7 +193,7 @@ namespace Bit.App.Pages
} }
var kdf = Enums.KdfType.PBKDF2_SHA256; var kdf = Enums.KdfType.PBKDF2_SHA256;
var kdfIterations = 5000; var kdfIterations = 100000;
var normalizedEmail = EmailCell.Entry.Text.ToLower().Trim(); var normalizedEmail = EmailCell.Entry.Text.ToLower().Trim();
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail, kdf, kdfIterations); var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail, kdf, kdfIterations);
var encKey = _cryptoService.MakeEncKey(key); var encKey = _cryptoService.MakeEncKey(key);
@ -203,7 +203,7 @@ namespace Bit.App.Pages
MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text), MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text),
MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text) MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text)
? PasswordHintCell.Entry.Text : null, ? PasswordHintCell.Entry.Text : null,
Key = encKey.EncryptedString, Key = encKey.Item2.EncryptedString,
Kdf = kdf, Kdf = kdf,
KdfIterations = kdfIterations KdfIterations = kdfIterations
}; };

View file

@ -501,7 +501,8 @@ namespace Bit.App.Pages
} }
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading); await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
var data = await _cipherService.DownloadAndDecryptAttachmentAsync(attachment.Url, cipher.OrganizationId); var data = await _cipherService.DownloadAndDecryptAttachmentAsync(attachment.Url, attachment.Key,
cipher.OrganizationId);
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
if(data == null) if(data == null)

View file

@ -21,7 +21,7 @@ namespace Bit.App.Repositories
protected override string ApiRoute => "/ciphers"; protected override string ApiRoute => "/ciphers";
public virtual async Task<ApiResult<CipherResponse>> PostAttachmentAsync(string cipherId, byte[] data, public virtual async Task<ApiResult<CipherResponse>> PostAttachmentAsync(string cipherId, byte[] data,
string fileName) string key, string fileName)
{ {
if(!Connectivity.IsConnected) if(!Connectivity.IsConnected)
{ {
@ -37,6 +37,7 @@ namespace Bit.App.Repositories
using(var client = HttpService.ApiClient) using(var client = HttpService.ApiClient)
using(var content = new MultipartFormDataContent("--BWMobileFormBoundary" + DateTime.UtcNow.Ticks)) using(var content = new MultipartFormDataContent("--BWMobileFormBoundary" + DateTime.UtcNow.Ticks))
{ {
content.Add(new StringContent(key), "key");
content.Add(new StreamContent(new MemoryStream(data)), "data", fileName); content.Add(new StreamContent(new MemoryStream(data)), "data", fileName);
var requestMessage = new TokenHttpRequestMessage var requestMessage = new TokenHttpRequestMessage

View file

@ -310,7 +310,7 @@ namespace Bit.App.Services
_appSettingsService.ClearCiphersCache = true; _appSettingsService.ClearCiphersCache = true;
} }
public async Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, string orgId = null) public async Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, CipherString key, string orgId = null)
{ {
using(var client = new HttpClient()) using(var client = new HttpClient())
{ {
@ -328,14 +328,20 @@ namespace Bit.App.Services
return null; return null;
} }
if(!string.IsNullOrWhiteSpace(orgId)) SymmetricCryptoKey regularKey = !string.IsNullOrWhiteSpace(orgId) ?
_cryptoService.GetOrgKey(orgId) : null;
SymmetricCryptoKey dataKey = null;
if(key != null)
{ {
return _cryptoService.DecryptToBytes(data, _cryptoService.GetOrgKey(orgId)); var decDataKey = _cryptoService.DecryptToBytes(key, regularKey);
dataKey = new SymmetricCryptoKey(decDataKey);
} }
else else
{ {
return _cryptoService.DecryptToBytes(data, null); dataKey = regularKey;
} }
return _cryptoService.DecryptToBytes(data, dataKey);
} }
catch catch
{ {
@ -346,10 +352,13 @@ namespace Bit.App.Services
public async Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Cipher cipher, byte[] data, string fileName) public async Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Cipher cipher, byte[] data, string fileName)
{ {
var key = cipher.OrganizationId != null ? _cryptoService.GetOrgKey(cipher.OrganizationId) : null;
var encFileName = fileName.Encrypt(cipher.OrganizationId); var encFileName = fileName.Encrypt(cipher.OrganizationId);
var encBytes = _cryptoService.EncryptToBytes(data,
cipher.OrganizationId != null ? _cryptoService.GetOrgKey(cipher.OrganizationId) : null); var dataKey = _cryptoService.MakeEncKey(key);
var response = await _cipherApiRepository.PostAttachmentAsync(cipher.Id, encBytes, encFileName.EncryptedString); var encBytes = _cryptoService.EncryptToBytes(data, dataKey.Item1);
var response = await _cipherApiRepository.PostAttachmentAsync(cipher.Id, encBytes,
dataKey.Item2.EncryptedString, encFileName.EncryptedString);
if(response.Succeeded) if(response.Succeeded)
{ {

View file

@ -483,18 +483,20 @@ namespace Bit.App.Services
return Convert.ToBase64String(hash); return Convert.ToBase64String(hash);
} }
public CipherString MakeEncKey(SymmetricCryptoKey key) public Tuple<SymmetricCryptoKey, CipherString> MakeEncKey(SymmetricCryptoKey key)
{ {
var theKey = key ?? EncKey ?? Key;
var encKey = Crypto.RandomBytes(64); var encKey = Crypto.RandomBytes(64);
// TODO: Remove hardcoded true/false when we're ready to enable key stretching if(theKey.Key.Length == 32)
if(false && key.Key.Length == 32)
{ {
var newKey = StretchKey(key); var newKey = StretchKey(theKey);
return Encrypt(encKey, newKey); return new Tuple<SymmetricCryptoKey, CipherString>(
new SymmetricCryptoKey(encKey), Encrypt(encKey, newKey));
} }
else if(true || key.Key.Length == 64) else if(theKey.Key.Length == 64)
{ {
return Encrypt(encKey, key); return new Tuple<SymmetricCryptoKey, CipherString>(
new SymmetricCryptoKey(encKey), Encrypt(encKey, theKey));
} }
throw new Exception("Invalid key size."); throw new Exception("Invalid key size.");