The next generation of the Teknik Services. Written in ASP.NET.
https://www.teknik.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
6.1 KiB
223 lines
6.1 KiB
using System; |
|
using System.IO; |
|
|
|
namespace Teknik.Utilities.Cryptography |
|
{ |
|
public class AesCounterStream : Stream |
|
{ |
|
private Stream _Inner; |
|
private CounterModeCryptoTransform _Cipher; |
|
|
|
/// <summary> |
|
/// Performs Encryption or Decryption on a stream with the given Key and IV |
|
/// |
|
/// Cipher is AES-256 in CTR mode with no padding |
|
/// </summary> |
|
/// <param name="stream"></param> |
|
/// <param name="encrypt"></param> |
|
/// <param name="key"></param> |
|
/// <param name="iv"></param> |
|
public AesCounterStream(Stream stream, bool encrypt, byte[] key, byte[] iv) |
|
{ |
|
_Inner = stream; |
|
|
|
// Create the Aes Cipher |
|
AesCounterMode aes = new AesCounterMode(iv); |
|
if (encrypt) |
|
{ |
|
_Cipher = (CounterModeCryptoTransform)aes.CreateEncryptor(key, iv); // Encrypt |
|
} |
|
else |
|
{ |
|
_Cipher = (CounterModeCryptoTransform)aes.CreateDecryptor(key, iv); // Decrypt |
|
} |
|
|
|
// Sync the counter |
|
SyncCounter(); |
|
} |
|
|
|
public override int Read(byte[] buffer, int offset, int count) |
|
{ |
|
if (_Inner != null && CanRead) |
|
{ |
|
byte[] readBuf = new byte[count]; |
|
int processed = 0; |
|
|
|
// Read the data from the stream |
|
int bytesRead = _Inner.Read(readBuf, 0, count); |
|
if (bytesRead > 0) |
|
{ |
|
// Process the read buffer |
|
processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, buffer, offset); |
|
} |
|
|
|
// Do we have more? |
|
if (processed < bytesRead) |
|
{ |
|
// Finalize the cipher |
|
byte[] finalBuf = _Cipher.TransformFinalBlock(readBuf, processed + offset, bytesRead); |
|
finalBuf.CopyTo(buffer, processed); |
|
processed += finalBuf.Length; |
|
} |
|
|
|
return processed; |
|
} |
|
return -1; |
|
} |
|
|
|
public override void Write(byte[] buffer, int offset, int count) |
|
{ |
|
if (_Inner != null && CanWrite) |
|
{ |
|
// Process the cipher |
|
byte[] output = new byte[count]; |
|
|
|
// Process the buffer |
|
int processed = _Cipher.TransformBlock(buffer, offset, count, output, 0); |
|
|
|
// Do we have more? |
|
if (processed < count) |
|
{ |
|
// Finalize the cipher |
|
byte[] finalBuf = _Cipher.TransformFinalBlock(buffer, processed + offset, count); |
|
finalBuf.CopyTo(output, processed); |
|
} |
|
|
|
_Inner.Write(output, 0, count); |
|
} |
|
} |
|
|
|
public override bool CanRead |
|
{ |
|
get |
|
{ |
|
if (_Inner != null) |
|
{ |
|
return _Inner.CanRead; |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
public override bool CanSeek |
|
{ |
|
get |
|
{ |
|
if (_Inner != null) |
|
{ |
|
return _Inner.CanSeek; |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
public override bool CanWrite |
|
{ |
|
get |
|
{ |
|
if (_Inner != null) |
|
{ |
|
return _Inner.CanWrite; |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
public override long Length |
|
{ |
|
get |
|
{ |
|
if (_Inner != null) |
|
{ |
|
return _Inner.Length; |
|
} |
|
return -1; |
|
} |
|
} |
|
|
|
public override long Position |
|
{ |
|
get |
|
{ |
|
if (_Inner != null) |
|
{ |
|
return _Inner.Position; |
|
} |
|
return -1; |
|
} |
|
|
|
set |
|
{ |
|
if (_Inner != null) |
|
{ |
|
_Inner.Position = value; |
|
|
|
// Sync the counter |
|
SyncCounter(); |
|
} |
|
} |
|
} |
|
|
|
public override void Flush() |
|
{ |
|
if (_Inner != null) |
|
{ |
|
_Inner.Flush(); |
|
} |
|
} |
|
|
|
public override long Seek(long offset, SeekOrigin origin) |
|
{ |
|
if (_Inner != null) |
|
{ |
|
long newPos = _Inner.Seek(offset, origin); |
|
|
|
// Sync the counter |
|
SyncCounter(); |
|
|
|
return newPos; |
|
} |
|
return -1; |
|
} |
|
|
|
public override void SetLength(long value) |
|
{ |
|
if (_Inner != null) |
|
{ |
|
_Inner.SetLength(value); |
|
} |
|
} |
|
|
|
private void SyncCounter() |
|
{ |
|
if (_Cipher != null) |
|
{ |
|
// Calculate the counter iterations and position needed |
|
int iterations = (int)Math.Floor(_Inner.Position / (decimal)_Cipher.InputBlockSize); |
|
int counterPos = (int)(_Inner.Position % _Cipher.InputBlockSize); |
|
|
|
// Are we out of sync with the cipher? |
|
if (_Cipher.Iterations != iterations + 1 || _Cipher.CounterPosition != counterPos) |
|
{ |
|
// Reset the current counter |
|
_Cipher.ResetCounter(); |
|
|
|
// Iterate the counter to the current position |
|
for (int i = 0; i < iterations; i++) |
|
{ |
|
_Cipher.IncrementCounter(); |
|
} |
|
|
|
// Encrypt the counter |
|
_Cipher.EncryptCounter(); |
|
|
|
// Set the current position of the counter |
|
_Cipher.CounterPosition = counterPos; |
|
|
|
// Increment the counter for the next time |
|
_Cipher.IncrementCounter(); |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|