mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
support attachment key, 100k iterations on regist.
This commit is contained in:
parent
36e263b9ff
commit
a9d204d3fa
12 changed files with 44 additions and 21 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.");
|
||||||
|
|
Loading…
Reference in a new issue