#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
using CSharpTest.Net.IO;
using System.Runtime.InteropServices;
namespace CSharpTest.Net.Crypto
{
interface IBlockTransform
{
byte[] EncryptBlock(byte[] blob);
byte[] DecryptBlock(byte[] blob);
}
///
/// Provides the ability to encrypt and decrypt block-transform data
///
public abstract class AsymmetricKey : CryptoKey, IBlockTransform
{
/// Provides the size, in bytes, of the maximum transform unit
protected abstract int BlockSize { get; }
/// Proivdes the output size, in bytes, assuming an input of BlockSize
protected abstract int TransformSize { get; }
/// Encrypts a raw data block as a set of bytes
protected abstract byte[] EncryptBlock(byte[] blob);
/// Decrypts a raw data block as a set of bytes
protected abstract byte[] DecryptBlock(byte[] blob);
/// Encrypts a raw data block as a set of bytes
public sealed override byte[] Encrypt(byte[] blob)
{
try
{
using (MemoryStream ms = new MemoryStream())
{
using (Stream io = Encrypt(new NonClosingStream(ms)))
io.Write(blob, 0, blob.Length);
return ms.ToArray();
}
}
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
/// Decrypts a raw data block as a set of bytes
public sealed override byte[] Decrypt(byte[] blob)
{
try
{
using (MemoryStream ms = new MemoryStream())
{
using (Stream io = Decrypt(new MemoryStream(blob)))
IOStream.CopyStream(io, ms);
return ms.ToArray();
}
}
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
/// Wraps the stream with a cryptographic stream
public sealed override Stream Encrypt(Stream stream)
{
try
{
ICryptoTransform xform = new Encryptor(this, BlockSize, TransformSize);
return new DisposingStream(new CryptoStream(stream, xform, CryptoStreamMode.Write))
.WithDisposeOf(xform);
}
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
/// Wraps the stream with a cryptographic stream
public sealed override Stream Decrypt(Stream stream)
{
try
{
ICryptoTransform xform = new Decryptor(this, TransformSize, BlockSize);
return new DisposingStream(new CryptoStream(stream, xform, CryptoStreamMode.Read))
.WithDisposeOf(xform);
}
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
#region IBlockTransform Members
byte[] IBlockTransform.EncryptBlock(byte[] blob)
{
try { return this.EncryptBlock(blob); }
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
byte[] IBlockTransform.DecryptBlock(byte[] blob)
{
try { return this.DecryptBlock(blob); }
catch (InvalidOperationException) { throw; }
catch { throw CryptographicException(); }
}
#endregion
class Encryptor : ICryptoTransform
{
readonly IBlockTransform _encryptor;
readonly int _blockSize, _outputSize;
public Encryptor(IBlockTransform encryptor, int blockSize, int outputSize)
{
_encryptor = encryptor;
_blockSize = blockSize;
_outputSize = outputSize;
}
public void Dispose() { }
public bool CanReuseTransform { get { return false; } }
public bool CanTransformMultipleBlocks { get { return CanReuseTransform; } }
public int InputBlockSize { get { return _blockSize; } }
public int OutputBlockSize { get { return _outputSize; } }
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
byte[] data = TransformFinalBlock(inputBuffer, inputOffset, inputCount);
Array.Copy(data, 0, outputBuffer, outputOffset, data.Length);
return data.Length;
}
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
byte[] data = inputBuffer;
if (inputOffset != 0 || inputCount != inputBuffer.Length)
{
data = new byte[inputCount];
Array.Copy(inputBuffer, inputOffset, data, 0, inputCount);
}
if (inputCount > 0)
data = Transform(_encryptor, data);
return data;
}
protected virtual byte[] Transform(IBlockTransform encryptor, byte[] data)
{
return encryptor.EncryptBlock(data);
}
}
class Decryptor : Encryptor
{
public Decryptor(IBlockTransform encryptor, int blockSize, int outputSize)
: base(encryptor, blockSize, outputSize)
{ }
protected override byte[] Transform(IBlockTransform encryptor, byte[] data)
{
return encryptor.DecryptBlock(data);
}
}
}
}