//
// Authors:
// Thomas Neidhart (tome@sbox.tugraz.at)
-// Sebastien Pouliot (spouliot@motus.com)
+// Sebastien Pouliot <sebastien@ximian.com>
//
// Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using Mono.Security.Cryptography;
namespace System.Security.Cryptography {
- // This class implement most of the common code required for symmetric
- // algorithm transforms, like:
- // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
- // - PaddingMode, transform properties, multiple blocks, reuse...
- //
- // Descendants MUST:
- // - intialize themselves (like key expansion, ...)
- // - override the ECB (Electronic Code Book) method which will only be
- // called using BlockSize byte[] array.
- internal abstract class SymmetricTransform : ICryptoTransform {
- protected SymmetricAlgorithm algo;
- protected bool encrypt;
- private int BlockSizeByte;
- private byte[] temp;
- private byte[] temp2;
- private byte[] workBuff;
- private byte[] workout;
- private int FeedBackByte;
- private int FeedBackIter;
- private bool m_disposed = false;
-
- public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
- {
- algo = symmAlgo;
- encrypt = encryption;
- BlockSizeByte = (algo.BlockSize >> 3);
- // mode buffers
- temp = new byte [BlockSizeByte];
- Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
- temp2 = new byte [BlockSizeByte];
- FeedBackByte = (algo.FeedbackSize >> 3);
- FeedBackIter = (int) BlockSizeByte / FeedBackByte;
- // transform buffers
- workBuff = new byte [BlockSizeByte];
- workout = new byte [BlockSizeByte];
- }
-
- ~SymmetricTransform ()
- {
- Dispose (false);
- }
-
- void IDisposable.Dispose ()
- {
- Dispose (true);
- GC.SuppressFinalize (this); // Finalization is now unnecessary
- }
-
- // MUST be overriden by classes using unmanaged ressources
- // the override method must call the base class
- protected void Dispose (bool disposing)
- {
- if (!m_disposed) {
- if (disposing) {
- // dispose managed object: zeroize and free
- Array.Clear (temp, 0, BlockSizeByte);
- temp = null;
- Array.Clear (temp2, 0, BlockSizeByte);
- temp2 = null;
- }
- m_disposed = true;
- }
- }
-
- public virtual bool CanTransformMultipleBlocks {
- get { return true; }
- }
-
- public bool CanReuseTransform {
- get { return false; }
- }
-
- public virtual int InputBlockSize {
- get { return BlockSizeByte; }
- }
-
- public virtual int OutputBlockSize {
- get { return BlockSizeByte; }
- }
-
- // note: Each block MUST be BlockSizeValue in size!!!
- // i.e. Any padding must be done before calling this method
- protected void Transform (byte[] input, byte[] output)
- {
- switch (algo.Mode) {
- case CipherMode.ECB:
- ECB (input, output);
- break;
- case CipherMode.CBC:
- CBC (input, output);
- break;
- case CipherMode.CFB:
- CFB (input, output);
- break;
- case CipherMode.OFB:
- OFB (input, output);
- break;
- case CipherMode.CTS:
- CTS (input, output);
- break;
- default:
- throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
- }
- }
-
- // Electronic Code Book (ECB)
- protected abstract void ECB (byte[] input, byte[] output);
-
- // Cipher-Block-Chaining (CBC)
- protected virtual void CBC (byte[] input, byte[] output)
- {
- if (encrypt) {
- for (int i = 0; i < BlockSizeByte; i++)
- temp[i] ^= input[i];
- ECB (temp, output);
- Array.Copy (output, 0, temp, 0, BlockSizeByte);
- }
- else {
- Array.Copy (input, 0, temp2, 0, BlockSizeByte);
- ECB (input, output);
- for (int i = 0; i < BlockSizeByte; i++)
- output[i] ^= temp[i];
- Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
- }
- }
-
- // Cipher-FeedBack (CFB)
- protected virtual void CFB (byte[] input, byte[] output)
- {
- if (encrypt) {
- for (int x = 0; x < FeedBackIter; x++) {
- // temp is first initialized with the IV
- ECB (temp, temp2);
-
- for (int i = 0; i < FeedBackByte; i++)
- output[i + x] = (byte)(temp2[i] ^ input[i + x]);
- Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
- Array.Copy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
- }
- }
- else {
- for (int x = 0; x < FeedBackIter; x++) {
- // we do not really decrypt this data!
- encrypt = true;
- // temp is first initialized with the IV
- ECB (temp, temp2);
- encrypt = false;
-
- Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
- Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
- for (int i = 0; i < FeedBackByte; i++)
- output[i + x] = (byte)(temp2[i] ^ input[i + x]);
- }
- }
- }
-
- // Output-FeedBack (OFB)
- protected virtual void OFB (byte[] input, byte[] output)
- {
- throw new NotImplementedException ("OFB not yet supported");
- }
-
- // Cipher Text Stealing (CTS)
- protected virtual void CTS (byte[] input, byte[] output)
- {
- throw new NotImplementedException ("CTS not yet supported");
- }
-
- // this method may get called MANY times so this is the one to optimize
- public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
- {
- if (m_disposed)
- throw new ObjectDisposedException ("Object is disposed");
-
- if (outputOffset + inputCount > outputBuffer.Length)
- throw new CryptographicException ("Insufficient output buffer size.");
-
- int offs = inputOffset;
- int full;
-
- // this way we don't do a modulo every time we're called
- // and we may save a division
- if (inputCount != BlockSizeByte) {
- if ((inputCount % BlockSizeByte) != 0)
- throw new CryptographicException ("Invalid input block size.");
-
- full = inputCount / BlockSizeByte;
- }
- else
- full = 1;
-
- int total = 0;
- for (int i = 0; i < full; i++) {
- Array.Copy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
- Transform (workBuff, workout);
- Array.Copy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
- offs += BlockSizeByte;
- outputOffset += BlockSizeByte;
- total += BlockSizeByte;
- }
-
- return total;
- }
-
- private byte[] FinalEncrypt (byte[] inputBuffer, int inputOffset, int inputCount)
- {
- // are there still full block to process ?
- int full = (inputCount / BlockSizeByte) * BlockSizeByte;
- int rem = inputCount - full;
- int total = full;
-
- if (algo.Padding != PaddingMode.PKCS7) {
- if (inputCount == 0)
- return new byte [0];
- if (rem != 0) {
- if (algo.Padding == PaddingMode.None)
- throw new CryptographicException ("invalid block length");
- // zero padding the input (by adding a block for the partial data)
- byte[] paddedInput = new byte [full + BlockSizeByte];
- Buffer.BlockCopy (inputBuffer, inputOffset, paddedInput, 0, inputCount);
- inputBuffer = paddedInput;
- inputOffset = 0;
- inputCount = paddedInput.Length;
- total = inputCount;
- }
- }
- else {
- // we need to add an extra block for padding
- total += BlockSizeByte;
- }
-
- byte[] res = new byte [total];
-
- // process all blocks except the last (final) block
- while (total > BlockSizeByte) {
- TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
- inputOffset += BlockSizeByte;
- total -= BlockSizeByte;
- }
-
- // now we only have a single last block to encrypt
- if (algo.Padding == PaddingMode.PKCS7) {
- byte padding = (byte) (BlockSizeByte - rem);
- for (int i = res.Length; --i >= (res.Length - padding);)
- res [i] = padding;
- Array.Copy (inputBuffer, inputOffset, res, full, rem);
- // the last padded block will be transformed in-place
- TransformBlock (res, full, BlockSizeByte, res, full);
- }
- else
- TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
-
- return res;
- }
-
- private byte[] FinalDecrypt (byte [] inputBuffer, int inputOffset, int inputCount)
- {
- if ((inputCount % BlockSizeByte) > 0)
- throw new CryptographicException ("Invalid input block size.");
-
- int total = inputCount;
- byte[] res = new byte [total];
- while (inputCount > 0) {
- TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
- inputOffset += BlockSizeByte;
- inputCount -= BlockSizeByte;
- }
-
- switch (algo.Padding) {
- case PaddingMode.None: // nothing to do - it's a multiple of block size
- case PaddingMode.Zeros: // nothing to do - user must unpad himself
- break;
- case PaddingMode.PKCS7:
- total -= res [total - 1];
- break;
- }
-
- // return output without padding
- if (total > 0) {
- byte[] data = new byte [total];
- Array.Copy (res, 0, data, 0, total);
- // zeroize decrypted data (copy with padding)
- Array.Clear (res, 0, res.Length);
- return data;
- }
- else
- return res;
- }
-
- public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
- {
- if (m_disposed)
- throw new ObjectDisposedException ("Object is disposed");
-
- if (encrypt)
- return FinalEncrypt (inputBuffer, inputOffset, inputCount);
- else
- return FinalDecrypt (inputBuffer, inputOffset, inputCount);
- }
- }
-
- /// <summary>
- /// Abstract base class for all cryptographic symmetric algorithms.
- /// Available algorithms include:
- /// DES, RC2, Rijndael, TripleDES
- /// </summary>
+ [ComVisible (true)]
public abstract class SymmetricAlgorithm : IDisposable {
- protected int BlockSizeValue; // The block size of the cryptographic operation in bits.
- protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits.
- protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm.
- protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits.
- protected byte[] KeyValue; // The secret key for the symmetric algorithm.
- protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm.
- protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm.
- protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm.
- protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm.
+ protected int BlockSizeValue;
+ protected byte[] IVValue;
+ protected int KeySizeValue;
+ protected byte[] KeyValue;
+ protected KeySizes[] LegalBlockSizesValue;
+ protected KeySizes[] LegalKeySizesValue;
+#if MOONLIGHT
+ // Silverlight 2.0 only supports CBC
+ internal int FeedbackSizeValue;
+ internal CipherMode ModeValue;
+ internal PaddingMode PaddingValue;
+#else
+ protected int FeedbackSizeValue;
+ protected CipherMode ModeValue;
+ protected PaddingMode PaddingValue;
+#endif
private bool m_disposed;
- /// <summary>
- /// Called from constructor of derived class.
- /// </summary>
- public SymmetricAlgorithm ()
+ protected SymmetricAlgorithm ()
{
ModeValue = CipherMode.CBC;
PaddingValue = PaddingMode.PKCS7;
- m_disposed = false;
- }
-
- /// <summary>
- /// Called from constructor of derived class.
- /// </summary>
- ~SymmetricAlgorithm ()
- {
- Dispose (false);
}
- public void Clear()
+#if NET_4_0
+ public void Dispose ()
+#else
+ void IDisposable.Dispose ()
+#endif
{
Dispose (true);
+ GC.SuppressFinalize (this); // Finalization is now unnecessary
}
- void IDisposable.Dispose ()
+ public void Clear()
{
Dispose (true);
- GC.SuppressFinalize (this); // Finalization is now unnecessary
}
protected virtual void Dispose (bool disposing)
}
}
- /// <summary>
- /// Gets or sets the actual BlockSize
- /// </summary>
public virtual int BlockSize {
get { return this.BlockSizeValue; }
set {
- if (KeySizes.IsLegalKeySize (this.LegalBlockSizesValue, value))
- this.BlockSizeValue = value;
- else
- throw new CryptographicException("block size not supported by algorithm");
+ if (!KeySizes.IsLegalKeySize (this.LegalBlockSizesValue, value)) {
+ throw new CryptographicException (
+ Locale.GetText ("block size not supported by algorithm"));
+ }
+ // re-setting the same BlockSize *doesn't* regenerate the IV
+ if (BlockSizeValue != value) {
+ BlockSizeValue = value;
+ IVValue = null;
+ }
}
}
- /// <summary>
- /// Gets or sets the actual FeedbackSize
- /// </summary>
public virtual int FeedbackSize {
get { return this.FeedbackSizeValue; }
set {
- if (value > this.BlockSizeValue)
- throw new CryptographicException("feedback size larger than block size");
- else
- this.FeedbackSizeValue = value;
+ if ((value <= 0) || (value > this.BlockSizeValue)) {
+ throw new CryptographicException (
+ Locale.GetText ("feedback size larger than block size"));
+ }
+ this.FeedbackSizeValue = value;
}
}
- /// <summary>
- /// Gets or sets the actual Initial Vector
- /// </summary>
public virtual byte[] IV {
get {
if (this.IVValue == null)
GenerateIV();
- return this.IVValue;
+ return (byte[]) this.IVValue.Clone ();
}
set {
if (value == null)
- throw new ArgumentNullException ("tried setting initial vector to null");
-
- if (value.Length * 8 != this.BlockSizeValue)
- throw new CryptographicException ("IV length must match block size");
-
- this.IVValue = new byte [value.Length];
- Array.Copy (value, 0, this.IVValue, 0, value.Length);
+ throw new ArgumentNullException ("IV");
+ // 2.0 is stricter for IV length - which is bad for IV-less stream ciphers like RC4
+ if ((value.Length << 3) != this.BlockSizeValue) {
+ throw new CryptographicException (
+ Locale.GetText ("IV length is different than block size"));
+ }
+ this.IVValue = (byte[]) value.Clone ();
}
}
- /// <summary>
- /// Gets or sets the actual key
- /// </summary>
public virtual byte[] Key {
get {
if (this.KeyValue == null)
GenerateKey();
- return this.KeyValue;
+ return (byte[]) this.KeyValue.Clone ();
}
set {
if (value == null)
- throw new ArgumentNullException ("tried setting key to null");
-
- if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
- throw new CryptographicException ("key size not supported by algorithm");
+ throw new ArgumentNullException ("Key");
- this.KeySizeValue = value.Length * 8;
- this.KeyValue = new byte [value.Length];
- Array.Copy (value, 0, this.KeyValue, 0, value.Length);
+ int length = (value.Length << 3);
+ if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, length)) {
+ throw new CryptographicException (
+ Locale.GetText ("Key size not supported by algorithm"));
+ }
+ this.KeySizeValue = length;
+ this.KeyValue = (byte[]) value.Clone ();
}
}
- /// <summary>
- /// Gets or sets the actual key size in bits
- /// </summary>
public virtual int KeySize {
get { return this.KeySizeValue; }
set {
- if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, value))
- throw new CryptographicException ("key size not supported by algorithm");
-
- this.KeyValue = null;
- this.KeySizeValue = value;
+ if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, value)) {
+ throw new CryptographicException (
+ Locale.GetText ("Key size not supported by algorithm"));
+ }
+ // re-setting the same KeySize *does* regenerate the key
+ KeySizeValue = value;
+ KeyValue = null;
}
}
- /// <summary>
- /// Gets all legal block sizes
- /// </summary>
public virtual KeySizes[] LegalBlockSizes {
get { return this.LegalBlockSizesValue; }
}
- /// <summary>
- /// Gets all legal key sizes
- /// </summary>
public virtual KeySizes[] LegalKeySizes {
get { return this.LegalKeySizesValue; }
}
- /// <summary>
- /// Gets or sets the actual cipher mode
- /// </summary>
public virtual CipherMode Mode {
get { return this.ModeValue; }
set {
- if (Enum.IsDefined( ModeValue.GetType (), value))
- this.ModeValue = value;
- else
- throw new CryptographicException ("padding mode not available");
+ if (!Enum.IsDefined (ModeValue.GetType (), value)) {
+ throw new CryptographicException (
+ Locale.GetText ("Cipher mode not available"));
+ }
+
+ this.ModeValue = value;
}
}
- /// <summary>
- /// Gets or sets the actual padding
- /// </summary>
public virtual PaddingMode Padding {
get { return this.PaddingValue; }
set {
- if (Enum.IsDefined (PaddingValue.GetType (), value))
- this.PaddingValue = value;
- else
- throw new CryptographicException ("padding mode not available");
+ if (!Enum.IsDefined (PaddingValue.GetType (), value)) {
+ throw new CryptographicException (
+ Locale.GetText ("Padding mode not available"));
+ }
+
+ this.PaddingValue = value;
}
}
- /// <summary>
- /// Gets an Decryptor transform object to work with a CryptoStream
- /// </summary>
public virtual ICryptoTransform CreateDecryptor ()
{
return CreateDecryptor (Key, IV);
}
- /// <summary>
- /// Gets an Decryptor transform object to work with a CryptoStream
- /// </summary>
public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
- /// <summary>
- /// Gets an Encryptor transform object to work with a CryptoStream
- /// </summary>
public virtual ICryptoTransform CreateEncryptor()
{
return CreateEncryptor (Key, IV);
}
- /// <summary>
- /// Gets an Encryptor transform object to work with a CryptoStream
- /// </summary>
public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
- /// <summary>
- /// used to generate an inital vector if none is specified
- /// </summary>
public abstract void GenerateIV ();
- /// </summary>
- /// used to generate a random key if none is specified
- /// </summary>
public abstract void GenerateKey ();
- /// <summary>
- /// Checks wether the given keyLength is valid for the current algorithm
- /// </summary>
- /// <param name="bitLength">the given keyLength</param>
public bool ValidKeySize (int bitLength)
{
return KeySizes.IsLegalKeySize (LegalKeySizesValue, bitLength);
}
- /// <summary>
- /// Creates the default implementation of the default symmetric algorithm (Rijndael).
- /// </summary>
// LAMESPEC: Default is Rijndael - not TripleDES
public static SymmetricAlgorithm Create ()
{
return Create ("System.Security.Cryptography.SymmetricAlgorithm");
}
- /// <summary>
- /// Creates a specific implementation of the given symmetric algorithm.
- /// </summary>
- /// <param name="algName">Specifies which derived class to create</param>
public static SymmetricAlgorithm Create (string algName)
{
return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);