Browse Source

Created new AES CTR wrapper to allow specifying the counter manually

tags/3.0.0
Teknikode 2 years ago
parent
commit
38bcfd9fc1

+ 1
- 1
ServerMaint/Program.cs View File

@@ -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);

+ 10
- 0
Teknik.sln View File

@@ -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

+ 4
- 1
Teknik/Areas/Upload/Controllers/UploadController.cs View File

@@ -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

+ 4
- 0
Teknik/Teknik.csproj View File

@@ -802,6 +802,10 @@
<Project>{c492c2c6-d45a-498b-84a2-6d4c8bf9de77}</Project>
<Name>Piwik</Name>
</ProjectReference>
<ProjectReference Include="..\Utilities\Security\Security.csproj">
<Project>{2bb2f552-b80f-41f2-937d-5d73f15c6dc4}</Project>
<Name>Security</Name>
</ProjectReference>
<ProjectReference Include="..\Utilities\Utilities\Utilities.csproj">
<Project>{f45de6fc-3754-4954-a20a-4277362cc6c1}</Project>
<Name>Utilities</Name>

+ 137
- 0
Utilities/Security/Cryptography/AesCounterMode.cs View File

@@ -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<byte> _xorMask = new Queue<byte>();
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()
{
}
}
}

+ 36
- 0
Utilities/Security/Properties/AssemblyInfo.cs View File

@@ -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")]

+ 55
- 0
Utilities/Security/Security.csproj View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2BB2F552-B80F-41F2-937D-5D73F15C6DC4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Teknik.Security</RootNamespace>
<AssemblyName>Teknik.Security</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Cryptography\AesCounterMode.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 73
- 17
Utilities/Utilities/StreamHelper.cs View File

@@ -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);
}

+ 6
- 0
Utilities/Utilities/Utilities.csproj View File

@@ -139,6 +139,12 @@
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Security\Security.csproj">
<Project>{2bb2f552-b80f-41f2-937d-5d73f15c6dc4}</Project>
<Name>Security</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\packages\GitVersionTask.3.6.5\build\dotnet\GitVersionTask.targets" Condition="Exists('..\..\packages\GitVersionTask.3.6.5\build\dotnet\GitVersionTask.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

Loading…
Cancel
Save