/*
* Mentalis.org Security Library
*
* Copyright © 2002-2005, The KPD-Team
* All rights reserved.
* http://www.mentalis.org/
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the KPD-Team, nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using Org.Mentalis.Security;
namespace Org.Mentalis.Security.Cryptography {
///
/// Defines the basic operations of a unmanaged Rijndael cryptographic transformation.
///
internal class RijndaelUnmanagedTransform : ICryptoTransform {
///
/// Initializes a new instance of the RijndaelUnmanagedTransform class.
///
/// One of the values.
/// One of the values.
/// The key to use.
/// The IV to use.
/// One of the values.
/// The feedback size of the cryptographic operation in bits.
/// One of the values.
public RijndaelUnmanagedTransform(CryptoAlgorithm algorithm, CryptoMethod method, byte[] key, byte[] iv, CipherMode mode, int feedback, PaddingMode padding) {
m_Key = new SymmetricKey(CryptoProvider.RsaAes, algorithm, key);
m_Key.IV = iv;
m_Key.Mode = mode;
if (mode == CipherMode.CFB)
m_Key.FeedbackSize = feedback;
m_Key.Padding = padding;
m_BlockSize = 128;
m_Method = method;
}
///
/// Releases all unmanaged resources.
///
~RijndaelUnmanagedTransform() {
Dispose();
}
///
/// Releases all unmanaged resources.
///
public void Dispose() {
if (m_Key != null) {
m_Key.Dispose();
m_Key = null;
}
try {
GC.SuppressFinalize(this);
} catch {}
}
///
/// Gets a value indicating whether the current transform can be reused.
///
/// true if the current transform can be reused; otherwise, false.
public bool CanReuseTransform {
get {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
return true;
}
}
///
/// Gets a value indicating whether multiple blocks can be transformed.
///
/// true if multiple blocks can be transformed; otherwise, false.
public bool CanTransformMultipleBlocks {
get {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
return true;
}
}
///
/// Gets the input block size.
///
/// The size of the input data blocks in bytes.
public int InputBlockSize {
get {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
return m_BlockSize / 8;
}
}
///
/// Gets the output block size.
///
/// The size of the output data blocks in bytes.
public int OutputBlockSize {
get {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
return m_BlockSize / 8;
}
}
///
/// Transforms the specified region of the input byte array and copies the resulting transform to the specified region of the output byte array.
///
/// The input for which to compute the transform.
/// The offset into the input byte array from which to begin using data.
/// The number of bytes in the input byte array to use as data.
/// The output to which to write the transform.
/// The offset into the output byte array from which to begin writing data.
/// The number of bytes written.
/// The object has been disposed.
/// or is a null reference (Nothing in Visual Basic).
/// One of the specified offsets or lengths is invalid.
/// An error occurs while transforming the specified data.
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
if (inputBuffer == null || outputBuffer == null)
throw new ArgumentNullException();
if (inputCount < 0 || inputOffset < 0 || outputOffset < 0 ||inputOffset + inputCount > inputBuffer.Length)
throw new ArgumentOutOfRangeException();
int length = inputCount;
if (m_Method == CryptoMethod.Encrypt) {
if (SspiProvider.CryptEncrypt(m_Key.Handle, 0, 0, 0, null, ref length, 0) == 0)
throw new CryptographicException("Could not encrypt data.");
if (outputBuffer.Length - outputOffset < length)
throw new ArgumentOutOfRangeException();
Array.Copy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
length = inputCount;
GCHandle h = GCHandle.Alloc(outputBuffer, GCHandleType.Pinned);
try {
if (SspiProvider.CryptEncrypt(m_Key.Handle, 0, 0, 0, new IntPtr(h.AddrOfPinnedObject().ToInt64() + outputOffset), ref length, outputBuffer.Length - outputOffset) == 0)
throw new CryptographicException("Could not encrypt data.");
} finally {
h.Free();
}
} else { // decrypt
byte[] orgCopy = new byte[inputCount];
Array.Copy(inputBuffer, inputOffset, orgCopy, 0, inputCount);
if (SspiProvider.CryptDecrypt(m_Key.Handle, 0, 0, 0, orgCopy, ref length) == 0)
throw new CryptographicException("Could not decrypt data.");
if (length > outputBuffer.Length - outputOffset)
throw new ArgumentOutOfRangeException();
Array.Copy(orgCopy, 0, outputBuffer, outputOffset, length);
}
return length;
}
///
/// Transforms the specified region of the specified byte array.
///
/// The input for which to compute the transform.
/// The offset into the byte array from which to begin using data.
/// The number of bytes in the byte array to use as data.
/// The computed transform.
/// The object has been disposed.
/// is a null reference (Nothing in Visual Basic).
/// The combination of offset and length is invalid.
/// An error occurs while transforming the specified data.
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount ) {
if (m_Key == null)
throw new ObjectDisposedException(this.GetType().FullName);
if (inputBuffer == null)
throw new ArgumentNullException();
if (inputCount < 0 || inputOffset < 0 ||inputOffset + inputCount > inputBuffer.Length)
throw new ArgumentOutOfRangeException();
int length = inputCount;
byte[] ret;
if (m_Method == CryptoMethod.Encrypt) {
if (SspiProvider.CryptEncrypt(m_Key.Handle, 0, 1, 0, null, ref length, 0) == 0)
throw new CryptographicException("Could not encrypt data.");
ret = new byte[length];
Array.Copy(inputBuffer, inputOffset, ret, 0, inputCount);
length = inputCount;
if (SspiProvider.CryptEncrypt(m_Key.Handle, 0, 1, 0, ret, ref length, ret.Length) == 0)
throw new CryptographicException("Could not encrypt data.");
} else { // decrypt
byte[] orgCopy = new byte[inputCount];
Array.Copy(inputBuffer, inputOffset, orgCopy, 0, inputCount);
if (SspiProvider.CryptDecrypt(m_Key.Handle, 0, 1, 0, orgCopy, ref length) == 0)
throw new CryptographicException("Could not decrypt data.");
ret = new byte[length];
Array.Copy(orgCopy, 0, ret, 0, length);
}
return ret;
}
///
/// Holds the block size of the algorithm.
///
private int m_BlockSize;
///
/// Holds the used for the cryptographic transformations.
///
private SymmetricKey m_Key;
///
/// Holds the for this cryptographic operation.
///
private CryptoMethod m_Method;
}
}