mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
cipher service encrypt
This commit is contained in:
parent
4b67ba027e
commit
694e4960ad
3 changed files with 296 additions and 22 deletions
|
@ -74,28 +74,28 @@ namespace Bit.Core.Models.Domain
|
|||
public List<PasswordHistory> PasswordHistory { get; set; }
|
||||
public List<string> CollectionIds { get; set; }
|
||||
|
||||
public async Task<CipherView> DecryptAsync(string orgId)
|
||||
public async Task<CipherView> DecryptAsync()
|
||||
{
|
||||
var model = new CipherView(this);
|
||||
await DecryptObjAsync(model, this, new HashSet<string>
|
||||
{
|
||||
"Name",
|
||||
"Notes"
|
||||
}, orgId);
|
||||
}, OrganizationId);
|
||||
|
||||
switch(Type)
|
||||
{
|
||||
case Enums.CipherType.Login:
|
||||
model.Login = await Login.DecryptAsync(orgId);
|
||||
model.Login = await Login.DecryptAsync(OrganizationId);
|
||||
break;
|
||||
case Enums.CipherType.SecureNote:
|
||||
model.SecureNote = await SecureNote.DecryptAsync(orgId);
|
||||
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId);
|
||||
break;
|
||||
case Enums.CipherType.Card:
|
||||
model.Card = await Card.DecryptAsync(orgId);
|
||||
model.Card = await Card.DecryptAsync(OrganizationId);
|
||||
break;
|
||||
case Enums.CipherType.Identity:
|
||||
model.Identity = await Identity.DecryptAsync(orgId);
|
||||
model.Identity = await Identity.DecryptAsync(OrganizationId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -107,7 +107,7 @@ namespace Bit.Core.Models.Domain
|
|||
var tasks = new List<Task>();
|
||||
foreach(var attachment in Attachments)
|
||||
{
|
||||
var t = attachment.DecryptAsync(orgId)
|
||||
var t = attachment.DecryptAsync(OrganizationId)
|
||||
.ContinueWith(async decAttachment => model.Attachments.Add(await decAttachment));
|
||||
tasks.Add(t);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ namespace Bit.Core.Models.Domain
|
|||
var tasks = new List<Task>();
|
||||
foreach(var field in Fields)
|
||||
{
|
||||
var t = field.DecryptAsync(orgId)
|
||||
var t = field.DecryptAsync(OrganizationId)
|
||||
.ContinueWith(async decField => model.Fields.Add(await decField));
|
||||
tasks.Add(t);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ namespace Bit.Core.Models.Domain
|
|||
var tasks = new List<Task>();
|
||||
foreach(var ph in PasswordHistory)
|
||||
{
|
||||
var t = ph.DecryptAsync(orgId)
|
||||
var t = ph.DecryptAsync(OrganizationId)
|
||||
.ContinueWith(async decPh => model.PasswordHistory.Add(await decPh));
|
||||
tasks.Add(t);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@ namespace Bit.Core.Models.View
|
|||
}
|
||||
|
||||
public string Password { get; set; }
|
||||
public DateTime? LastUsedDate { get; set; }
|
||||
public DateTime LastUsedDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.View;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
|
@ -69,12 +69,78 @@ namespace Bit.Core.Services
|
|||
// Adjust password history
|
||||
if(model.Id != null)
|
||||
{
|
||||
// TODO
|
||||
if(originalCipher == null)
|
||||
{
|
||||
originalCipher = await GetAsync(model.Id);
|
||||
}
|
||||
if(originalCipher != null)
|
||||
{
|
||||
var existingCipher = await originalCipher.DecryptAsync();
|
||||
if(model.PasswordHistory == null)
|
||||
{
|
||||
model.PasswordHistory = new List<PasswordHistoryView>();
|
||||
}
|
||||
if(model.Type == CipherType.Login && existingCipher.Type == CipherType.Login)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(existingCipher.Login.Password) &&
|
||||
existingCipher.Login.Password != model.Login.Password)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var ph = new PasswordHistoryView
|
||||
{
|
||||
Password = existingCipher.Login.Password,
|
||||
LastUsedDate = now
|
||||
};
|
||||
model.Login.PasswordRevisionDate = now;
|
||||
model.PasswordHistory.Insert(0, ph);
|
||||
}
|
||||
else
|
||||
{
|
||||
model.Login.PasswordRevisionDate = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
if(existingCipher.HasFields)
|
||||
{
|
||||
var existingHiddenFields = existingCipher.Fields.Where(f =>
|
||||
f.Type == FieldType.Hidden && !string.IsNullOrWhiteSpace(f.Name) &&
|
||||
!string.IsNullOrWhiteSpace(f.Value));
|
||||
var hiddenFields = model.Fields?.Where(f =>
|
||||
f.Type == FieldType.Hidden && !string.IsNullOrWhiteSpace(f.Name)) ??
|
||||
new List<FieldView>();
|
||||
foreach(var ef in existingHiddenFields)
|
||||
{
|
||||
var matchedField = hiddenFields.FirstOrDefault(f => f.Name == ef.Name);
|
||||
if(matchedField == null || matchedField.Value != ef.Value)
|
||||
{
|
||||
var ph = new PasswordHistoryView
|
||||
{
|
||||
Password = string.Format("{0}: {1}", ef.Name, ef.Value),
|
||||
LastUsedDate = DateTime.UtcNow
|
||||
};
|
||||
model.PasswordHistory.Insert(0, ph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!model.PasswordHistory?.Any() ?? false)
|
||||
{
|
||||
model.PasswordHistory = null;
|
||||
}
|
||||
else if(model.PasswordHistory != null && model.PasswordHistory.Count > 5)
|
||||
{
|
||||
model.PasswordHistory = model.PasswordHistory.Take(5).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
var cipher = new Cipher();
|
||||
cipher.Id = model.Id;
|
||||
// TODO others
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = model.Id,
|
||||
FolderId = model.FolderId,
|
||||
Favorite = model.Favorite,
|
||||
OrganizationId = model.OrganizationId,
|
||||
Type = model.Type,
|
||||
CollectionIds = model.CollectionIds
|
||||
};
|
||||
|
||||
if(key == null && cipher.OrganizationId != null)
|
||||
{
|
||||
|
@ -85,16 +151,52 @@ namespace Bit.Core.Services
|
|||
}
|
||||
}
|
||||
|
||||
var tasks = new List<Task>();
|
||||
tasks.Add(EncryptObjPropertyAsync(model, cipher, new HashSet<string>
|
||||
var tasks = new List<Task>
|
||||
{
|
||||
nameof(model.Name)
|
||||
}, key));
|
||||
|
||||
EncryptObjPropertyAsync(model, cipher, new HashSet<string>
|
||||
{
|
||||
"Name",
|
||||
"Notes"
|
||||
}, key),
|
||||
EncryptCipherDataAsync(cipher, model, key),
|
||||
EncryptFieldsAsync(model.Fields, key)
|
||||
.ContinueWith(async fields => cipher.Fields = await fields),
|
||||
EncryptPasswordHistoriesAsync(model.PasswordHistory, key)
|
||||
.ContinueWith(async phs => cipher.PasswordHistory = await phs),
|
||||
EncryptAttachmentsAsync(model.Attachments, key)
|
||||
.ContinueWith(async attachments => cipher.Attachments = await attachments)
|
||||
};
|
||||
await Task.WhenAll(tasks);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
public async Task<Cipher> GetAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>(
|
||||
Keys_LocalData);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
|
||||
string.Format(Keys_CiphersFormat, userId));
|
||||
if(!ciphers?.ContainsKey(id) ?? true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new Cipher(ciphers[id], false,
|
||||
localData?.ContainsKey(id) ?? false ? localData[id] : null);
|
||||
}
|
||||
|
||||
public async Task<List<Cipher>> GetAllAsync()
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>(
|
||||
Keys_LocalData);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
|
||||
string.Format(Keys_CiphersFormat, userId));
|
||||
var response = ciphers.Select(c => new Cipher(c.Value, false,
|
||||
localData?.ContainsKey(c.Key) ?? false ? localData[c.Key] : null));
|
||||
return response.ToList();
|
||||
}
|
||||
|
||||
private Task EncryptObjPropertyAsync<V, D>(V model, D obj, HashSet<string> map, SymmetricCryptoKey key)
|
||||
where V : View
|
||||
where D : Domain
|
||||
|
@ -125,5 +227,177 @@ namespace Bit.Core.Services
|
|||
}
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private async Task<List<Attachment>> EncryptAttachmentsAsync(
|
||||
List<AttachmentView> attachmentsModel, SymmetricCryptoKey key)
|
||||
{
|
||||
if(!attachmentsModel?.Any() ?? true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tasks = new List<Task>();
|
||||
var encAttachments = new List<Attachment>();
|
||||
foreach(var model in attachmentsModel)
|
||||
{
|
||||
var attachment = new Attachment
|
||||
{
|
||||
Id = model.Id,
|
||||
Size = model.Size,
|
||||
SizeName = model.SizeName,
|
||||
Url = model.Url
|
||||
};
|
||||
var task = EncryptObjPropertyAsync(model, attachment, new HashSet<string>
|
||||
{
|
||||
"FileName"
|
||||
}, key).ContinueWith(async (t) =>
|
||||
{
|
||||
if(model.Key != null)
|
||||
{
|
||||
attachment.Key = await _cryptoService.EncryptAsync(model.Key.Key, key);
|
||||
}
|
||||
encAttachments.Add(attachment);
|
||||
});
|
||||
tasks.Add(task);
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
return encAttachments;
|
||||
}
|
||||
|
||||
private async Task EncryptCipherDataAsync(Cipher cipher, CipherView model, SymmetricCryptoKey key)
|
||||
{
|
||||
switch(cipher.Type)
|
||||
{
|
||||
case Enums.CipherType.Login:
|
||||
cipher.Login = new Login
|
||||
{
|
||||
PasswordRevisionDate = model.Login.PasswordRevisionDate
|
||||
};
|
||||
await EncryptObjPropertyAsync(model.Login, cipher.Login, new HashSet<string>
|
||||
{
|
||||
"Username",
|
||||
"Password",
|
||||
"Totp"
|
||||
}, key);
|
||||
if(model.Login.Uris != null)
|
||||
{
|
||||
cipher.Login.Uris = new List<LoginUri>();
|
||||
foreach(var uri in model.Login.Uris)
|
||||
{
|
||||
var loginUri = new LoginUri
|
||||
{
|
||||
Match = uri.Match
|
||||
};
|
||||
await EncryptObjPropertyAsync(uri, loginUri, new HashSet<string> { "Uri" }, key);
|
||||
cipher.Login.Uris.Add(loginUri);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Enums.CipherType.SecureNote:
|
||||
cipher.SecureNote = new SecureNote
|
||||
{
|
||||
Type = model.SecureNote.Type
|
||||
};
|
||||
break;
|
||||
case Enums.CipherType.Card:
|
||||
cipher.Card = new Card();
|
||||
await EncryptObjPropertyAsync(model.Card, cipher.Card, new HashSet<string>
|
||||
{
|
||||
"CardholderName",
|
||||
"Brand",
|
||||
"Number",
|
||||
"ExpMonth",
|
||||
"ExpYear",
|
||||
"Code"
|
||||
}, key);
|
||||
break;
|
||||
case Enums.CipherType.Identity:
|
||||
cipher.Identity = new Identity();
|
||||
await EncryptObjPropertyAsync(model.Identity, cipher.Identity, new HashSet<string>
|
||||
{
|
||||
"Title",
|
||||
"FirstName",
|
||||
"MiddleName",
|
||||
"LastName",
|
||||
"Address1",
|
||||
"Address2",
|
||||
"Address3",
|
||||
"City",
|
||||
"State",
|
||||
"PostalCode",
|
||||
"Country",
|
||||
"Company",
|
||||
"Email",
|
||||
"Phone",
|
||||
"SSN",
|
||||
"Username",
|
||||
"PassportNumber",
|
||||
"LicenseNumber"
|
||||
}, key);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown cipher type.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<Field>> EncryptFieldsAsync(List<FieldView> fieldsModel, SymmetricCryptoKey key)
|
||||
{
|
||||
if(!fieldsModel?.Any() ?? true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tasks = new List<Task>();
|
||||
var encFields = new List<Field>();
|
||||
foreach(var model in fieldsModel)
|
||||
{
|
||||
var field = new Field
|
||||
{
|
||||
Type = model.Type
|
||||
};
|
||||
// normalize boolean type field values
|
||||
if(model.Type == FieldType.Boolean && model.Value != "true")
|
||||
{
|
||||
model.Value = "false";
|
||||
}
|
||||
var task = EncryptObjPropertyAsync(model, field, new HashSet<string>
|
||||
{
|
||||
"Name",
|
||||
"Value"
|
||||
}, key).ContinueWith((t) =>
|
||||
{
|
||||
encFields.Add(field);
|
||||
});
|
||||
tasks.Add(task);
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
return encFields;
|
||||
}
|
||||
|
||||
private async Task<List<PasswordHistory>> EncryptPasswordHistoriesAsync(List<PasswordHistoryView> phModels,
|
||||
SymmetricCryptoKey key)
|
||||
{
|
||||
if(!phModels?.Any() ?? true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tasks = new List<Task>();
|
||||
var encPhs = new List<PasswordHistory>();
|
||||
foreach(var model in phModels)
|
||||
{
|
||||
var ph = new PasswordHistory
|
||||
{
|
||||
LastUsedDate = model.LastUsedDate
|
||||
};
|
||||
var task = EncryptObjPropertyAsync(model, ph, new HashSet<string>
|
||||
{
|
||||
"Password"
|
||||
}, key).ContinueWith((t) =>
|
||||
{
|
||||
encPhs.Add(ph);
|
||||
});
|
||||
tasks.Add(task);
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
return encPhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue