1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-10 05:13:31 +00:00

[PM-2713] convert cipher service and others to crypto service api

This commit is contained in:
Jacob Fink
2023-07-19 08:59:56 -04:00
parent 7fdc5597fc
commit 546bf8dcb1
6 changed files with 67 additions and 43 deletions

View File

@@ -177,25 +177,28 @@ namespace Bit.App.Pages
Name = string.IsNullOrWhiteSpace(Name) ? null : Name; Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
Email = Email.Trim().ToLower(); Email = Email.Trim().ToLower();
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null); var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
var encKey = await _cryptoService.MakeEncKeyAsync(masterKey); var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey); newMasterKey,
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1); await _cryptoService.MakeUserKeyAsync()
);
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new RegisterRequest var request = new RegisterRequest
{ {
Email = Email, Email = Email,
Name = Name, Name = Name,
MasterPasswordHash = hashedPassword, MasterPasswordHash = hashedPassword,
MasterPasswordHint = Hint, MasterPasswordHint = Hint,
Key = encKey.Item2.EncryptedString, Key = newProtectedUserKey.EncryptedString,
Kdf = kdfConfig.Type, Kdf = kdfConfig.Type,
KdfIterations = kdfConfig.Iterations, KdfIterations = kdfConfig.Iterations,
KdfMemory = kdfConfig.Memory, KdfMemory = kdfConfig.Memory,
KdfParallelism = kdfConfig.Parallelism, KdfParallelism = kdfConfig.Parallelism,
Keys = new KeysRequest Keys = new KeysRequest
{ {
PublicKey = keys.Item1, PublicKey = newPublicKey,
EncryptedPrivateKey = keys.Item2.EncryptedString EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
}, },
CaptchaResponse = _captchaToken, CaptchaResponse = _captchaToken,
}; };

View File

@@ -74,7 +74,7 @@ namespace Bit.App.Pages
_cipherDomain = await _cipherService.GetAsync(CipherId); _cipherDomain = await _cipherService.GetAsync(CipherId);
Cipher = await _cipherDomain.DecryptAsync(); Cipher = await _cipherDomain.DecryptAsync();
LoadAttachments(); LoadAttachments();
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync(); _hasUpdatedKey = await _cryptoService.HasUserKeyAsync();
var canAccessPremium = await _stateService.CanAccessPremiumAsync(); var canAccessPremium = await _stateService.CanAccessPremiumAsync();
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null; _canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
if (!_canAccessAttachments) if (!_canAccessAttachments)

View File

@@ -12,6 +12,7 @@ namespace Bit.Core.Abstractions
Task ToggleKeysAsync(); Task ToggleKeysAsync();
Task SetUserKeyAsync(UserKey userKey, string userId = null); Task SetUserKeyAsync(UserKey userKey, string userId = null);
Task<UserKey> GetUserKeyAsync(string userId = null); Task<UserKey> GetUserKeyAsync(string userId = null);
Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null);
Task<bool> HasUserKeyAsync(string userId = null); Task<bool> HasUserKeyAsync(string userId = null);
Task<bool> HasEncryptedUserKeyAsync(string userId = null); Task<bool> HasEncryptedUserKeyAsync(string userId = null);
Task<UserKey> MakeUserKeyAsync(); Task<UserKey> MakeUserKeyAsync();
@@ -23,6 +24,8 @@ namespace Bit.Core.Abstractions
Task ClearMasterKeyAsync(string userId = null); Task ClearMasterKeyAsync(string userId = null);
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null); Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null);
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(UserKey key);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(OrgKey key);
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
Task SetPasswordHashAsync(string keyHash); Task SetPasswordHashAsync(string keyHash);
Task<string> GetPasswordHashAsync(); Task<string> GetPasswordHashAsync();
@@ -70,11 +73,9 @@ namespace Bit.Core.Abstractions
void ClearCache(); void ClearCache();
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null); Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null); Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
Task<bool> HasEncKeyAsync();
Task<bool> HasKeyAsync(string userId = null); Task<bool> HasKeyAsync(string userId = null);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key); Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
// TODO(Jake): This isn't used, delete // TODO(Jake): This isn't used, delete
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
Task SetEncKeyAsync(string encKey); Task SetEncKeyAsync(string encKey);
Task SetKeyAsync(SymmetricCryptoKey key); Task SetKeyAsync(SymmetricCryptoKey key);
} }

View File

