Пример #1
0
        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");
            }
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #4
0
 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);
     }
 }
Пример #5
0
        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);
        }
Пример #6
0
        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>());
        }
Пример #7
0
        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");
            }
        }
Пример #8
0
        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");
                }
            }
        }
Пример #9
0
        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;
            }
        }
Пример #10
0
        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;
            }
        }
Пример #11
0
        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));
        }
Пример #12
0
        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);
        }
Пример #13
0
        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>());
        }
Пример #14
0
        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);
        }
Пример #15
0
        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");
                }
            }
        }