diff --git a/ServerMaint/Program.cs b/ServerMaint/Program.cs index ca4cdbd..d43b232 100644 --- a/ServerMaint/Program.cs +++ b/ServerMaint/Program.cs @@ -168,7 +168,7 @@ namespace ServerMaint byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key); byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV); FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); - AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"); + AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding", 0); // We have the data, let's scan it ClamScanResult scanResult = clam.SendAndScanFile(aesStream); diff --git a/Teknik.sln b/Teknik.sln index 37e5d56..3e2c91e 100644 --- a/Teknik.sln +++ b/Teknik.sln @@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Configuration", "Utilities\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeknikStreaming", "TeknikStreaming\TeknikStreaming.csproj", "{7695CE9A-A0DB-4D73-BC9B-2206481F0254}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Security", "Utilities\Security\Security.csproj", "{2BB2F552-B80F-41F2-937D-5D73F15C6DC4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,6 +93,14 @@ Global {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|Any CPU.Build.0 = Release|Any CPU {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.ActiveCfg = Release|Any CPU {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.Build.0 = Release|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|x64.ActiveCfg = Debug|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|x64.Build.0 = Debug|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|Any CPU.Build.0 = Release|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|x64.ActiveCfg = Release|Any CPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Teknik/Areas/Upload/Controllers/UploadController.cs b/Teknik/Areas/Upload/Controllers/UploadController.cs index 741a3da..0982ae8 100644 --- a/Teknik/Areas/Upload/Controllers/UploadController.cs +++ b/Teknik/Areas/Upload/Controllers/UploadController.cs @@ -292,9 +292,12 @@ namespace Teknik.Areas.Upload.Controllers byte[] keyBytes = Encoding.UTF8.GetBytes(key); byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + // Calculate the block offset needed for the counter + int counterOffset = (int)Math.Floor(startByte / 16.0); + return new FileGenerateResult(url, contentType, - (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"), (int)length, Config.UploadConfig.ChunkSize), + (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding", counterOffset), (int)length, Config.UploadConfig.ChunkSize), false); } else // Otherwise just send it diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj index 522a97d..232f7ac 100644 --- a/Teknik/Teknik.csproj +++ b/Teknik/Teknik.csproj @@ -802,6 +802,10 @@ {c492c2c6-d45a-498b-84a2-6d4c8bf9de77} Piwik + + {2bb2f552-b80f-41f2-937d-5d73f15c6dc4} + Security + {f45de6fc-3754-4954-a20a-4277362cc6c1} Utilities diff --git a/Utilities/Security/Cryptography/AesCounterMode.cs b/Utilities/Security/Cryptography/AesCounterMode.cs new file mode 100644 index 0000000..c9ffd51 --- /dev/null +++ b/Utilities/Security/Cryptography/AesCounterMode.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Teknik.Security.Cryptography +{ + public class AesCounterMode : SymmetricAlgorithm + { + private const int _blockSize = 16; + private readonly byte[] _counter; + private readonly AesManaged _aes; + + public AesCounterMode(byte[] counter) + { + if (counter == null) throw new ArgumentNullException("counter"); + if (counter.Length != _blockSize) + throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", + counter.Length, _blockSize)); + + _aes = new AesManaged + { + Mode = CipherMode.ECB, + Padding = PaddingMode.None + }; + + _counter = counter; + } + + public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) + { + return new CounterModeCryptoTransform(_aes, key, iv, _counter); + } + + public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) + { + return new CounterModeCryptoTransform(_aes, key, iv, _counter); + } + + public override void GenerateKey() + { + _aes.GenerateKey(); + } + + public override void GenerateIV() + { + // IV not needed in Counter Mode + } + } + + public class CounterModeCryptoTransform : ICryptoTransform + { + private readonly byte[] _iv; + private readonly byte[] _counter; + private readonly ICryptoTransform _counterEncryptor; + private readonly Queue _xorMask = new Queue(); + private readonly SymmetricAlgorithm _symmetricAlgorithm; + + public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter) + { + if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm"); + if (key == null) throw new ArgumentNullException("key"); + if (iv == null) throw new ArgumentNullException("counter"); + if (iv.Length != symmetricAlgorithm.BlockSize / 8) + throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", + iv.Length, symmetricAlgorithm.BlockSize / 8)); + + _symmetricAlgorithm = symmetricAlgorithm; + _counter = counter; + _iv = iv; + + _counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); + } + + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + var output = new byte[inputCount]; + TransformBlock(inputBuffer, inputOffset, inputCount, output, 0); + return output; + } + + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + for (var i = 0; i < inputCount; i++) + { + if (NeedMoreXorMaskBytes()) EncryptCounterThenIncrement(); + + var mask = _xorMask.Dequeue(); + outputBuffer[outputOffset + i] = (byte)(mask ^ inputBuffer[inputOffset + i]); + } + + return inputCount; + } + + private bool NeedMoreXorMaskBytes() + { + return _xorMask.Count == 0; + } + + private void EncryptCounterThenIncrement() + { + var counterModeBlock = new byte[_symmetricAlgorithm.BlockSize / 8]; + + _counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0); + IncrementCounter(); + + foreach (var b in counterModeBlock) + { + _xorMask.Enqueue(b); + } + } + + private void IncrementCounter() + { + int j = _counter.Length; + while (--j >= 0 && ++_counter[j] == 0) + { + } + //for (var i = _counter.Length - 1; i >= 0; i--) + //{ + // if (++_counter[i] != 0) + // break; + //} + } + + public int InputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } } + public int OutputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } } + public bool CanTransformMultipleBlocks { get { return true; } } + public bool CanReuseTransform { get { return false; } } + + public void Dispose() + { + } + } +} diff --git a/Utilities/Security/Properties/AssemblyInfo.cs b/Utilities/Security/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8d9c36f --- /dev/null +++ b/Utilities/Security/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Teknik Security Utilities")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Teknik")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2bb2f552-b80f-41f2-937d-5d73f15c6dc4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Utilities/Security/Security.csproj b/Utilities/Security/Security.csproj new file mode 100644 index 0000000..53c6c51 --- /dev/null +++ b/Utilities/Security/Security.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {2BB2F552-B80F-41F2-937D-5D73F15C6DC4} + Library + Properties + Teknik.Security + Teknik.Security + v4.6.2 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Utilities/Utilities/StreamHelper.cs b/Utilities/Utilities/StreamHelper.cs index bfb69ca..7609a75 100644 --- a/Utilities/Utilities/StreamHelper.cs +++ b/Utilities/Utilities/StreamHelper.cs @@ -1,22 +1,45 @@ -using Org.BouncyCastle.Crypto; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; +using Teknik.Security.Cryptography; namespace Teknik.Utilities { public class AESCryptoStream : Stream { private Stream _Inner; - private IBufferedCipher _Cipher; + //private IBufferedCipher _Cipher; + private ICryptoTransform _Cipher; - public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding) + public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding, int initCounter) { _Inner = stream; - _Cipher = AES.CreateCipher(encrypt, key, iv, mode, padding); + + // Create initial counter value from IV + byte[] counter = new byte[iv.Length]; + iv.CopyTo(counter, 0); + + // Increment the counter depending on the init counter + for (int i = 0; i < initCounter; i++) + { + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) { } + } + + // Create the Aes Cipher + AesCounterMode aes = new AesCounterMode(counter); + if (encrypt) + { + _Cipher = aes.CreateEncryptor(key, iv); // Encrypt + } + else + { + _Cipher = aes.CreateDecryptor(key, iv); // Decrypt + } } public override int Read(byte[] buffer, int offset, int count) @@ -24,30 +47,63 @@ namespace Teknik.Utilities if (_Inner != null && CanRead) { int bytesRead = 0; + int totalBytesRead = 0; + int bytesProcessed = 0; long startPosition = _Inner.Position; - int blockSize = _Cipher.GetBlockSize(); - long blockOffset = (startPosition % blockSize); + int blockSize = _Cipher.InputBlockSize; + long byteOffset = (startPosition % blockSize); + int initialOffset = (int)byteOffset; + int blockOffset = (byteOffset == 0) ? 0 : 1; + int blocksToProcess = (int)Math.Ceiling(count / (double)blockSize) + blockOffset; // Determine if we are at the start of a block, or not - if (blockOffset != 0) + if (byteOffset != 0) { // We are not a multiple of the block size, so let's backup to get the current block - _Inner.Seek(startPosition - blockOffset, SeekOrigin.Begin); + _Inner.Seek(startPosition - byteOffset, SeekOrigin.Begin); } - // Process the cipher - int processed = AES.ProcessCipherBlock(_Cipher, _Inner, 0, count + (int)blockOffset, buffer, offset, out bytesRead); + // Initialize buffers + byte[] readBuf = new byte[blockSize]; + byte[] outBuf = new byte[blockSize]; + + // Iterate through each block of the ammount we want to read + for (int i = 0; i < blocksToProcess; i++) + { + // Read the next block of data + totalBytesRead += bytesRead = _Inner.Read(readBuf, 0, blockSize); + if (bytesRead > 0) + { + // process the cipher for the read block and add it to the output + int processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, outBuf, 0); + + // copy the values to the output + outBuf.Skip(initialOffset).ToArray().CopyTo(buffer, bytesProcessed + offset); + + // Reset initial offset and calibrate + bytesProcessed += processed - initialOffset; + + // Reset initial offset + initialOffset = 0; + } + + // Clear the read buffer + Array.Clear(readBuf, 0, blockSize); + } // Adjust bytes read by the block offset - bytesRead -= (int)blockOffset; + totalBytesRead -= (int)byteOffset; + bytesProcessed -= (int)byteOffset; - if (processed < bytesRead) + if (bytesProcessed < count) { // Finalize the cipher - processed += AES.FinalizeCipherBlock(_Cipher, buffer, processed + offset); + byte[] finalBuf = _Cipher.TransformFinalBlock(readBuf, bytesProcessed, bytesRead); + finalBuf.Take(count - bytesProcessed).ToArray().CopyTo(buffer, bytesProcessed); + bytesProcessed += count - bytesProcessed; } - return bytesRead; + return bytesProcessed; } return -1; } @@ -58,10 +114,10 @@ namespace Teknik.Utilities { // Process the cipher byte[] output = new byte[count]; - int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0); + //int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0); // Finalize the cipher - AES.FinalizeCipherBlock(_Cipher, output, processed); + //AES.FinalizeCipherBlock(_Cipher, output, processed); _Inner.Write(output, 0, count); } diff --git a/Utilities/Utilities/Utilities.csproj b/Utilities/Utilities/Utilities.csproj index ab32f22..3308f98 100644 --- a/Utilities/Utilities/Utilities.csproj +++ b/Utilities/Utilities/Utilities.csproj @@ -139,6 +139,12 @@ + + + {2bb2f552-b80f-41f2-937d-5d73f15c6dc4} + Security + +