From dc76918300fb3247cb793e35430734753b12ec69 Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Wed, 25 May 2022 23:30:14 -0700 Subject: [PATCH] Fixed range requests and consolidated data download --- .../Paste/Controllers/PasteController.cs | 2 +- .../Podcast/Controllers/PodcastController.cs | 4 +- .../Upload/Controllers/UploadController.cs | 65 +++++++++---------- Utilities/Cryptography/AesCounterMode.cs | 16 +++-- Utilities/Cryptography/AesCounterStream.cs | 17 ++--- Utilities/ResponseHelper.cs | 13 ++-- 6 files changed, 53 insertions(+), 64 deletions(-) diff --git a/Teknik/Areas/Paste/Controllers/PasteController.cs b/Teknik/Areas/Paste/Controllers/PasteController.cs index 4df1f6a..b150b8f 100644 --- a/Teknik/Areas/Paste/Controllers/PasteController.cs +++ b/Teknik/Areas/Paste/Controllers/PasteController.cs @@ -161,7 +161,7 @@ namespace Teknik.Areas.Paste.Controllers Response.Headers.Add("Content-Disposition", cd.ToString()); - return new BufferedFileStreamResult("application/octet-stream", async (response) => await ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fileStream, false, keyBytes, ivBytes), contentSize, _config.PasteConfig.ChunkSize), false); + return new BufferedFileStreamResult("application/octet-stream", async (response) => await ResponseHelper.StreamToOutput(response, new AesCounterStream(fileStream, false, keyBytes, ivBytes), contentSize, _config.PasteConfig.ChunkSize), false); default: return View("~/Areas/Paste/Views/Paste/Full.cshtml", model); } diff --git a/Teknik/Areas/Podcast/Controllers/PodcastController.cs b/Teknik/Areas/Podcast/Controllers/PodcastController.cs index 701d5de..77e5b26 100644 --- a/Teknik/Areas/Podcast/Controllers/PodcastController.cs +++ b/Teknik/Areas/Podcast/Controllers/PodcastController.cs @@ -83,7 +83,7 @@ namespace Teknik.Areas.Podcast.Controllers [HttpGet] [AllowAnonymous] - [ResponseCache(Duration = 31536000, Location = ResponseCacheLocation.Any)] + [ResponseCache(Duration = 31536000, Location = ResponseCacheLocation.Any, NoStore = false)] [TrackDownload] public IActionResult Download(int episode, string fileName) { @@ -215,7 +215,7 @@ namespace Teknik.Areas.Podcast.Controllers // Reset file stream to starting position (or start of range) fileStream.Seek(startByte, SeekOrigin.Begin); - return new BufferedFileStreamResult(contentType, (response) => ResponseHelper.StreamToOutput(response, true, fileStream, (int)length, 4 * 1024), false); + return new BufferedFileStreamResult(contentType, (response) => ResponseHelper.StreamToOutput(response, fileStream, (int)length, 4 * 1024), false); } } return new StatusCodeResult(StatusCodes.Status404NotFound); diff --git a/Teknik/Areas/Upload/Controllers/UploadController.cs b/Teknik/Areas/Upload/Controllers/UploadController.cs index 2f0fc75..176104f 100644 --- a/Teknik/Areas/Upload/Controllers/UploadController.cs +++ b/Teknik/Areas/Upload/Controllers/UploadController.cs @@ -397,28 +397,7 @@ namespace Teknik.Areas.Upload.Controllers // Reset file stream to starting position (or start of range) fileStream.Seek(startByte, SeekOrigin.Begin); - try - { - // If the IV is set, and Key is set, then decrypt it while sending - if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv)) - { - byte[] keyBytes = Encoding.UTF8.GetBytes(key); - byte[] ivBytes = Encoding.UTF8.GetBytes(iv); - - var aesStream = new AesCounterStream(fileStream, false, keyBytes, ivBytes); - - return new BufferedFileStreamResult(contentType, async (response) => await ResponseHelper.StreamToOutput(response, true, aesStream, (int)length, _config.UploadConfig.ChunkSize), false); - } - else // Otherwise just send it - { - // Send the file - return new BufferedFileStreamResult(contentType, async (response) => await ResponseHelper.StreamToOutput(response, true, fileStream, (int)length, _config.UploadConfig.ChunkSize), false); - } - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Error in Download: {url}", new { url }); - } + return DownloadData(url, fileStream, contentType, (int)length, key, iv); } } return new StatusCodeResult(StatusCodes.Status404NotFound); @@ -462,19 +441,7 @@ namespace Teknik.Areas.Upload.Controllers Response.Headers.Add("Content-Disposition", cd.ToString()); - // If the IV is set, and Key is set, then decrypt it while sending - if (decrypt && !string.IsNullOrEmpty(upload.Key) && !string.IsNullOrEmpty(upload.IV)) - { - byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key); - byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV); - - return new BufferedFileStreamResult(upload.ContentType, (response) => ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fileStream, false, keyBytes, ivBytes), (int)upload.ContentLength, _config.UploadConfig.ChunkSize), false); - } - else // Otherwise just send it - { - // Send the file - return new BufferedFileStreamResult(upload.ContentType, (response) => ResponseHelper.StreamToOutput(response, true, fileStream, (int)upload.ContentLength, _config.UploadConfig.ChunkSize), false); - } + return DownloadData(upload.Url, fileStream, upload.ContentType, (int)upload.ContentLength, upload.Key, upload.IV); } } return Json(new { error = new { message = "File Does Not Exist" } }); @@ -540,5 +507,33 @@ namespace Teknik.Areas.Upload.Controllers } return Json(new { error = new { message = "This Upload does not exist" } }); } + + private IActionResult DownloadData(string url, Stream fileStream, string contentType, int length, string key, string iv) + { + try + { + // If the IV is set, and Key is set, then decrypt it while sending + if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv)) + { + byte[] keyBytes = Encoding.UTF8.GetBytes(key); + byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + + var aesStream = new AesCounterStream(fileStream, false, keyBytes, ivBytes); + //return File(aesStream, contentType, true); + return new BufferedFileStreamResult(contentType, async (response) => await ResponseHelper.StreamToOutput(response, aesStream, length, _config.UploadConfig.ChunkSize), false); + } + else // Otherwise just send it + { + // Send the file + return new BufferedFileStreamResult(contentType, async (response) => await ResponseHelper.StreamToOutput(response, fileStream, length, _config.UploadConfig.ChunkSize), false); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Error in Download: {url}", new { url }); + } + + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } } } diff --git a/Utilities/Cryptography/AesCounterMode.cs b/Utilities/Cryptography/AesCounterMode.cs index f26b92b..250a09d 100644 --- a/Utilities/Cryptography/AesCounterMode.cs +++ b/Utilities/Cryptography/AesCounterMode.cs @@ -54,10 +54,10 @@ namespace Teknik.Utilities.Cryptography } } - public class CounterModeCryptoTransform : ICryptoTransform + public class CounterModeCryptoTransform : ICryptoTransform, IDisposable { private readonly int _BlockSize; - private readonly Memory _IV; + private readonly byte[] _IV; private readonly byte[] _Counter; private readonly byte[] _EncryptedCounter; private readonly ICryptoTransform _CounterEncryptor; @@ -103,13 +103,17 @@ namespace Teknik.Utilities.Cryptography _BlockSize = symmetricAlgorithm.BlockSize; + // Initialize Counter + _Counter = new byte[initialCounter.Length]; + initialCounter.CopyTo(_Counter, 0); + // Initialize the encrypted counter _EncryptedCounter = new byte[_BlockSize / 8]; - _IV = iv; + // Initialize IV + _IV = new byte[iv.Length]; + iv.CopyTo(_IV, 0); - _Counter = initialCounter; - _CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); // Initialize State @@ -204,7 +208,7 @@ namespace Teknik.Utilities.Cryptography public void ResetCounter() { - _IV.CopyTo(_Counter); + _IV.CopyTo(_Counter, 0); _Iterations = 0; } diff --git a/Utilities/Cryptography/AesCounterStream.cs b/Utilities/Cryptography/AesCounterStream.cs index 0fd25ad..b0e7182 100644 --- a/Utilities/Cryptography/AesCounterStream.cs +++ b/Utilities/Cryptography/AesCounterStream.cs @@ -45,7 +45,7 @@ namespace Teknik.Utilities.Cryptography int processed = 0; // Read the data from the stream - int bytesRead = await _Inner.ReadAsync(buffer); + int bytesRead = await _Inner.ReadAsync(buffer).ConfigureAwait(false); if (bytesRead > 0) { // Process the read buffer @@ -128,21 +128,20 @@ namespace Teknik.Utilities.Cryptography if (_Inner != null && CanWrite) { // Process the cipher - Memory output = buffer; + Span output = buffer; // Process the buffer - int processed = _Cipher.TransformBlock(output.Span, offset, count); + int processed = _Cipher.TransformBlock(output, offset, count); // Do we have more? if (processed < count) { // Finalize the cipher - var finalProcessed = _Cipher.TransformFinalBlock(output.Span, processed + offset, count); + var finalProcessed = _Cipher.TransformFinalBlock(output, processed + offset, count); if (finalProcessed > 0) processed += finalProcessed; } - ReadOnlyMemory readOnlyOutput = buffer; - _Inner.Write(readOnlyOutput.Span); + _Inner.Write(output); } } @@ -255,12 +254,6 @@ namespace Teknik.Utilities.Cryptography base.Dispose(disposing); } - public override async ValueTask DisposeAsync() - { - await _Inner.DisposeAsync(); - await base.DisposeAsync(); - } - private void SyncCounter() { if (_Cipher != null) diff --git a/Utilities/ResponseHelper.cs b/Utilities/ResponseHelper.cs index 5136660..6338c3a 100644 --- a/Utilities/ResponseHelper.cs +++ b/Utilities/ResponseHelper.cs @@ -11,8 +11,9 @@ namespace Teknik.Utilities { public static class ResponseHelper { - public async static Task StreamToOutput(HttpResponse response, bool flush, Stream stream, int length, int chunkSize) + public async static Task StreamToOutput(HttpResponse response, Stream stream, int length, int chunkSize) { + response.RegisterForDisposeAsync(stream); var bufferSize = chunkSize; if (length < chunkSize) bufferSize = length; @@ -27,11 +28,7 @@ namespace Teknik.Utilities { await response.Body.WriteAsync(buffer.Slice(0, processedBytes)); - // Flush the response - if (flush) - { - //await response.Body.FlushAsync(); - } + await response.Body.FlushAsync(); } } while (processedBytes > 0); @@ -42,10 +39,10 @@ namespace Teknik.Utilities } finally { - //await response.Body.FlushAsync(); + await response.Body.FlushAsync(); // dispose of file stream - stream?.Dispose(); + //stream?.Dispose(); } } }