@@ -557,9 +557,20 @@ namespace Bit.Core.Services
public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data) public async Task<Cipher> SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)
{ {
SymmetricCryptoKey attachmentKey;
EncString protectedAttachmentKey;
var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId); var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId);
if (orgKey != null)
{
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
}
else
{
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
}
var encFileName = await _cryptoService.EncryptAsync(filename, orgKey); var encFileName = await _cryptoService.EncryptAsync(filename, orgKey);
var (attachmentKey, orgEncAttachmentKey) = await _cryptoService.MakeEncKeyAsync(orgKey);
var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey); var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey);
CipherResponse response; CipherResponse response;
@@ -567,7 +578,7 @@ namespace Bit.Core.Services
{ {
var request = new AttachmentRequest var request = new AttachmentRequest
{ {
Key = orgEncAttachmentKey.EncryptedString, Key = protectedAttachmentKey.EncryptedString,
FileName = encFileName.EncryptedString, FileName = encFileName.EncryptedString,
FileSize = encFileData.Buffer.Length, FileSize = encFileData.Buffer.Length,
}; };
@@ -578,7 +589,7 @@ namespace Bit.Core.Services
} }
catch (ApiException e) when (e.Error.StatusCode == System.Net.HttpStatusCode.NotFound || e.Error.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed) catch (ApiException e) when (e.Error.StatusCode == System.Net.HttpStatusCode.NotFound || e.Error.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
{ {
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, orgEncAttachmentKey); response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, protectedAttachmentKey);
} }
var userId = await _stateService.GetActiveUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
@@ -807,14 +818,27 @@ namespace Bit.Core.Services
var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync(); var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync();
var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null); var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null);
var key = await _cryptoService.GetOrgKeyAsync(organizationId);
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, key); SymmetricCryptoKey attachmentKey;
var dataEncKey = await _cryptoService.MakeEncKeyAsync(key); EncString protectedAttachmentKey;
var encData = await _cryptoService.EncryptToBytesAsync(decBytes, dataEncKey.Item1); var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId);
if (orgKey != null)
{
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey);
}
else
{
var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync();
(attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey);
}
var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, orgKey);
var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey);
var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks); var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks);
var fd = new MultipartFormDataContent(boundary); var fd = new MultipartFormDataContent(boundary);
fd.Add(new StringContent(dataEncKey.Item2.EncryptedString), "key"); fd.Add(new StringContent(protectedAttachmentKey.EncryptedString), "key");
fd.Add(new StreamContent(new MemoryStream(encData.Buffer)), "data", encFileName.EncryptedString); fd.Add(new StreamContent(new MemoryStream(encFileData.Buffer)), "data", encFileName.EncryptedString);
await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId); await _apiService.PostShareCipherAttachmentAsync(cipherId, attachmentView.Id, fd, organizationId);
} }

View File

@@ -68,6 +68,19 @@ namespace Bit.Core.Services
return await _stateService.GetUserKeyAsync(userId); return await _stateService.GetUserKeyAsync(userId);
} }
public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null)
{
var userKey = await GetUserKeyAsync();
if (userKey != null)
{
return userKey;
}
// Legacy support: encryption used to be done with the master key (derived from master password).
// Users who have not migrated will have a null user key and must use the master key instead.
return (SymmetricCryptoKey)await GetMasterKeyAsync() as UserKey;
}
public async Task<bool> HasUserKeyAsync(string userId = null) public async Task<bool> HasUserKeyAsync(string userId = null)
{ {
return await GetUserKeyAsync(userId) != null; return await GetUserKeyAsync(userId) != null;
@@ -176,22 +189,22 @@ namespace Bit.Core.Services
return new UserKey(decUserKey); return new UserKey(decUserKey);
} }
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKey(UserKey key) public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(UserKey key)
{ {
if (key == null) if (key == null)
{ {
throw new Exception("No key provided"); throw new Exception("No user key provided");
} }
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64);
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey); return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey);
} }
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKey(OrgKey key) public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(OrgKey key)
{ {
if (key == null) if (key == null)
{ {
throw new Exception("No key provided"); throw new Exception("No org key provided");
} }
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64);
@@ -1152,12 +1165,6 @@ namespace Bit.Core.Services
return key != null; return key != null;
} }
public async Task<bool> HasEncKeyAsync()
{
var encKey = await _stateService.GetEncKeyEncryptedAsync();
return encKey != null;
}
public async Task ClearKeyAsync(string userId = null) public async Task ClearKeyAsync(string userId = null)
{ {
await _stateService.SetKeyDecryptedAsync(null, userId); await _stateService.SetKeyDecryptedAsync(null, userId);
@@ -1193,17 +1200,6 @@ namespace Bit.Core.Services
} }
// TODO(Jake): This isn't used, delete
public async Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync()
{
var shareKey = await _cryptoFunctionService.RandomBytesAsync(64);
var publicKey = await GetPublicKeyAsync();
var encShareKey = await RsaEncryptAsync(shareKey, publicKey);
return new Tuple<EncString, SymmetricCryptoKey>(encShareKey, new SymmetricCryptoKey(shareKey));
}
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key) public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key)

View File

@@ -29,7 +29,7 @@ namespace Bit.Core.Test.Services
.Returns(encFileName); .Returns(encFileName);
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>()) sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
.Returns(data); .Returns(data);
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey)); sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>()) sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
.Returns(uploadDataResponse); .Returns(uploadDataResponse);
@@ -50,7 +50,7 @@ namespace Bit.Core.Test.Services
.Returns(new EncString(fileName)); .Returns(new EncString(fileName));
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>()) sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
.Returns(data); .Returns(data);
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey)); sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>()).Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>()) sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())
.Throws(new ApiException(new ErrorResponse { StatusCode = statusCode })); .Throws(new ApiException(new ErrorResponse { StatusCode = statusCode }));
sutProvider.GetDependency<IApiService>().PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any<MultipartFormDataContent>()) sutProvider.GetDependency<IApiService>().PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any<MultipartFormDataContent>())
@@ -70,7 +70,7 @@ namespace Bit.Core.Test.Services
.Returns(new EncString(fileName)); .Returns(new EncString(fileName));
sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>()) sutProvider.GetDependency<ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any<SymmetricCryptoKey>())
.Returns(data); .Returns(data);
sutProvider.GetDependency<ICryptoService>().MakeEncKeyAsync(Arg.Any<SymmetricCryptoKey>()) sutProvider.GetDependency<ICryptoService>().MakeDataEncKeyAsync(Arg.Any<UserKey>())
.Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey)); .Returns(new Tuple<SymmetricCryptoKey, EncString>(null, encKey));
var expectedException = new ApiException(new ErrorResponse { StatusCode = HttpStatusCode.BadRequest }); var expectedException = new ApiException(new ErrorResponse { StatusCode = HttpStatusCode.BadRequest });
sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>()) sutProvider.GetDependency<IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any<AttachmentRequest>())