public async Task SaveWithServerAsync_NewTextSend_Success(SutProvider <SendService> sutProvider, string userId, SendResponse response, Send send) { send.Id = null; sutProvider.GetDependency <IStateService>().GetActiveUserIdAsync().Returns(userId); sutProvider.GetDependency <IApiService>().PostSendAsync(Arg.Any <SendRequest>()).Returns(response); var fileContentBytes = new EncByteArray(Encoding.UTF8.GetBytes("This is the file content")); await sutProvider.Sut.SaveWithServerAsync(send, fileContentBytes); Predicate <SendRequest> sendRequestPredicate = r => { // Note Send -> SendRequest tested in SendRequestTests TestHelper.AssertPropertyEqual(new SendRequest(send, fileContentBytes.Buffer?.LongLength), r); return(true); }; switch (send.Type) { case SendType.Text: await sutProvider.GetDependency <IApiService>().Received(1) .PostSendAsync(Arg.Is <SendRequest>(r => sendRequestPredicate(r))); break; case SendType.File: default: throw new Exception("Untested send type"); } }
public async Task <(Send send, EncByteArray encryptedFileData)> EncryptAsync(SendView model, byte[] fileData, string password, SymmetricCryptoKey key = null) { if (model.Key == null) { model.Key = _cryptoFunctionService.RandomBytes(16); model.CryptoKey = await _cryptoService.MakeSendKeyAsync(model.Key); } var send = new Send { Id = model.Id, Type = model.Type, Disabled = model.Disabled, DeletionDate = model.DeletionDate, ExpirationDate = model.ExpirationDate, MaxAccessCount = model.MaxAccessCount, Key = await _cryptoService.EncryptAsync(model.Key, key), Name = await _cryptoService.EncryptAsync(model.Name, model.CryptoKey), Notes = await _cryptoService.EncryptAsync(model.Notes, model.CryptoKey), HideEmail = model.HideEmail }; EncByteArray encryptedFileData = null; if (password != null) { var passwordHash = await _cryptoFunctionService.Pbkdf2Async(password, model.Key, CryptoHashAlgorithm.Sha256, 100000); send.Password = Convert.ToBase64String(passwordHash); } switch (send.Type) { case SendType.Text: send.Text = new SendText { Text = await _cryptoService.EncryptAsync(model.Text.Text, model.CryptoKey), Hidden = model.Text.Hidden }; break; case SendType.File: send.File = new SendFile(); if (fileData != null) { send.File.FileName = await _cryptoService.EncryptAsync(model.File.FileName, model.CryptoKey); encryptedFileData = await _cryptoService.EncryptToBytesAsync(fileData, model.CryptoKey); } break; default: break; } return(send, encryptedFileData); }
public async Task Upload(string encryptedFileName, EncByteArray encryptedFileData, Func <MultipartFormDataContent, Task> apiCall) { var fd = new MultipartFormDataContent($"--BWMobileFormBoundary{DateTime.UtcNow.Ticks}") { { new ByteArrayContent(encryptedFileData.Buffer), "data", encryptedFileName } }; await apiCall(fd); }
public async Task Upload(string uri, EncByteArray data, Func <Task <string> > renewalCallback) { if (data?.Buffer?.Length <= MAX_SINGLE_BLOB_UPLOAD_SIZE) { await AzureUploadBlob(uri, data); } else { await AzureUploadBlocks(uri, data, renewalCallback); } }
public async Task <string> SaveWithServerAsync(Send send, EncByteArray encryptedFileData) { var request = new SendRequest(send, encryptedFileData?.Buffer?.LongLength); SendResponse response = default; if (send.Id == null) { switch (send.Type) { case SendType.Text: response = await _apiService.PostSendAsync(request); break; case SendType.File: try { var uploadDataResponse = await _apiService.PostFileTypeSendAsync(request); response = uploadDataResponse.SendResponse; await _fileUploadService.UploadSendFileAsync(uploadDataResponse, send.File.FileName, encryptedFileData); } catch (ApiException e) when(e.Error.StatusCode == HttpStatusCode.NotFound) { response = await LegacyServerSendFileUpload(request, send, encryptedFileData); } catch { if (response != default) { await _apiService.DeleteSendAsync(response.Id); } throw; } break; default: throw new NotImplementedException($"Cannot save unknown Send type {send.Type}"); } send.Id = response.Id; } else { response = await _apiService.PutSendAsync(send.Id, request); } var userId = await _stateService.GetActiveUserIdAsync(); await UpsertAsync(new SendData(response, userId)); return(response.Id); }
public async Task SaveWithServerAsync_NewFileSend_LegacyFallback_Success(SutProvider <SendService> sutProvider, string userId, Send send, SendResponse response) { send.Id = null; sutProvider.GetDependency <IUserService>().GetUserIdAsync().Returns(userId); var error = new ErrorResponse(null, System.Net.HttpStatusCode.NotFound); sutProvider.GetDependency <IApiService>().PostFileTypeSendAsync(Arg.Any <SendRequest>()).Throws(new ApiException(error)); sutProvider.GetDependency <IApiService>().PostSendFileAsync(Arg.Any <MultipartFormDataContent>()).Returns(response); var fileContentBytes = new EncByteArray(Encoding.UTF8.GetBytes("This is the file content")); await sutProvider.Sut.SaveWithServerAsync(send, fileContentBytes); await sutProvider.GetDependency <IApiService>().Received(1).PostSendFileAsync(Arg.Any <MultipartFormDataContent>()); }
public async Task SaveWithServerAsync_NewFileSend_AzureUpload_Success(SutProvider <SendService> sutProvider, string userId, SendFileUploadDataResponse response, Send send) { send.Id = null; response.FileUploadType = FileUploadType.Azure; sutProvider.GetDependency <IUserService>().GetUserIdAsync().Returns(userId); sutProvider.GetDependency <IApiService>().PostFileTypeSendAsync(Arg.Any <SendRequest>()).Returns(response); var fileContentBytes = new EncByteArray(Encoding.UTF8.GetBytes("This is the file content")); await sutProvider.Sut.SaveWithServerAsync(send, fileContentBytes); switch (send.Type) { case SendType.File: await sutProvider.GetDependency <IFileUploadService>().Received(1).UploadSendFileAsync(response, send.File.FileName, fileContentBytes); break; case SendType.Text: default: throw new Exception("Untested send type"); } }
private async Task AzureUploadBlob(string uri, EncByteArray data) { using (var requestMessage = new HttpRequestMessage()) { var uriBuilder = new UriBuilder(uri); var paramValues = HttpUtility.ParseQueryString(uriBuilder.Query); requestMessage.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R")); requestMessage.Headers.Add("x-ms-version", paramValues["sv"]); requestMessage.Headers.Add("x-ms-blob-type", "BlockBlob"); requestMessage.Content = new ByteArrayContent(data.Buffer); requestMessage.Version = new Version(1, 0); requestMessage.Method = HttpMethod.Put; requestMessage.RequestUri = uriBuilder.Uri; var blobResponse = await _httpClient.SendAsync(requestMessage); if (blobResponse.StatusCode != HttpStatusCode.Created) { throw new Exception("Failed to create Azure blob"); } } }
public async Task UploadSendFileAsync(SendFileUploadDataResponse uploadData, EncString fileName, EncByteArray encryptedFileData) { try { switch (uploadData.FileUploadType) { case FileUploadType.Direct: await _bitwardenFileUploadService.Upload(fileName.EncryptedString, encryptedFileData, fd => _apiService.PostSendFileAsync(uploadData.SendResponse.Id, uploadData.SendResponse.File.Id, fd)); break; case FileUploadType.Azure: Func <Task <string> > renewalCallback = async() => { var response = await _apiService.RenewFileUploadUrlAsync(uploadData.SendResponse.Id, uploadData.SendResponse.File.Id); return(response.Url); }; await _azureFileUploadService.Upload(uploadData.Url, encryptedFileData, renewalCallback); break; default: throw new Exception("Unknown file upload type"); } } catch (Exception) { await _apiService.DeleteSendAsync(uploadData.SendResponse.Id); throw; } }
public async Task UploadCipherAttachmentFileAsync(AttachmentUploadDataResponse uploadData, EncString encryptedFileName, EncByteArray encryptedFileData) { try { switch (uploadData.FileUploadType) { case FileUploadType.Direct: await _bitwardenFileUploadService.Upload(encryptedFileName.EncryptedString, encryptedFileData, fd => _apiService.PostAttachmentFileAsync(uploadData.CipherResponse.Id, uploadData.AttachmentId, fd)); break; case FileUploadType.Azure: Func <Task <string> > renewalCallback = async() => { var response = await _apiService.RenewAttachmentUploadUrlAsync(uploadData.CipherResponse.Id, uploadData.AttachmentId); return(response.Url); }; await _azureFileUploadService.Upload(uploadData.Url, encryptedFileData, renewalCallback); break; default: throw new Exception($"Unkown file upload type: {uploadData.FileUploadType}"); } } catch { await _apiService.DeleteCipherAttachmentAsync(uploadData.CipherResponse.Id, uploadData.AttachmentId); throw; } }
private async Task <SendResponse> LegacyServerSendFileUpload(SendRequest request, Send send, EncByteArray encryptedFileData) { var fd = new MultipartFormDataContent($"--BWMobileFormBoundary{DateTime.UtcNow.Ticks}") { { new StringContent(JsonConvert.SerializeObject(request)), "model" }, { new ByteArrayContent(encryptedFileData.Buffer), "data", send.File.FileName.EncryptedString } }; return(await _apiService.PostSendFileAsync(fd)); }
public async Task SaveWithServerAsync_ThrowsOnBadRequestApiException(SutProvider <CipherService> sutProvider, Cipher cipher, string fileName, EncByteArray data, EncString encKey) { sutProvider.GetDependency <ICryptoService>().EncryptAsync(fileName, Arg.Any <SymmetricCryptoKey>()) .Returns(new EncString(fileName)); sutProvider.GetDependency <ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any <SymmetricCryptoKey>()) .Returns(data); sutProvider.GetDependency <ICryptoService>().MakeEncKeyAsync(Arg.Any <SymmetricCryptoKey>()) .Returns(new Tuple <SymmetricCryptoKey, EncString>(null, encKey)); var expectedException = new ApiException(new ErrorResponse { StatusCode = HttpStatusCode.BadRequest }); sutProvider.GetDependency <IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any <AttachmentRequest>()) .Throws(expectedException); var actualException = await Assert.ThrowsAsync <ApiException>(async() => await sutProvider.Sut.SaveAttachmentRawWithServerAsync(cipher, fileName, data.Buffer)); Assert.Equal(expectedException.Error.StatusCode, actualException.Error.StatusCode); }
public async Task SaveWithServerAsync_FallsBackToLegacyFormData(HttpStatusCode statusCode, SutProvider <CipherService> sutProvider, Cipher cipher, string fileName, EncByteArray data, CipherResponse response, EncString encKey) { sutProvider.GetDependency <ICryptoService>().EncryptAsync(fileName, Arg.Any <SymmetricCryptoKey>()) .Returns(new EncString(fileName)); sutProvider.GetDependency <ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any <SymmetricCryptoKey>()) .Returns(data); sutProvider.GetDependency <ICryptoService>().MakeEncKeyAsync(Arg.Any <SymmetricCryptoKey>()).Returns(new Tuple <SymmetricCryptoKey, EncString>(null, encKey)); sutProvider.GetDependency <IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any <AttachmentRequest>()) .Throws(new ApiException(new ErrorResponse { StatusCode = statusCode })); sutProvider.GetDependency <IApiService>().PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any <MultipartFormDataContent>()) .Returns(response); await sutProvider.Sut.SaveAttachmentRawWithServerAsync(cipher, fileName, data.Buffer); await sutProvider.GetDependency <IApiService>().Received(1) .PostCipherAttachmentLegacyAsync(cipher.Id, Arg.Any <MultipartFormDataContent>()); }
public async Task SaveWithServerAsync_PrefersFileUploadService(SutProvider <CipherService> sutProvider, Cipher cipher, string fileName, EncByteArray data, AttachmentUploadDataResponse uploadDataResponse, EncString encKey) { var encFileName = new EncString(fileName); sutProvider.GetDependency <ICryptoService>().EncryptAsync(fileName, Arg.Any <SymmetricCryptoKey>()) .Returns(encFileName); sutProvider.GetDependency <ICryptoService>().EncryptToBytesAsync(data.Buffer, Arg.Any <SymmetricCryptoKey>()) .Returns(data); sutProvider.GetDependency <ICryptoService>().MakeEncKeyAsync(Arg.Any <SymmetricCryptoKey>()).Returns(new Tuple <SymmetricCryptoKey, EncString>(null, encKey)); sutProvider.GetDependency <IApiService>().PostCipherAttachmentAsync(cipher.Id, Arg.Any <AttachmentRequest>()) .Returns(uploadDataResponse); await sutProvider.Sut.SaveAttachmentRawWithServerAsync(cipher, fileName, data.Buffer); await sutProvider.GetDependency <IFileUploadService>().Received(1) .UploadCipherAttachmentFileAsync(uploadDataResponse, encFileName, data); }
private async Task AzureUploadBlocks(string uri, EncByteArray data, Func <Task <string> > renewalFunc) { _httpClient.Timeout = TimeSpan.FromHours(3); var baseParams = HttpUtility.ParseQueryString(CoreHelpers.GetUri(uri).Query); var blockSize = MaxBlockSize(baseParams["sv"]); var blockIndex = 0; var numBlocks = Math.Ceiling((decimal)data.Buffer.Length / blockSize); var blocksStaged = new List <string>(); if (numBlocks > MAX_BLOCKS_PER_BLOB) { throw new Exception($"Cannot upload file, exceeds maximum size of {blockSize * MAX_BLOCKS_PER_BLOB}"); } while (blockIndex < numBlocks) { uri = await RenewUriIfNecessary(uri, renewalFunc); var blockUriBuilder = new UriBuilder(uri); var blockId = EncodeBlockId(blockIndex); var blockParams = HttpUtility.ParseQueryString(blockUriBuilder.Query); blockParams.Add("comp", "block"); blockParams.Add("blockid", blockId); blockUriBuilder.Query = blockParams.ToString(); using (var requestMessage = new HttpRequestMessage()) { requestMessage.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R")); requestMessage.Headers.Add("x-ms-version", baseParams["sv"]); requestMessage.Headers.Add("x-ms-blob-type", "BlockBlob"); requestMessage.Content = new ByteArrayContent(data.Buffer.Skip(blockIndex * blockSize).Take(blockSize).ToArray()); requestMessage.Version = new Version(1, 0); requestMessage.Method = HttpMethod.Put; requestMessage.RequestUri = blockUriBuilder.Uri; var blockResponse = await _httpClient.SendAsync(requestMessage); if (blockResponse.StatusCode != HttpStatusCode.Created) { throw new Exception("Failed to create Azure block"); } } blocksStaged.Add(blockId); blockIndex++; } using (var requestMessage = new HttpRequestMessage()) { uri = await RenewUriIfNecessary(uri, renewalFunc); var blockListXml = GenerateBlockListXml(blocksStaged); var blockListUriBuilder = new UriBuilder(uri); var blockListParams = HttpUtility.ParseQueryString(blockListUriBuilder.Query); blockListParams.Add("comp", "blocklist"); blockListUriBuilder.Query = blockListParams.ToString(); requestMessage.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R")); requestMessage.Headers.Add("x-ms-version", baseParams["sv"]); requestMessage.Content = new StringContent(blockListXml); requestMessage.Version = new Version(1, 0); requestMessage.Method = HttpMethod.Put; requestMessage.RequestUri = blockListUriBuilder.Uri; var blockListResponse = await _httpClient.SendAsync(requestMessage); if (blockListResponse.StatusCode != HttpStatusCode.Created) { throw new Exception("Failed to PUT Azure block list"); } } }